passagemath-repl 10.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_repl-10.5.1.data/scripts/sage-cachegrind +25 -0
- passagemath_repl-10.5.1.data/scripts/sage-callgrind +16 -0
- passagemath_repl-10.5.1.data/scripts/sage-cleaner +230 -0
- passagemath_repl-10.5.1.data/scripts/sage-coverage +327 -0
- passagemath_repl-10.5.1.data/scripts/sage-eval +14 -0
- passagemath_repl-10.5.1.data/scripts/sage-fixdoctests +710 -0
- passagemath_repl-10.5.1.data/scripts/sage-inline-fortran +12 -0
- passagemath_repl-10.5.1.data/scripts/sage-ipynb2rst +50 -0
- passagemath_repl-10.5.1.data/scripts/sage-ipython +16 -0
- passagemath_repl-10.5.1.data/scripts/sage-massif +25 -0
- passagemath_repl-10.5.1.data/scripts/sage-notebook +267 -0
- passagemath_repl-10.5.1.data/scripts/sage-omega +25 -0
- passagemath_repl-10.5.1.data/scripts/sage-preparse +302 -0
- passagemath_repl-10.5.1.data/scripts/sage-run +27 -0
- passagemath_repl-10.5.1.data/scripts/sage-run-cython +10 -0
- passagemath_repl-10.5.1.data/scripts/sage-runtests +9 -0
- passagemath_repl-10.5.1.data/scripts/sage-startuptime.py +163 -0
- passagemath_repl-10.5.1.data/scripts/sage-valgrind +34 -0
- passagemath_repl-10.5.1.dist-info/METADATA +77 -0
- passagemath_repl-10.5.1.dist-info/RECORD +162 -0
- passagemath_repl-10.5.1.dist-info/WHEEL +5 -0
- passagemath_repl-10.5.1.dist-info/top_level.txt +1 -0
- sage/all__sagemath_repl.py +119 -0
- sage/doctest/__init__.py +4 -0
- sage/doctest/__main__.py +236 -0
- sage/doctest/all.py +4 -0
- sage/doctest/check_tolerance.py +261 -0
- sage/doctest/control.py +1727 -0
- sage/doctest/external.py +534 -0
- sage/doctest/fixtures.py +383 -0
- sage/doctest/forker.py +2665 -0
- sage/doctest/marked_output.py +102 -0
- sage/doctest/parsing.py +1708 -0
- sage/doctest/parsing_test.py +79 -0
- sage/doctest/reporting.py +733 -0
- sage/doctest/rif_tol.py +124 -0
- sage/doctest/sources.py +1657 -0
- sage/doctest/test.py +584 -0
- sage/doctest/tests/1second.rst +4 -0
- sage/doctest/tests/99seconds.rst +4 -0
- sage/doctest/tests/abort.rst +5 -0
- sage/doctest/tests/atexit.rst +7 -0
- sage/doctest/tests/fail_and_die.rst +6 -0
- sage/doctest/tests/initial.rst +15 -0
- sage/doctest/tests/interrupt.rst +7 -0
- sage/doctest/tests/interrupt_diehard.rst +14 -0
- sage/doctest/tests/keyboardinterrupt.rst +11 -0
- sage/doctest/tests/longtime.rst +5 -0
- sage/doctest/tests/nodoctest +5 -0
- sage/doctest/tests/random_seed.rst +4 -0
- sage/doctest/tests/show_skipped.rst +18 -0
- sage/doctest/tests/sig_on.rst +9 -0
- sage/doctest/tests/simple_failure.rst +8 -0
- sage/doctest/tests/sleep_and_raise.rst +106 -0
- sage/doctest/tests/tolerance.rst +31 -0
- sage/doctest/util.py +750 -0
- sage/interfaces/cleaner.py +48 -0
- sage/interfaces/quit.py +163 -0
- sage/misc/all__sagemath_repl.py +51 -0
- sage/misc/banner.py +235 -0
- sage/misc/benchmark.py +221 -0
- sage/misc/classgraph.py +134 -0
- sage/misc/copying.py +22 -0
- sage/misc/cython.py +694 -0
- sage/misc/dev_tools.py +745 -0
- sage/misc/edit_module.py +304 -0
- sage/misc/explain_pickle.py +3079 -0
- sage/misc/gperftools.py +361 -0
- sage/misc/inline_fortran.py +212 -0
- sage/misc/messaging.py +86 -0
- sage/misc/pager.py +21 -0
- sage/misc/profiler.py +179 -0
- sage/misc/python.py +70 -0
- sage/misc/remote_file.py +53 -0
- sage/misc/sage_eval.py +249 -0
- sage/misc/sage_input.py +3621 -0
- sage/misc/sagedoc.py +1742 -0
- sage/misc/sh.py +38 -0
- sage/misc/trace.py +90 -0
- sage/repl/__init__.py +16 -0
- sage/repl/all.py +15 -0
- sage/repl/attach.py +625 -0
- sage/repl/configuration.py +186 -0
- sage/repl/display/__init__.py +1 -0
- sage/repl/display/fancy_repr.py +354 -0
- sage/repl/display/formatter.py +318 -0
- sage/repl/display/jsmol_iframe.py +290 -0
- sage/repl/display/pretty_print.py +153 -0
- sage/repl/display/util.py +163 -0
- sage/repl/image.py +302 -0
- sage/repl/inputhook.py +91 -0
- sage/repl/interface_magic.py +298 -0
- sage/repl/interpreter.py +854 -0
- sage/repl/ipython_extension.py +593 -0
- sage/repl/ipython_kernel/__init__.py +1 -0
- sage/repl/ipython_kernel/__main__.py +4 -0
- sage/repl/ipython_kernel/all_jupyter.py +10 -0
- sage/repl/ipython_kernel/install.py +301 -0
- sage/repl/ipython_kernel/interact.py +278 -0
- sage/repl/ipython_kernel/kernel.py +217 -0
- sage/repl/ipython_kernel/widgets.py +466 -0
- sage/repl/ipython_kernel/widgets_sagenb.py +587 -0
- sage/repl/ipython_tests.py +163 -0
- sage/repl/load.py +326 -0
- sage/repl/preparse.py +2218 -0
- sage/repl/prompts.py +90 -0
- sage/repl/rich_output/__init__.py +4 -0
- sage/repl/rich_output/backend_base.py +648 -0
- sage/repl/rich_output/backend_doctest.py +316 -0
- sage/repl/rich_output/backend_emacs.py +151 -0
- sage/repl/rich_output/backend_ipython.py +596 -0
- sage/repl/rich_output/buffer.py +311 -0
- sage/repl/rich_output/display_manager.py +829 -0
- sage/repl/rich_output/example.avi +0 -0
- sage/repl/rich_output/example.canvas3d +1 -0
- sage/repl/rich_output/example.dvi +0 -0
- sage/repl/rich_output/example.flv +0 -0
- sage/repl/rich_output/example.gif +0 -0
- sage/repl/rich_output/example.jpg +0 -0
- sage/repl/rich_output/example.mkv +0 -0
- sage/repl/rich_output/example.mov +0 -0
- sage/repl/rich_output/example.mp4 +0 -0
- sage/repl/rich_output/example.ogv +0 -0
- sage/repl/rich_output/example.pdf +0 -0
- sage/repl/rich_output/example.png +0 -0
- sage/repl/rich_output/example.svg +54 -0
- sage/repl/rich_output/example.webm +0 -0
- sage/repl/rich_output/example.wmv +0 -0
- sage/repl/rich_output/example_jmol.spt.zip +0 -0
- sage/repl/rich_output/example_wavefront_scene.mtl +7 -0
- sage/repl/rich_output/example_wavefront_scene.obj +17 -0
- sage/repl/rich_output/output_basic.py +391 -0
- sage/repl/rich_output/output_browser.py +103 -0
- sage/repl/rich_output/output_catalog.py +54 -0
- sage/repl/rich_output/output_graphics.py +320 -0
- sage/repl/rich_output/output_graphics3d.py +345 -0
- sage/repl/rich_output/output_video.py +231 -0
- sage/repl/rich_output/preferences.py +432 -0
- sage/repl/rich_output/pretty_print.py +339 -0
- sage/repl/rich_output/test_backend.py +201 -0
- sage/repl/user_globals.py +214 -0
- sage/tests/all.py +0 -0
- sage/tests/all__sagemath_repl.py +3 -0
- sage/tests/article_heuberger_krenn_kropf_fsm-in-sage.py +630 -0
- sage/tests/arxiv_0812_2725.py +351 -0
- sage/tests/benchmark.py +1925 -0
- sage/tests/book_schilling_zabrocki_kschur_primer.py +795 -0
- sage/tests/book_stein_ent.py +651 -0
- sage/tests/book_stein_modform.py +558 -0
- sage/tests/cmdline.py +796 -0
- sage/tests/combinatorial_hopf_algebras.py +52 -0
- sage/tests/finite_poset.py +623 -0
- sage/tests/functools_partial_src.py +27 -0
- sage/tests/gosper-sum.py +218 -0
- sage/tests/lazy_imports.py +28 -0
- sage/tests/modular_group_cohomology.py +80 -0
- sage/tests/numpy.py +21 -0
- sage/tests/parigp.py +76 -0
- sage/tests/startup.py +27 -0
- sage/tests/symbolic-series.py +76 -0
- sage/tests/sympy.py +16 -0
- sage/tests/test_deprecation.py +31 -0
@@ -0,0 +1,3079 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-repl
|
2
|
+
"""
|
3
|
+
A tool for inspecting Python pickles
|
4
|
+
|
5
|
+
AUTHORS:
|
6
|
+
|
7
|
+
- Carl Witty (2009-03)
|
8
|
+
|
9
|
+
The :func:`explain_pickle` function takes a pickle and produces Sage code that
|
10
|
+
will evaluate to the contents of the pickle. Ideally, the combination
|
11
|
+
of :func:`explain_pickle` to produce Sage code and :func:`sage_eval` to evaluate the code
|
12
|
+
would be a 100% compatible implementation of cPickle's unpickler; this
|
13
|
+
is almost the case now.
|
14
|
+
|
15
|
+
EXAMPLES::
|
16
|
+
|
17
|
+
sage: explain_pickle(dumps(12345))
|
18
|
+
pg_make_integer = unpickle_global('sage.rings.integer', 'make_integer')
|
19
|
+
pg_make_integer('c1p')
|
20
|
+
sage: explain_pickle(dumps(polygen(QQ)))
|
21
|
+
pg_Polynomial_rational_flint = unpickle_global('sage.rings.polynomial.polynomial_rational_flint', 'Polynomial_rational_flint')
|
22
|
+
pg_unpickle_PolynomialRing = unpickle_global('sage.rings.polynomial.polynomial_ring_constructor', 'unpickle_PolynomialRing')
|
23
|
+
pg_RationalField = unpickle_global('sage.rings.rational_field', 'RationalField')
|
24
|
+
pg = unpickle_instantiate(pg_RationalField, ())
|
25
|
+
pg_make_rational = unpickle_global('sage.rings.rational', 'make_rational')
|
26
|
+
pg_Polynomial_rational_flint(pg_unpickle_PolynomialRing(pg, ('x',), None, False), [pg_make_rational('0'), pg_make_rational('1')], False, True)
|
27
|
+
sage: sage_eval(explain_pickle(dumps(polygen(QQ)))) == polygen(QQ)
|
28
|
+
True
|
29
|
+
|
30
|
+
By default (as above) the code produced contains calls to several
|
31
|
+
utility functions (:func:`unpickle_global`, etc.); this is done so that the
|
32
|
+
code is truly equivalent to the pickle. If the pickle can be loaded
|
33
|
+
into a future version of Sage, then the code that :func:`explain_pickle`
|
34
|
+
produces today should work in that future Sage as well.
|
35
|
+
|
36
|
+
It is also possible to produce simpler code, that is tied to the current
|
37
|
+
version of Sage; here are the above two examples again::
|
38
|
+
|
39
|
+
sage: explain_pickle(dumps(12345), in_current_sage=True)
|
40
|
+
from sage.rings.integer import make_integer
|
41
|
+
make_integer('c1p')
|
42
|
+
sage: explain_pickle(dumps(polygen(QQ)), in_current_sage=True)
|
43
|
+
from sage.rings.polynomial.polynomial_rational_flint import Polynomial_rational_flint
|
44
|
+
from sage.rings.polynomial.polynomial_ring_constructor import unpickle_PolynomialRing
|
45
|
+
from sage.rings.rational import make_rational
|
46
|
+
Polynomial_rational_flint(unpickle_PolynomialRing(RationalField(), ('x',), None, False), [make_rational('0'), make_rational('1')], False, True)
|
47
|
+
|
48
|
+
The :func:`explain_pickle` function has several use cases.
|
49
|
+
|
50
|
+
- Write pickling support for your classes
|
51
|
+
|
52
|
+
You can use :func:`explain_pickle` to see what will happen when a pickle
|
53
|
+
is unpickled. Consider: is this sequence of commands something
|
54
|
+
that can be easily supported in all future Sage versions, or does
|
55
|
+
it expose internal design decisions that are subject to change?
|
56
|
+
|
57
|
+
- Debug old pickles
|
58
|
+
|
59
|
+
If you have a pickle from an old version of Sage that no longer
|
60
|
+
unpickles, you can use :func:`explain_pickle` to see what it is trying to
|
61
|
+
do, to figure out how to fix it.
|
62
|
+
|
63
|
+
- Use :func:`explain_pickle` in doctests to help maintenance
|
64
|
+
|
65
|
+
If you have a ``loads(dumps(S))`` doctest, you could also add an
|
66
|
+
``explain_pickle(dumps(S))`` doctest. Then if something changes
|
67
|
+
in a way that would invalidate old pickles, the output of
|
68
|
+
:func:`explain_pickle` will also change. At that point, you can add
|
69
|
+
the previous output of :obj:`explain_pickle` as a new set of
|
70
|
+
doctests (and then update the :obj:`explain_pickle` doctest to use
|
71
|
+
the new output), to ensure that old pickles will continue to work.
|
72
|
+
|
73
|
+
As mentioned above, there are several output modes for :func:`explain_pickle`,
|
74
|
+
that control fidelity versus simplicity of the output. For example,
|
75
|
+
the GLOBAL instruction takes a module name and a class name and
|
76
|
+
produces the corresponding class. So GLOBAL of ``sage.rings.integer``,
|
77
|
+
``Integer`` is approximately equivalent to ``sage.rings.integer.Integer``.
|
78
|
+
|
79
|
+
However, this class lookup process can be customized (using
|
80
|
+
:func:`sage.misc.persist.register_unpickle_override`). For instance,
|
81
|
+
if some future version of Sage renamed ``sage/rings/integer.pyx`` to
|
82
|
+
``sage/rings/knuth_was_here.pyx``, old pickles would no longer work unless
|
83
|
+
register_unpickle_override was used; in that case, GLOBAL of
|
84
|
+
``sage.rings.integer``, ``integer`` would mean
|
85
|
+
``sage.rings.knuth_was_here.integer``.
|
86
|
+
|
87
|
+
By default, :func:`explain_pickle` will map this GLOBAL instruction to
|
88
|
+
``unpickle_global('sage.rings.integer', 'integer')``. Then when this code
|
89
|
+
is evaluated, :func:`unpickle_global` will look up the current mapping in the
|
90
|
+
:func:`register_unpickle_override` table, so the generated code will continue
|
91
|
+
to work even in hypothetical future versions of Sage where ``integer.pyx``
|
92
|
+
has been renamed.
|
93
|
+
|
94
|
+
If you pass the flag ``in_current_sage=True``, then
|
95
|
+
:func:`explain_pickle` will generate code that may only work in the
|
96
|
+
current version of Sage, not in future versions. In this case, it
|
97
|
+
would generate::
|
98
|
+
|
99
|
+
from sage.rings.integer import integer
|
100
|
+
|
101
|
+
and if you ran :func:`explain_pickle` in hypothetical future sage, it would generate:
|
102
|
+
|
103
|
+
from sage.rings.knuth_was_here import integer
|
104
|
+
|
105
|
+
but the current code wouldn't work in the future sage.
|
106
|
+
|
107
|
+
If you pass the flag ``default_assumptions=True``, then
|
108
|
+
:func:`explain_pickle` will generate code that would work in the
|
109
|
+
absence of any special unpickling information. That is, in either
|
110
|
+
current Sage or hypothetical future Sage, it would generate::
|
111
|
+
|
112
|
+
from sage.rings.integer import integer
|
113
|
+
|
114
|
+
The intention is that ``default_assumptions`` output is prettier (more
|
115
|
+
human-readable), but may not actually work; so it is only intended for
|
116
|
+
human reading.
|
117
|
+
|
118
|
+
There are several functions used in the output of :func:`explain_pickle`.
|
119
|
+
Here I give a brief description of what they usually do, as well as
|
120
|
+
how to modify their operation (for instance, if you're trying to get
|
121
|
+
old pickles to work).
|
122
|
+
|
123
|
+
- ``unpickle_global(module, classname)``:
|
124
|
+
``unpickle_global('sage.foo.bar', 'baz')`` is usually equivalent to
|
125
|
+
``sage.foo.bar.baz``, but this can be customized with
|
126
|
+
:func:`register_unpickle_override`.
|
127
|
+
|
128
|
+
- ``unpickle_newobj(klass, args)``:
|
129
|
+
Usually equivalent to ``klass.__new__(klass, *args)``. If
|
130
|
+
``klass`` is a Python class, then you can define :meth:`__new__`
|
131
|
+
to control the result (this result actually need not be an
|
132
|
+
instance of ``klass``). (This doesn't work for Cython classes.)
|
133
|
+
|
134
|
+
- ``unpickle_build(obj, state)``:
|
135
|
+
If ``obj`` has a :meth:`__setstate__` method, then this is equivalent to
|
136
|
+
``obj.__setstate__(state)``. Otherwise uses ``state`` to set the attributes
|
137
|
+
of ``obj``. Customize by defining :meth:`__setstate__`.
|
138
|
+
|
139
|
+
- ``unpickle_instantiate(klass, args)``:
|
140
|
+
Usually equivalent to ``klass(*args)``. Cannot be customized.
|
141
|
+
|
142
|
+
- ``unpickle_appends(lst, vals)``:
|
143
|
+
Appends the values in ``vals`` to ``lst``. If not ``isinstance(lst, list)``,
|
144
|
+
can be customized by defining a :meth:`append` method.
|
145
|
+
"""
|
146
|
+
|
147
|
+
# *****************************************************************************
|
148
|
+
# Copyright (C) 2009 Carl Witty <Carl.Witty@gmail.com>
|
149
|
+
#
|
150
|
+
# This program is free software: you can redistribute it and/or modify
|
151
|
+
# it under the terms of the GNU General Public License as published by
|
152
|
+
# the Free Software Foundation, either version 2 of the License, or
|
153
|
+
# (at your option) any later version.
|
154
|
+
# http://www.gnu.org/licenses/
|
155
|
+
# *****************************************************************************
|
156
|
+
|
157
|
+
|
158
|
+
import pickletools
|
159
|
+
import re
|
160
|
+
import sys
|
161
|
+
import types
|
162
|
+
|
163
|
+
import zlib as comp
|
164
|
+
import bz2 as comp_other
|
165
|
+
|
166
|
+
from pickletools import genops
|
167
|
+
|
168
|
+
from sage.misc.sage_input import SageInputBuilder, SageInputExpression
|
169
|
+
from sage.misc.sage_eval import sage_eval
|
170
|
+
from sage.misc.persist import (unpickle_override, unpickle_global, dumps,
|
171
|
+
register_unpickle_override, SageUnpickler)
|
172
|
+
|
173
|
+
|
174
|
+
# Python 3 does not have a "ClassType". Instead, we ensure that
|
175
|
+
# isinstance(foo, ClassType) will always return False.
|
176
|
+
ClassType = ()
|
177
|
+
|
178
|
+
|
179
|
+
def explain_pickle(pickle=None, file=None, compress=True, **kwargs):
|
180
|
+
r"""
|
181
|
+
Explain a pickle. That is, produce source code such that evaluating
|
182
|
+
the code is equivalent to loading the pickle. Feeding the result
|
183
|
+
of :func:`explain_pickle` to :func:`sage_eval` should be totally equivalent to loading
|
184
|
+
the ``pickle`` with ``cPickle``.
|
185
|
+
|
186
|
+
INPUT:
|
187
|
+
|
188
|
+
- ``pickle`` -- string (default: ``None``); the pickle to explain
|
189
|
+
- ``file`` -- a filename of a pickle (default: ``None``)
|
190
|
+
- ``compress`` -- boolean (default: ``True``); if ``False``, don't attempt
|
191
|
+
to decompress the pickle
|
192
|
+
- ``in_current_sage`` -- boolean (default: ``False``); if ``True``,
|
193
|
+
produce potentially simpler code that is tied to the current version of
|
194
|
+
Sage
|
195
|
+
- ``default_assumptions`` -- boolean (default: ``False``); if ``True``,
|
196
|
+
produce potentially simpler code that assumes that generic unpickling
|
197
|
+
code will be used. This code may not actually work.
|
198
|
+
- ``eval`` -- boolean (default: ``False``); if ``True``, then evaluate the
|
199
|
+
resulting code and return the evaluated result
|
200
|
+
- ``preparse`` -- if ``True``, then produce code to be evaluated with
|
201
|
+
Sage's preparser; if ``False``, then produce standard
|
202
|
+
Python code; if ``None``, then produce code that will work
|
203
|
+
either with or without the preparser. (default: ``True``)
|
204
|
+
- ``pedantic`` -- boolean (default: ``False``); if ``True``, then carefully
|
205
|
+
ensures that the result has at least as much sharing as the result of
|
206
|
+
cPickle (it may have more, for immutable objects)
|
207
|
+
|
208
|
+
Exactly one of ``pickle`` (a string containing a pickle) or
|
209
|
+
``file`` (the filename of a pickle) must be provided.
|
210
|
+
|
211
|
+
EXAMPLES::
|
212
|
+
|
213
|
+
sage: explain_pickle(dumps({('a', 'b'): [1r, 2r]}))
|
214
|
+
{('a', 'b'):[1r, 2r]}
|
215
|
+
sage: explain_pickle(dumps(RR(pi)), in_current_sage=True) # needs sage.symbolic
|
216
|
+
from sage.rings.real_mpfr import __create__RealNumber_version0
|
217
|
+
from sage.rings.real_mpfr import __create__RealField_version0
|
218
|
+
__create__RealNumber_version0(__create__RealField_version0(53r, False, 'RNDN'), '3.4gvml245kc0@0', 32r)
|
219
|
+
sage: s = 'hi'
|
220
|
+
sage: explain_pickle(dumps((s, s)))
|
221
|
+
('hi', 'hi')
|
222
|
+
sage: explain_pickle(dumps((s, s)), pedantic=True)
|
223
|
+
si = 'hi'
|
224
|
+
(si, si)
|
225
|
+
sage: explain_pickle(dumps(5r))
|
226
|
+
5r
|
227
|
+
sage: explain_pickle(dumps(5r), preparse=False)
|
228
|
+
5
|
229
|
+
sage: explain_pickle(dumps(5r), preparse=None)
|
230
|
+
int(5)
|
231
|
+
sage: explain_pickle(dumps(22/7))
|
232
|
+
pg_make_rational = unpickle_global('sage.rings.rational', 'make_rational')
|
233
|
+
pg_make_rational('m/7')
|
234
|
+
sage: explain_pickle(dumps(22/7), in_current_sage=True)
|
235
|
+
from sage.rings.rational import make_rational
|
236
|
+
make_rational('m/7')
|
237
|
+
sage: explain_pickle(dumps(22/7), default_assumptions=True)
|
238
|
+
from sage.rings.rational import make_rational
|
239
|
+
make_rational('m/7')
|
240
|
+
"""
|
241
|
+
if pickle is not None:
|
242
|
+
p = pickle
|
243
|
+
elif file is not None:
|
244
|
+
with open(file) as f:
|
245
|
+
p = f.read()
|
246
|
+
else:
|
247
|
+
raise ValueError("Either pickle or file must be specified")
|
248
|
+
|
249
|
+
if compress:
|
250
|
+
try:
|
251
|
+
p = comp.decompress(p)
|
252
|
+
except Exception:
|
253
|
+
try:
|
254
|
+
p = comp_other.decompress(p)
|
255
|
+
except Exception:
|
256
|
+
# Maybe data is uncompressed?
|
257
|
+
pass
|
258
|
+
|
259
|
+
return explain_pickle_string(p, **kwargs)
|
260
|
+
|
261
|
+
|
262
|
+
def explain_pickle_string(pickle, in_current_sage=False,
|
263
|
+
default_assumptions=False, eval=False, preparse=True,
|
264
|
+
pedantic=False):
|
265
|
+
r"""
|
266
|
+
This is a helper function for :func:`explain_pickle`. It takes a decompressed
|
267
|
+
pickle string as input; other than that, its options are all the same
|
268
|
+
as :func:`explain_pickle`.
|
269
|
+
|
270
|
+
EXAMPLES::
|
271
|
+
|
272
|
+
sage: sage.misc.explain_pickle.explain_pickle_string(dumps("Hello, world", compress=False))
|
273
|
+
'Hello, world'
|
274
|
+
|
275
|
+
(See the documentation for :func:`explain_pickle` for many more examples.)
|
276
|
+
"""
|
277
|
+
sib = SageInputBuilder(preparse=preparse)
|
278
|
+
|
279
|
+
pe = PickleExplainer(sib, in_current_sage=in_current_sage,
|
280
|
+
default_assumptions=default_assumptions,
|
281
|
+
pedantic=pedantic)
|
282
|
+
|
283
|
+
v = pe.run_pickle(pickle)
|
284
|
+
|
285
|
+
ans = sib.result(sib(v))
|
286
|
+
|
287
|
+
if eval:
|
288
|
+
if default_assumptions:
|
289
|
+
raise ValueError("Not safe to evaluate code generated with default_assumptions")
|
290
|
+
from sage.misc.sage_eval import sage_eval
|
291
|
+
result = sage_eval(ans, preparse=preparse)
|
292
|
+
print(ans)
|
293
|
+
return result
|
294
|
+
else:
|
295
|
+
return ans
|
296
|
+
|
297
|
+
|
298
|
+
valid_name_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
|
299
|
+
|
300
|
+
|
301
|
+
def name_is_valid(name):
|
302
|
+
r"""
|
303
|
+
Test whether a string is a valid Python identifier. (We use a
|
304
|
+
conservative test, that only allows ASCII identifiers.)
|
305
|
+
|
306
|
+
EXAMPLES::
|
307
|
+
|
308
|
+
sage: from sage.misc.explain_pickle import name_is_valid
|
309
|
+
sage: name_is_valid('fred')
|
310
|
+
True
|
311
|
+
sage: name_is_valid('Yes!ValidName')
|
312
|
+
False
|
313
|
+
sage: name_is_valid('_happy_1234')
|
314
|
+
True
|
315
|
+
"""
|
316
|
+
# Technically, we also need to reject keywords...
|
317
|
+
return bool(valid_name_re.match(name))
|
318
|
+
|
319
|
+
|
320
|
+
# The pickle interpreter can push and pop "marks" on the stack.
|
321
|
+
# This string is used as the representation of a mark.
|
322
|
+
the_mark = 'mark'
|
323
|
+
|
324
|
+
|
325
|
+
class PickleObject:
|
326
|
+
r"""
|
327
|
+
Pickles have a stack-based virtual machine. The :func:`explain_pickle`
|
328
|
+
pickle interpreter mostly uses :class:`sage.misc.sage_input.SageInputExpression` objects
|
329
|
+
as the stack values. However, sometimes we want some more information
|
330
|
+
about the value on the stack, so that we can generate better
|
331
|
+
(prettier, less confusing) code. In such cases, we push
|
332
|
+
a :class:`PickleObject` instead of a :class:`~sage.misc.sage_input.SageInputExpression`.
|
333
|
+
A :class:`PickleObject`
|
334
|
+
contains a value (which may be a standard Python value, or a
|
335
|
+
:class:`PickleDict` or :class:`PickleInstance`), an expression
|
336
|
+
(a :class:`~sage.misc.sage_input.SageInputExpression`),
|
337
|
+
and an "immutable" flag (which checks whether this object
|
338
|
+
has been converted to a :class:`SageInputExpression`; if it has, then we
|
339
|
+
must not mutate the object, since the :class:`SageInputExpression` would not
|
340
|
+
reflect the changes).
|
341
|
+
"""
|
342
|
+
|
343
|
+
def __init__(self, value, expression):
|
344
|
+
r"""
|
345
|
+
Construct a PickleObject.
|
346
|
+
|
347
|
+
TESTS::
|
348
|
+
|
349
|
+
sage: from sage.misc.explain_pickle import *
|
350
|
+
sage: v = PickleObject(1, 2)
|
351
|
+
sage: v.value
|
352
|
+
1
|
353
|
+
sage: v.expression
|
354
|
+
2
|
355
|
+
sage: v.immutable
|
356
|
+
False
|
357
|
+
"""
|
358
|
+
self.value = value
|
359
|
+
self.expression = expression
|
360
|
+
self.immutable = False
|
361
|
+
|
362
|
+
def _sage_input_(self, sib, coerced):
|
363
|
+
r"""
|
364
|
+
Extracts the expression from a PickleObject, and sets the immutable
|
365
|
+
flag.
|
366
|
+
|
367
|
+
TESTS::
|
368
|
+
|
369
|
+
sage: from sage.misc.explain_pickle import *
|
370
|
+
sage: v = PickleObject(1, 2)
|
371
|
+
sage: v.immutable
|
372
|
+
False
|
373
|
+
sage: v._sage_input_('sib', False)
|
374
|
+
2
|
375
|
+
sage: v.immutable
|
376
|
+
True
|
377
|
+
"""
|
378
|
+
self.immutable = True
|
379
|
+
return self.expression
|
380
|
+
|
381
|
+
|
382
|
+
class PickleDict:
|
383
|
+
r"""
|
384
|
+
An object which can be used as the value of a :class:`PickleObject`. The items
|
385
|
+
is a list of key-value pairs, where the keys and values are
|
386
|
+
:class:`SageInputExpression` objects. We use this to help construct dictionary literals,
|
387
|
+
instead of always starting with an empty dictionary and assigning to
|
388
|
+
it.
|
389
|
+
"""
|
390
|
+
def __init__(self, items):
|
391
|
+
r"""
|
392
|
+
Initialize a PickleDict.
|
393
|
+
|
394
|
+
EXAMPLES::
|
395
|
+
|
396
|
+
sage: from sage.misc.explain_pickle import *
|
397
|
+
sage: PickleDict([('a', 1)]).items
|
398
|
+
[('a', 1)]
|
399
|
+
"""
|
400
|
+
self.items = items
|
401
|
+
|
402
|
+
|
403
|
+
class PickleInstance:
|
404
|
+
r"""
|
405
|
+
An object which can be used as the value of a :class:`PickleObject`. Unlike
|
406
|
+
other possible values of a :class:`PickleObject`, a :class:`PickleInstance` doesn't represent
|
407
|
+
an exact value; instead, it gives the class (type) of the object.
|
408
|
+
"""
|
409
|
+
def __init__(self, klass):
|
410
|
+
r"""
|
411
|
+
Initialize a PickleInstance.
|
412
|
+
|
413
|
+
EXAMPLES::
|
414
|
+
|
415
|
+
sage: from sage.misc.explain_pickle import *
|
416
|
+
sage: PickleInstance(Integer).klass
|
417
|
+
<class 'sage.rings.integer.Integer'>
|
418
|
+
"""
|
419
|
+
self.klass = klass
|
420
|
+
|
421
|
+
|
422
|
+
class PickleExplainer:
|
423
|
+
r"""
|
424
|
+
An interpreter for the pickle virtual machine, that executes
|
425
|
+
symbolically and constructs :class:`SageInputExpression` objects instead of
|
426
|
+
directly constructing values.
|
427
|
+
"""
|
428
|
+
def __init__(self, sib, in_current_sage=False, default_assumptions=False,
|
429
|
+
pedantic=False):
|
430
|
+
r"""
|
431
|
+
Initialize a PickleExplainer interpreter for the pickle virtual machine.
|
432
|
+
|
433
|
+
EXAMPLES::
|
434
|
+
|
435
|
+
sage: from sage.misc.explain_pickle import *
|
436
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
437
|
+
sage: pe = PickleExplainer(SageInputBuilder(), in_current_sage=True, default_assumptions=False, pedantic=True)
|
438
|
+
sage: pe.in_current_sage
|
439
|
+
True
|
440
|
+
sage: pe.pedantic
|
441
|
+
True
|
442
|
+
"""
|
443
|
+
self.sib = sib
|
444
|
+
self.in_current_sage = in_current_sage
|
445
|
+
self.default_assumptions = default_assumptions
|
446
|
+
self.pedantic = pedantic
|
447
|
+
self.stopped = False
|
448
|
+
self.stack = []
|
449
|
+
self.memo = {}
|
450
|
+
if in_current_sage and default_assumptions:
|
451
|
+
raise ValueError("in_current_sage and default_assumptions must not both be true")
|
452
|
+
|
453
|
+
self.new_instance = self.sib.import_name('types', 'InstanceType')
|
454
|
+
|
455
|
+
def run_pickle(self, p):
|
456
|
+
r"""
|
457
|
+
Given an (uncompressed) pickle as a string, run the pickle
|
458
|
+
in this virtual machine. Once a STOP has been executed, return
|
459
|
+
the result (a :class:`SageInputExpression` representing code which, when
|
460
|
+
evaluated, will give the value of the pickle).
|
461
|
+
|
462
|
+
EXAMPLES::
|
463
|
+
|
464
|
+
sage: from sage.misc.explain_pickle import *
|
465
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
466
|
+
sage: sib = SageInputBuilder()
|
467
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True, default_assumptions=False, pedantic=True)
|
468
|
+
sage: sib(pe.run_pickle('T\5\0\0\0hello.')) # py2
|
469
|
+
{atomic:'hello'}
|
470
|
+
"""
|
471
|
+
for op, arg, pos in genops(p):
|
472
|
+
assert not self.stopped
|
473
|
+
try:
|
474
|
+
handler = getattr(self, op.name)
|
475
|
+
except AttributeError:
|
476
|
+
raise NotImplementedError('PickleExplainer does not yet handle opcode %s' % op.name)
|
477
|
+
if arg is None:
|
478
|
+
handler()
|
479
|
+
else:
|
480
|
+
handler(arg)
|
481
|
+
|
482
|
+
assert self.stopped
|
483
|
+
assert len(self.stack) == 1
|
484
|
+
return self.stack[0]
|
485
|
+
|
486
|
+
def check_value(self, v):
|
487
|
+
r"""
|
488
|
+
Check that the given value is either a :class:`SageInputExpression` or a
|
489
|
+
:class:`PickleObject`. Used for internal sanity checking.
|
490
|
+
|
491
|
+
EXAMPLES::
|
492
|
+
|
493
|
+
sage: from sage.misc.explain_pickle import *
|
494
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
495
|
+
sage: sib = SageInputBuilder()
|
496
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True, default_assumptions=False, pedantic=True)
|
497
|
+
sage: pe.check_value(7)
|
498
|
+
Traceback (most recent call last):
|
499
|
+
...
|
500
|
+
AssertionError
|
501
|
+
sage: pe.check_value(sib(7))
|
502
|
+
"""
|
503
|
+
assert isinstance(v, (SageInputExpression, PickleObject))
|
504
|
+
|
505
|
+
def push(self, v):
|
506
|
+
r"""
|
507
|
+
Push a value onto the virtual machine's stack.
|
508
|
+
|
509
|
+
EXAMPLES::
|
510
|
+
|
511
|
+
sage: from sage.misc.explain_pickle import *
|
512
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
513
|
+
sage: sib = SageInputBuilder()
|
514
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True, default_assumptions=False, pedantic=True)
|
515
|
+
sage: pe.push(sib(7))
|
516
|
+
sage: pe.stack[-1]
|
517
|
+
{atomic:7}
|
518
|
+
"""
|
519
|
+
self.check_value(v)
|
520
|
+
self.stack.append(v)
|
521
|
+
|
522
|
+
def push_and_share(self, v):
|
523
|
+
r"""
|
524
|
+
Push a value onto the virtual machine's stack; also mark it as shared
|
525
|
+
for :func:`sage_input` if we are in pedantic mode.
|
526
|
+
|
527
|
+
EXAMPLES::
|
528
|
+
|
529
|
+
sage: from sage.misc.explain_pickle import *
|
530
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
531
|
+
sage: sib = SageInputBuilder()
|
532
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True, default_assumptions=False, pedantic=True)
|
533
|
+
sage: pe.push_and_share(sib(7))
|
534
|
+
sage: pe.stack[-1]
|
535
|
+
{atomic:7}
|
536
|
+
sage: pe.stack[-1]._sie_share
|
537
|
+
True
|
538
|
+
"""
|
539
|
+
self.share(v)
|
540
|
+
self.push(v)
|
541
|
+
|
542
|
+
def pop(self):
|
543
|
+
r"""
|
544
|
+
Pop a value from the virtual machine's stack, and return it.
|
545
|
+
|
546
|
+
EXAMPLES::
|
547
|
+
|
548
|
+
sage: from sage.misc.explain_pickle import *
|
549
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
550
|
+
sage: sib = SageInputBuilder()
|
551
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True, default_assumptions=False, pedantic=True)
|
552
|
+
sage: pe.push(sib(7))
|
553
|
+
sage: pe.pop()
|
554
|
+
{atomic:7}
|
555
|
+
"""
|
556
|
+
v = self.stack.pop()
|
557
|
+
self.check_value(v)
|
558
|
+
return v
|
559
|
+
|
560
|
+
def push_mark(self):
|
561
|
+
r"""
|
562
|
+
Push a 'mark' onto the virtual machine's stack.
|
563
|
+
|
564
|
+
EXAMPLES::
|
565
|
+
|
566
|
+
sage: from sage.misc.explain_pickle import *
|
567
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
568
|
+
sage: sib = SageInputBuilder()
|
569
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True, default_assumptions=False, pedantic=True)
|
570
|
+
sage: pe.push_mark()
|
571
|
+
sage: pe.stack[-1]
|
572
|
+
'mark'
|
573
|
+
sage: pe.stack[-1] is the_mark
|
574
|
+
True
|
575
|
+
"""
|
576
|
+
self.stack.append(the_mark)
|
577
|
+
|
578
|
+
def pop_to_mark(self):
|
579
|
+
r"""
|
580
|
+
Pop all values down to the 'mark' from the virtual machine's stack,
|
581
|
+
and return the values as a list.
|
582
|
+
|
583
|
+
EXAMPLES::
|
584
|
+
|
585
|
+
sage: from sage.misc.explain_pickle import *
|
586
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
587
|
+
sage: sib = SageInputBuilder()
|
588
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True, default_assumptions=False, pedantic=True)
|
589
|
+
sage: pe.push_mark()
|
590
|
+
sage: pe.push(sib(7))
|
591
|
+
sage: pe.push(sib('hello'))
|
592
|
+
sage: pe.pop_to_mark()
|
593
|
+
[{atomic:7}, {atomic:'hello'}]
|
594
|
+
"""
|
595
|
+
slice = []
|
596
|
+
while True:
|
597
|
+
v = self.stack.pop()
|
598
|
+
if v is the_mark:
|
599
|
+
slice.reverse()
|
600
|
+
return slice
|
601
|
+
self.check_value(v)
|
602
|
+
slice.append(v)
|
603
|
+
|
604
|
+
def share(self, v):
|
605
|
+
r"""
|
606
|
+
Mark a :func:`sage_input` value as shared, if we are in pedantic mode.
|
607
|
+
|
608
|
+
EXAMPLES::
|
609
|
+
|
610
|
+
sage: from sage.misc.explain_pickle import *
|
611
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
612
|
+
sage: sib = SageInputBuilder()
|
613
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True,
|
614
|
+
....: default_assumptions=False, pedantic=True)
|
615
|
+
sage: v = sib(7)
|
616
|
+
sage: v._sie_share
|
617
|
+
False
|
618
|
+
sage: pe.share(v)
|
619
|
+
{atomic:7}
|
620
|
+
sage: v._sie_share
|
621
|
+
True
|
622
|
+
"""
|
623
|
+
if self.pedantic:
|
624
|
+
self.sib.share(v)
|
625
|
+
return v
|
626
|
+
|
627
|
+
def is_mutable_pickle_object(self, v):
|
628
|
+
r"""
|
629
|
+
Test whether a :class:`PickleObject` is mutable (has never been converted
|
630
|
+
to a :class:`SageInputExpression`).
|
631
|
+
|
632
|
+
EXAMPLES::
|
633
|
+
|
634
|
+
sage: from sage.misc.explain_pickle import *
|
635
|
+
sage: from sage.misc.sage_input import SageInputBuilder
|
636
|
+
sage: sib = SageInputBuilder()
|
637
|
+
sage: pe = PickleExplainer(sib, in_current_sage=True,
|
638
|
+
....: default_assumptions=False, pedantic=True)
|
639
|
+
sage: v = PickleObject(1, sib(1))
|
640
|
+
sage: pe.is_mutable_pickle_object(v)
|
641
|
+
True
|
642
|
+
sage: sib(v)
|
643
|
+
{atomic:1}
|
644
|
+
sage: pe.is_mutable_pickle_object(v)
|
645
|
+
False
|
646
|
+
"""
|
647
|
+
return isinstance(v, PickleObject) and not v.immutable
|
648
|
+
|
649
|
+
# Opcodes are in alphabetical order
|
650
|
+
|
651
|
+
def APPEND(self):
|
652
|
+
r"""
|
653
|
+
TESTS::
|
654
|
+
|
655
|
+
sage: from sage.misc.explain_pickle import *
|
656
|
+
sage: test_pickle(['a'])
|
657
|
+
0: \x80 PROTO 2
|
658
|
+
2: ] EMPTY_LIST
|
659
|
+
3: q BINPUT 0
|
660
|
+
5: X BINUNICODE 'a'
|
661
|
+
11: q BINPUT 1
|
662
|
+
13: a APPEND
|
663
|
+
14: . STOP
|
664
|
+
highest protocol among opcodes = 2
|
665
|
+
explain_pickle in_current_sage=True/False:
|
666
|
+
['a']
|
667
|
+
result: ['a']
|
668
|
+
|
669
|
+
As shown above, we prefer to create a list literal. This is not
|
670
|
+
possible if the list is recursive::
|
671
|
+
|
672
|
+
sage: v = []
|
673
|
+
sage: v.append(v)
|
674
|
+
sage: test_pickle(v)
|
675
|
+
0: \x80 PROTO 2
|
676
|
+
2: ] EMPTY_LIST
|
677
|
+
3: q BINPUT 0
|
678
|
+
5: h BINGET 0
|
679
|
+
7: a APPEND
|
680
|
+
8: . STOP
|
681
|
+
highest protocol among opcodes = 2
|
682
|
+
explain_pickle in_current_sage=True/False:
|
683
|
+
si = []
|
684
|
+
list.append(si, si)
|
685
|
+
si
|
686
|
+
result: [[...]]
|
687
|
+
"""
|
688
|
+
|
689
|
+
obj = self.pop()
|
690
|
+
lst = self.pop()
|
691
|
+
self._APPENDS_helper(lst, [obj])
|
692
|
+
|
693
|
+
def APPENDS(self):
|
694
|
+
r"""
|
695
|
+
TESTS::
|
696
|
+
|
697
|
+
sage: from sage.misc.explain_pickle import *
|
698
|
+
sage: test_pickle(['a', 'b'])
|
699
|
+
0: \x80 PROTO 2
|
700
|
+
2: ] EMPTY_LIST
|
701
|
+
3: q BINPUT 0
|
702
|
+
5: ( MARK
|
703
|
+
6: X BINUNICODE 'a'
|
704
|
+
12: q BINPUT 1
|
705
|
+
14: X BINUNICODE 'b'
|
706
|
+
20: q BINPUT 2
|
707
|
+
22: e APPENDS (MARK at 5)
|
708
|
+
23: . STOP
|
709
|
+
highest protocol among opcodes = 2
|
710
|
+
explain_pickle in_current_sage=True/False:
|
711
|
+
['a', 'b']
|
712
|
+
result: ['a', 'b']
|
713
|
+
|
714
|
+
As shown above, we prefer to create a list literal. This is not
|
715
|
+
possible if the list is recursive::
|
716
|
+
|
717
|
+
sage: v = []
|
718
|
+
sage: v.append(v)
|
719
|
+
sage: v.append(v)
|
720
|
+
sage: test_pickle(v)
|
721
|
+
0: \x80 PROTO 2
|
722
|
+
2: ] EMPTY_LIST
|
723
|
+
3: q BINPUT 0
|
724
|
+
5: ( MARK
|
725
|
+
6: h BINGET 0
|
726
|
+
8: h BINGET 0
|
727
|
+
10: e APPENDS (MARK at 5)
|
728
|
+
11: . STOP
|
729
|
+
highest protocol among opcodes = 2
|
730
|
+
explain_pickle in_current_sage=True/False:
|
731
|
+
si = []
|
732
|
+
list.extend(si, [si, si])
|
733
|
+
si
|
734
|
+
result: [[...], [...]]
|
735
|
+
"""
|
736
|
+
slice = self.pop_to_mark()
|
737
|
+
lst = self.pop()
|
738
|
+
self._APPENDS_helper(lst, slice)
|
739
|
+
|
740
|
+
def _APPENDS_helper(self, lst, slice):
|
741
|
+
r"""
|
742
|
+
TESTS:
|
743
|
+
|
744
|
+
See the doctests for APPEND and APPENDS for some simple indirect
|
745
|
+
tests of this method. Here we test some subtle behavior.
|
746
|
+
|
747
|
+
For subtypes of list, we use list.append/list.extend instead of
|
748
|
+
the append method of the object (TestAppendList.append raises
|
749
|
+
an exception, so we can tell that cPickle doesn't call it either)::
|
750
|
+
|
751
|
+
sage: from sage.misc.explain_pickle import *
|
752
|
+
sage: test_pickle(TestAppendList((True,))) # indirect doctest
|
753
|
+
0: \x80 PROTO 2
|
754
|
+
2: c GLOBAL 'sage.misc.explain_pickle TestAppendList'
|
755
|
+
43: q BINPUT 0
|
756
|
+
45: ) EMPTY_TUPLE
|
757
|
+
46: \x81 NEWOBJ
|
758
|
+
47: q BINPUT 1
|
759
|
+
49: \x88 NEWTRUE
|
760
|
+
50: a APPEND
|
761
|
+
51: . STOP
|
762
|
+
highest protocol among opcodes = 2
|
763
|
+
explain_pickle in_current_sage=True:
|
764
|
+
from sage.misc.explain_pickle import TestAppendList
|
765
|
+
si = unpickle_newobj(TestAppendList, ())
|
766
|
+
list.append(si, True)
|
767
|
+
si
|
768
|
+
explain_pickle in_current_sage=False:
|
769
|
+
pg_TestAppendList = unpickle_global('sage.misc.explain_pickle', 'TestAppendList')
|
770
|
+
si = unpickle_newobj(pg_TestAppendList, ())
|
771
|
+
unpickle_appends(si, [True])
|
772
|
+
si
|
773
|
+
result: [True] (cPickle raised an exception!)
|
774
|
+
|
775
|
+
For values which are not subtypes of list, we use their own append
|
776
|
+
method::
|
777
|
+
|
778
|
+
sage: v = TestAppendNonlist()
|
779
|
+
sage: v.list = [False, None]
|
780
|
+
sage: test_pickle(v, verbose_eval=True)
|
781
|
+
0: \x80 PROTO 2
|
782
|
+
2: c GLOBAL 'sage.misc.explain_pickle TestAppendNonlist'
|
783
|
+
46: q BINPUT 0
|
784
|
+
48: ) EMPTY_TUPLE
|
785
|
+
49: R REDUCE
|
786
|
+
50: q BINPUT 1
|
787
|
+
52: ( MARK
|
788
|
+
53: \x89 NEWFALSE
|
789
|
+
54: N NONE
|
790
|
+
55: e APPENDS (MARK at 52)
|
791
|
+
56: . STOP
|
792
|
+
highest protocol among opcodes = 2
|
793
|
+
explain_pickle in_current_sage=True:
|
794
|
+
from sage.misc.explain_pickle import TestAppendNonlist
|
795
|
+
si = TestAppendNonlist()
|
796
|
+
si.append(False)
|
797
|
+
si.append(None)
|
798
|
+
si
|
799
|
+
explain_pickle in_current_sage=False:
|
800
|
+
pg_TestAppendNonlist = unpickle_global('sage.misc.explain_pickle', 'TestAppendNonlist')
|
801
|
+
pg = unpickle_instantiate(pg_TestAppendNonlist, ())
|
802
|
+
unpickle_appends(pg, [False, None])
|
803
|
+
pg
|
804
|
+
evaluating explain_pickle in_current_sage=True:
|
805
|
+
Fetching append attribute
|
806
|
+
Fetching append attribute
|
807
|
+
evaluating explain_pickle in_current_sage=False:
|
808
|
+
Fetching append attribute
|
809
|
+
loading pickle with cPickle:
|
810
|
+
Fetching append attribute
|
811
|
+
result: [False, None]
|
812
|
+
|
813
|
+
We see above that the in_current_sage=True code doesn't quite match
|
814
|
+
the other cases, because it fetches the append attribute twice
|
815
|
+
instead of once. If we set pedantic=True, then this is fixed.
|
816
|
+
(We show only the changed parts of the output)::
|
817
|
+
|
818
|
+
sage: test_pickle(v, verbose_eval=True, pedantic=True)
|
819
|
+
0: \x80 PROTO 2
|
820
|
+
...
|
821
|
+
explain_pickle in_current_sage=True:
|
822
|
+
from sage.misc.explain_pickle import TestAppendNonlist
|
823
|
+
si1 = TestAppendNonlist()
|
824
|
+
si2 = si1.append
|
825
|
+
si2(False)
|
826
|
+
si2(None)
|
827
|
+
si1
|
828
|
+
...
|
829
|
+
evaluating explain_pickle in_current_sage=True:
|
830
|
+
Fetching append attribute
|
831
|
+
...
|
832
|
+
"""
|
833
|
+
# This has the side-effect of marking lst as immutable, if
|
834
|
+
# slice happens to include lst.
|
835
|
+
slice_exp = self.sib(slice)
|
836
|
+
if self.is_mutable_pickle_object(lst) and isinstance(lst.value, list):
|
837
|
+
lst.value.extend(slice)
|
838
|
+
lst.expression = self.sib(lst.value)
|
839
|
+
elif isinstance(lst, PickleObject) or self.default_assumptions:
|
840
|
+
if isinstance(lst.value, list) or \
|
841
|
+
(isinstance(lst.value, PickleInstance) and
|
842
|
+
issubclass(lst.value.klass, list)) or \
|
843
|
+
self.default_assumptions:
|
844
|
+
if len(slice) > 1:
|
845
|
+
self.sib.command(lst, self.sib.name('list').extend(lst, slice))
|
846
|
+
else:
|
847
|
+
for s in slice:
|
848
|
+
self.sib.command(lst, self.sib.name('list').append(lst, self.sib(s)))
|
849
|
+
else:
|
850
|
+
if self.pedantic:
|
851
|
+
app = self.sib(lst).append
|
852
|
+
for s in slice:
|
853
|
+
self.sib.command(lst, app(self.sib(s)))
|
854
|
+
else:
|
855
|
+
for s in slice:
|
856
|
+
self.sib.command(lst, self.sib(lst).append(self.sib(s)))
|
857
|
+
else:
|
858
|
+
self.sib.command(lst, self.sib.name('unpickle_appends')(self.sib(lst), slice_exp))
|
859
|
+
self.push(lst)
|
860
|
+
|
861
|
+
def BINFLOAT(self, f):
|
862
|
+
r"""
|
863
|
+
TESTS::
|
864
|
+
|
865
|
+
sage: from sage.misc.explain_pickle import *
|
866
|
+
sage: test_pickle(float(pi)) # needs sage.symbolic
|
867
|
+
0: \x80 PROTO 2
|
868
|
+
2: G BINFLOAT 3.141592653589793
|
869
|
+
11: . STOP
|
870
|
+
highest protocol among opcodes = 2
|
871
|
+
explain_pickle in_current_sage=True/False:
|
872
|
+
3.141592653589793
|
873
|
+
result: 3.141592653589793
|
874
|
+
"""
|
875
|
+
self.push(self.sib(f))
|
876
|
+
|
877
|
+
def BINGET(self, n):
|
878
|
+
r"""
|
879
|
+
TESTS::
|
880
|
+
|
881
|
+
sage: from pickle import *
|
882
|
+
sage: from sage.misc.explain_pickle import *
|
883
|
+
sage: test_pickle(EMPTY_LIST + BINPUT + 'x' + POP + BINGET + 'x' + '.') # py2
|
884
|
+
0: ] EMPTY_LIST
|
885
|
+
1: q BINPUT 120
|
886
|
+
3: 0 POP
|
887
|
+
4: h BINGET 120
|
888
|
+
6: . STOP
|
889
|
+
highest protocol among opcodes = 1
|
890
|
+
explain_pickle in_current_sage=True/False:
|
891
|
+
[]
|
892
|
+
result: []
|
893
|
+
"""
|
894
|
+
self.push(self.memo[n])
|
895
|
+
|
896
|
+
def BININT(self, n):
|
897
|
+
r"""
|
898
|
+
TESTS::
|
899
|
+
|
900
|
+
sage: from sage.misc.explain_pickle import *
|
901
|
+
sage: test_pickle(dumps(100000r, compress=False)) # py2
|
902
|
+
0: \x80 PROTO 2
|
903
|
+
2: J BININT 100000
|
904
|
+
7: . STOP
|
905
|
+
highest protocol among opcodes = 2
|
906
|
+
explain_pickle in_current_sage=True/False:
|
907
|
+
100000
|
908
|
+
result: 100000
|
909
|
+
"""
|
910
|
+
self.push_and_share(self.sib(n))
|
911
|
+
|
912
|
+
def BININT1(self, n):
|
913
|
+
r"""
|
914
|
+
TESTS::
|
915
|
+
|
916
|
+
sage: from sage.misc.explain_pickle import *
|
917
|
+
sage: test_pickle(dumps(100r, compress=False)) # py2
|
918
|
+
0: \x80 PROTO 2
|
919
|
+
2: K BININT1 100
|
920
|
+
4: . STOP
|
921
|
+
highest protocol among opcodes = 2
|
922
|
+
explain_pickle in_current_sage=True/False:
|
923
|
+
100
|
924
|
+
result: 100
|
925
|
+
"""
|
926
|
+
self.push_and_share(self.sib(n))
|
927
|
+
|
928
|
+
def BININT2(self, n):
|
929
|
+
r"""
|
930
|
+
TESTS::
|
931
|
+
|
932
|
+
sage: from sage.misc.explain_pickle import *
|
933
|
+
sage: test_pickle(dumps(1000r, compress=False)) # py2
|
934
|
+
0: \x80 PROTO 2
|
935
|
+
2: M BININT2 1000
|
936
|
+
5: . STOP
|
937
|
+
highest protocol among opcodes = 2
|
938
|
+
explain_pickle in_current_sage=True/False:
|
939
|
+
1000
|
940
|
+
result: 1000
|
941
|
+
"""
|
942
|
+
self.push_and_share(self.sib(n))
|
943
|
+
|
944
|
+
def BINPUT(self, n):
|
945
|
+
r"""
|
946
|
+
TESTS::
|
947
|
+
|
948
|
+
sage: from pickle import *
|
949
|
+
sage: from sage.misc.explain_pickle import *
|
950
|
+
sage: test_pickle(EMPTY_LIST + BINPUT + 'x' + POP + BINGET + 'x') # py2
|
951
|
+
0: ] EMPTY_LIST
|
952
|
+
1: q BINPUT 120
|
953
|
+
3: 0 POP
|
954
|
+
4: h BINGET 120
|
955
|
+
6: . STOP
|
956
|
+
highest protocol among opcodes = 1
|
957
|
+
explain_pickle in_current_sage=True/False:
|
958
|
+
[]
|
959
|
+
result: []
|
960
|
+
"""
|
961
|
+
v = self.pop()
|
962
|
+
self.memo[n] = v
|
963
|
+
self.push(v)
|
964
|
+
|
965
|
+
def BINSTRING(self, s):
|
966
|
+
r"""
|
967
|
+
TESTS::
|
968
|
+
|
969
|
+
sage: from sage.misc.explain_pickle import *
|
970
|
+
sage: test_pickle('T\5\0\0\0hello.') # py2
|
971
|
+
0: T BINSTRING 'hello'
|
972
|
+
10: . STOP
|
973
|
+
highest protocol among opcodes = 1
|
974
|
+
explain_pickle in_current_sage=True/False:
|
975
|
+
'hello'
|
976
|
+
result: 'hello'
|
977
|
+
"""
|
978
|
+
self.push(PickleObject(s, self.share(self.sib(s))))
|
979
|
+
|
980
|
+
def BINUNICODE(self, s):
|
981
|
+
r"""
|
982
|
+
TESTS::
|
983
|
+
|
984
|
+
sage: from sage.misc.explain_pickle import *
|
985
|
+
sage: test_pickle(u'hi\u1234\U00012345') # py2
|
986
|
+
0: \x80 PROTO 2
|
987
|
+
2: X BINUNICODE u'hi\u1234\U00012345'
|
988
|
+
16: q BINPUT 1
|
989
|
+
18: . STOP
|
990
|
+
highest protocol among opcodes = 2
|
991
|
+
explain_pickle in_current_sage=True/False:
|
992
|
+
u'hi\u1234\U00012345'
|
993
|
+
result: u'hi\u1234\U00012345'
|
994
|
+
"""
|
995
|
+
self.push_and_share(self.sib(s))
|
996
|
+
|
997
|
+
def BUILD(self):
|
998
|
+
r"""
|
999
|
+
TESTS::
|
1000
|
+
|
1001
|
+
sage: from sage.misc.explain_pickle import *
|
1002
|
+
sage: test_pickle(TestBuild())
|
1003
|
+
0: \x80 PROTO 2
|
1004
|
+
2: c GLOBAL 'sage.misc.explain_pickle TestBuild'
|
1005
|
+
38: q BINPUT 0
|
1006
|
+
40: ) EMPTY_TUPLE
|
1007
|
+
41: \x81 NEWOBJ
|
1008
|
+
42: q BINPUT 1
|
1009
|
+
44: } EMPTY_DICT
|
1010
|
+
45: q BINPUT 2
|
1011
|
+
47: X BINUNICODE 'x'
|
1012
|
+
53: q BINPUT 3
|
1013
|
+
55: K BININT1 3
|
1014
|
+
57: s SETITEM
|
1015
|
+
58: } EMPTY_DICT
|
1016
|
+
59: q BINPUT 4
|
1017
|
+
61: X BINUNICODE 'y'
|
1018
|
+
67: q BINPUT 5
|
1019
|
+
69: K BININT1 4
|
1020
|
+
71: s SETITEM
|
1021
|
+
72: \x86 TUPLE2
|
1022
|
+
73: q BINPUT 6
|
1023
|
+
75: b BUILD
|
1024
|
+
76: . STOP
|
1025
|
+
highest protocol among opcodes = 2
|
1026
|
+
explain_pickle in_current_sage=True:
|
1027
|
+
from sage.misc.explain_pickle import TestBuild
|
1028
|
+
si = unpickle_newobj(TestBuild, ())
|
1029
|
+
si.__dict__['x'] = 3
|
1030
|
+
setattr(si, 'y', 4)
|
1031
|
+
si
|
1032
|
+
explain_pickle in_current_sage=False:
|
1033
|
+
pg_TestBuild = unpickle_global('sage.misc.explain_pickle', 'TestBuild')
|
1034
|
+
si = unpickle_newobj(pg_TestBuild, ())
|
1035
|
+
unpickle_build(si, ({'x':3}, {'y':4}))
|
1036
|
+
si
|
1037
|
+
result: TestBuild: x=3; y=4
|
1038
|
+
|
1039
|
+
::
|
1040
|
+
|
1041
|
+
sage: test_pickle(TestBuildSetstate(), verbose_eval=True)
|
1042
|
+
0: \x80 PROTO 2
|
1043
|
+
2: c GLOBAL 'sage.misc.explain_pickle TestBuildSetstate'
|
1044
|
+
46: q BINPUT 0
|
1045
|
+
48: ) EMPTY_TUPLE
|
1046
|
+
49: \x81 NEWOBJ
|
1047
|
+
50: q BINPUT 1
|
1048
|
+
52: } EMPTY_DICT
|
1049
|
+
53: q BINPUT 2
|
1050
|
+
55: X BINUNICODE 'x'
|
1051
|
+
61: q BINPUT 3
|
1052
|
+
63: K BININT1 3
|
1053
|
+
65: s SETITEM
|
1054
|
+
66: } EMPTY_DICT
|
1055
|
+
67: q BINPUT 4
|
1056
|
+
69: X BINUNICODE 'y'
|
1057
|
+
75: q BINPUT 5
|
1058
|
+
77: K BININT1 4
|
1059
|
+
79: s SETITEM
|
1060
|
+
80: \x86 TUPLE2
|
1061
|
+
81: q BINPUT 6
|
1062
|
+
83: b BUILD
|
1063
|
+
84: . STOP
|
1064
|
+
highest protocol among opcodes = 2
|
1065
|
+
explain_pickle in_current_sage=True:
|
1066
|
+
from sage.misc.explain_pickle import TestBuildSetstate
|
1067
|
+
si = unpickle_newobj(TestBuildSetstate, ())
|
1068
|
+
si.__setstate__(({'x':3}, {'y':4}))
|
1069
|
+
si
|
1070
|
+
explain_pickle in_current_sage=False:
|
1071
|
+
pg_TestBuildSetstate = unpickle_global('sage.misc.explain_pickle', 'TestBuildSetstate')
|
1072
|
+
si = unpickle_newobj(pg_TestBuildSetstate, ())
|
1073
|
+
unpickle_build(si, ({'x':3}, {'y':4}))
|
1074
|
+
si
|
1075
|
+
evaluating explain_pickle in_current_sage=True:
|
1076
|
+
setting state from ({'x': 3}, {'y': 4})
|
1077
|
+
evaluating explain_pickle in_current_sage=False:
|
1078
|
+
setting state from ({'x': 3}, {'y': 4})
|
1079
|
+
loading pickle with cPickle:
|
1080
|
+
setting state from ({'x': 3}, {'y': 4})
|
1081
|
+
result: TestBuild: x=4; y=3
|
1082
|
+
"""
|
1083
|
+
args = self.pop()
|
1084
|
+
obj = self.pop()
|
1085
|
+
use_setstate = False
|
1086
|
+
direct_set = False
|
1087
|
+
if self.default_assumptions:
|
1088
|
+
direct_set = True
|
1089
|
+
elif self.in_current_sage:
|
1090
|
+
if isinstance(obj, PickleObject) and isinstance(obj.value, PickleInstance):
|
1091
|
+
if hasattr(obj.value.klass, '__setstate__'):
|
1092
|
+
use_setstate = True
|
1093
|
+
else:
|
1094
|
+
direct_set = True
|
1095
|
+
|
1096
|
+
can_handle_direct_set = False
|
1097
|
+
if direct_set:
|
1098
|
+
if isinstance(args, PickleObject):
|
1099
|
+
if isinstance(args.value, PickleDict):
|
1100
|
+
can_handle_direct_set = True
|
1101
|
+
if isinstance(args.value, tuple) and isinstance(args.value[0], PickleObject) and isinstance(args.value[0].value, PickleDict) and isinstance(args.value[1], PickleObject) and isinstance(args.value[1].value, PickleDict):
|
1102
|
+
can_handle_direct_set = True
|
1103
|
+
if not can_handle_direct_set:
|
1104
|
+
direct_set = False
|
1105
|
+
|
1106
|
+
if use_setstate:
|
1107
|
+
self.sib.command(obj, self.sib.getattr(obj, '__setstate__')(args))
|
1108
|
+
elif direct_set:
|
1109
|
+
state = args.value
|
1110
|
+
slots = None
|
1111
|
+
if isinstance(state, tuple):
|
1112
|
+
slots = state[1].value
|
1113
|
+
state = state[0].value
|
1114
|
+
d = self.sib.getattr(obj, '__dict__')
|
1115
|
+
for k, v in state.items:
|
1116
|
+
self.sib.command(obj, self.sib.assign(d[k], v))
|
1117
|
+
if slots is not None:
|
1118
|
+
for k, v in slots.items:
|
1119
|
+
if isinstance(k, PickleObject) and isinstance(k.value, str):
|
1120
|
+
self.sib.command(obj, self.sib.assign(self.sib.getattr(obj, k.value), v))
|
1121
|
+
else:
|
1122
|
+
self.sib.command(obj, self.sib.name('setattr')(obj, k, v))
|
1123
|
+
else:
|
1124
|
+
self.sib.command(obj, self.sib.name('unpickle_build')(obj, args))
|
1125
|
+
|
1126
|
+
self.push(obj)
|
1127
|
+
|
1128
|
+
def DICT(self):
|
1129
|
+
r"""
|
1130
|
+
TESTS::
|
1131
|
+
|
1132
|
+
sage: from pickle import *
|
1133
|
+
sage: from sage.misc.explain_pickle import *
|
1134
|
+
sage: test_pickle(DICT, args=('mark', 'a', 1, 2, 'b')) # py2
|
1135
|
+
0: ( MARK
|
1136
|
+
1: P PERSID '1'
|
1137
|
+
4: P PERSID '2'
|
1138
|
+
7: P PERSID '3'
|
1139
|
+
10: P PERSID '4'
|
1140
|
+
13: d DICT (MARK at 0)
|
1141
|
+
14: . STOP
|
1142
|
+
highest protocol among opcodes = 0
|
1143
|
+
explain_pickle in_current_sage=True/False:
|
1144
|
+
{unpickle_persistent('1'):unpickle_persistent('2'), unpickle_persistent('3'):unpickle_persistent('4')}
|
1145
|
+
result: {'a': 1, 2: 'b'}
|
1146
|
+
"""
|
1147
|
+
slice = self.pop_to_mark()
|
1148
|
+
self.EMPTY_DICT()
|
1149
|
+
self._SETITEMS_helper(slice)
|
1150
|
+
|
1151
|
+
def DUP(self):
|
1152
|
+
r"""
|
1153
|
+
TESTS::
|
1154
|
+
|
1155
|
+
sage: from pickle import *
|
1156
|
+
sage: from sage.misc.explain_pickle import *
|
1157
|
+
sage: test_pickle(EMPTY_LIST + DUP + TUPLE2 + STOP) # py2
|
1158
|
+
0: ] EMPTY_LIST
|
1159
|
+
1: 2 DUP
|
1160
|
+
2: \x86 TUPLE2
|
1161
|
+
3: . STOP
|
1162
|
+
highest protocol among opcodes = 2
|
1163
|
+
explain_pickle in_current_sage=True/False:
|
1164
|
+
si = []
|
1165
|
+
(si, si)
|
1166
|
+
result: ([], [])
|
1167
|
+
"""
|
1168
|
+
v = self.pop()
|
1169
|
+
self.push(v)
|
1170
|
+
self.push(v)
|
1171
|
+
|
1172
|
+
def EMPTY_DICT(self):
|
1173
|
+
r"""
|
1174
|
+
TESTS::
|
1175
|
+
|
1176
|
+
sage: from pickle import *
|
1177
|
+
sage: from sage.misc.explain_pickle import *
|
1178
|
+
sage: test_pickle(EMPTY_DICT) # py2
|
1179
|
+
0: } EMPTY_DICT
|
1180
|
+
1: . STOP
|
1181
|
+
highest protocol among opcodes = 1
|
1182
|
+
explain_pickle in_current_sage=True/False:
|
1183
|
+
{}
|
1184
|
+
result: {}
|
1185
|
+
"""
|
1186
|
+
self.push(PickleObject(PickleDict([]), self.sib({})))
|
1187
|
+
|
1188
|
+
def EMPTY_LIST(self):
|
1189
|
+
r"""
|
1190
|
+
TESTS::
|
1191
|
+
|
1192
|
+
sage: from pickle import *
|
1193
|
+
sage: from sage.misc.explain_pickle import *
|
1194
|
+
sage: test_pickle(EMPTY_LIST) # py2
|
1195
|
+
0: ] EMPTY_LIST
|
1196
|
+
1: . STOP
|
1197
|
+
highest protocol among opcodes = 1
|
1198
|
+
explain_pickle in_current_sage=True/False:
|
1199
|
+
[]
|
1200
|
+
result: []
|
1201
|
+
"""
|
1202
|
+
self.push(PickleObject([], self.sib([])))
|
1203
|
+
|
1204
|
+
def EMPTY_TUPLE(self):
|
1205
|
+
r"""
|
1206
|
+
TESTS::
|
1207
|
+
|
1208
|
+
sage: from pickle import *
|
1209
|
+
sage: from sage.misc.explain_pickle import *
|
1210
|
+
sage: test_pickle(EMPTY_TUPLE) # py2
|
1211
|
+
0: ) EMPTY_TUPLE
|
1212
|
+
1: . STOP
|
1213
|
+
highest protocol among opcodes = 1
|
1214
|
+
explain_pickle in_current_sage=True/False:
|
1215
|
+
()
|
1216
|
+
result: ()
|
1217
|
+
"""
|
1218
|
+
self.push(PickleObject((), self.sib(())))
|
1219
|
+
|
1220
|
+
def EXT1(self, n):
|
1221
|
+
r"""
|
1222
|
+
TESTS::
|
1223
|
+
|
1224
|
+
sage: from copyreg import *
|
1225
|
+
sage: from sage.misc.explain_pickle import *
|
1226
|
+
sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 42)
|
1227
|
+
sage: test_pickle(EmptyNewstyleClass())
|
1228
|
+
0: \x80 PROTO 2
|
1229
|
+
2: \x82 EXT1 42
|
1230
|
+
4: ) EMPTY_TUPLE
|
1231
|
+
5: \x81 NEWOBJ
|
1232
|
+
6: q BINPUT 0
|
1233
|
+
8: . STOP
|
1234
|
+
highest protocol among opcodes = 2
|
1235
|
+
explain_pickle in_current_sage=True/False:
|
1236
|
+
unpickle_newobj(unpickle_extension(42), ())
|
1237
|
+
result: EmptyNewstyleClass
|
1238
|
+
sage: remove_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 42)
|
1239
|
+
"""
|
1240
|
+
self.push(self.sib.name('unpickle_extension')(n))
|
1241
|
+
|
1242
|
+
def EXT2(self, n):
|
1243
|
+
r"""
|
1244
|
+
TESTS::
|
1245
|
+
|
1246
|
+
sage: from copyreg import *
|
1247
|
+
sage: from sage.misc.explain_pickle import *
|
1248
|
+
sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 31415)
|
1249
|
+
sage: test_pickle(EmptyNewstyleClass())
|
1250
|
+
0: \x80 PROTO 2
|
1251
|
+
2: \x83 EXT2 31415
|
1252
|
+
5: ) EMPTY_TUPLE
|
1253
|
+
6: \x81 NEWOBJ
|
1254
|
+
7: q BINPUT 0
|
1255
|
+
9: . STOP
|
1256
|
+
highest protocol among opcodes = 2
|
1257
|
+
explain_pickle in_current_sage=True/False:
|
1258
|
+
unpickle_newobj(unpickle_extension(31415), ())
|
1259
|
+
result: EmptyNewstyleClass
|
1260
|
+
sage: remove_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 31415)
|
1261
|
+
"""
|
1262
|
+
self.push(self.sib.name('unpickle_extension')(n))
|
1263
|
+
|
1264
|
+
def EXT4(self, n):
|
1265
|
+
r"""
|
1266
|
+
TESTS::
|
1267
|
+
|
1268
|
+
sage: from copyreg import *
|
1269
|
+
sage: from sage.misc.explain_pickle import *
|
1270
|
+
sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 27182818)
|
1271
|
+
sage: test_pickle(EmptyNewstyleClass())
|
1272
|
+
0: \x80 PROTO 2
|
1273
|
+
2: \x84 EXT4 27182818
|
1274
|
+
7: ) EMPTY_TUPLE
|
1275
|
+
8: \x81 NEWOBJ
|
1276
|
+
9: q BINPUT 0
|
1277
|
+
11: . STOP
|
1278
|
+
highest protocol among opcodes = 2
|
1279
|
+
explain_pickle in_current_sage=True/False:
|
1280
|
+
unpickle_newobj(unpickle_extension(27182818), ())
|
1281
|
+
result: EmptyNewstyleClass
|
1282
|
+
sage: remove_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 27182818)
|
1283
|
+
"""
|
1284
|
+
self.push(self.sib.name('unpickle_extension')(n))
|
1285
|
+
|
1286
|
+
def FLOAT(self, f):
|
1287
|
+
r"""
|
1288
|
+
TESTS::
|
1289
|
+
|
1290
|
+
sage: from pickle import *
|
1291
|
+
sage: from sage.misc.explain_pickle import *
|
1292
|
+
sage: test_pickle(FLOAT + '2.71828\n') # py2
|
1293
|
+
0: F FLOAT 2.71828
|
1294
|
+
9: . STOP
|
1295
|
+
highest protocol among opcodes = 0
|
1296
|
+
explain_pickle in_current_sage=True/False:
|
1297
|
+
2.71828
|
1298
|
+
result: 2.71828
|
1299
|
+
"""
|
1300
|
+
self.push(self.sib(f))
|
1301
|
+
|
1302
|
+
def GET(self, n):
|
1303
|
+
r"""
|
1304
|
+
TESTS::
|
1305
|
+
|
1306
|
+
sage: from pickle import *
|
1307
|
+
sage: from sage.misc.explain_pickle import *
|
1308
|
+
sage: test_pickle(EMPTY_LIST + PUT + '1\n' + POP + GET + '1\n' + '.') # py2
|
1309
|
+
0: ] EMPTY_LIST
|
1310
|
+
1: p PUT 1
|
1311
|
+
4: 0 POP
|
1312
|
+
5: g GET 1
|
1313
|
+
8: . STOP
|
1314
|
+
highest protocol among opcodes = 1
|
1315
|
+
explain_pickle in_current_sage=True/False:
|
1316
|
+
[]
|
1317
|
+
result: []
|
1318
|
+
"""
|
1319
|
+
self.push(self.memo[n])
|
1320
|
+
|
1321
|
+
def GLOBAL(self, name):
|
1322
|
+
r"""
|
1323
|
+
TESTS::
|
1324
|
+
|
1325
|
+
sage: from sage.misc.explain_pickle import *
|
1326
|
+
|
1327
|
+
We've used register_unpickle_override so that unpickle_global
|
1328
|
+
will map TestGlobalOldName to TestGlobalNewName.
|
1329
|
+
|
1330
|
+
::
|
1331
|
+
|
1332
|
+
sage: test_pickle(TestGlobalOldName())
|
1333
|
+
0: \x80 PROTO 2
|
1334
|
+
2: c GLOBAL 'sage.misc.explain_pickle TestGlobalOldName'
|
1335
|
+
46: q BINPUT 0
|
1336
|
+
48: ) EMPTY_TUPLE
|
1337
|
+
49: \x81 NEWOBJ
|
1338
|
+
50: q BINPUT 1
|
1339
|
+
52: . STOP
|
1340
|
+
highest protocol among opcodes = 2
|
1341
|
+
explain_pickle in_current_sage=True:
|
1342
|
+
from sage.misc.explain_pickle import TestGlobalNewName
|
1343
|
+
unpickle_newobj(TestGlobalNewName, ())
|
1344
|
+
explain_pickle in_current_sage=False:
|
1345
|
+
pg_TestGlobalOldName = unpickle_global('sage.misc.explain_pickle', 'TestGlobalOldName')
|
1346
|
+
unpickle_newobj(pg_TestGlobalOldName, ())
|
1347
|
+
result: TestGlobalNewName
|
1348
|
+
|
1349
|
+
Note that default_assumptions blithely assumes that it should
|
1350
|
+
use the old name, giving code that doesn't actually work as
|
1351
|
+
desired::
|
1352
|
+
|
1353
|
+
sage: explain_pickle(dumps(TestGlobalOldName()), default_assumptions=True)
|
1354
|
+
from sage.misc.explain_pickle import TestGlobalOldName
|
1355
|
+
unpickle_newobj(TestGlobalOldName, ())
|
1356
|
+
|
1357
|
+
A class name need not be a valid identifier::
|
1358
|
+
|
1359
|
+
sage: sage.misc.explain_pickle.__dict__['funny$name'] = TestGlobalFunnyName # see comment at end of file
|
1360
|
+
sage: test_pickle((TestGlobalFunnyName(), TestGlobalFunnyName()))
|
1361
|
+
0: \x80 PROTO 2
|
1362
|
+
2: c GLOBAL 'sage.misc.explain_pickle TestGlobalFunnyName'
|
1363
|
+
48: q BINPUT 0
|
1364
|
+
50: ) EMPTY_TUPLE
|
1365
|
+
51: \x81 NEWOBJ
|
1366
|
+
52: q BINPUT 1
|
1367
|
+
54: h BINGET 0
|
1368
|
+
56: ) EMPTY_TUPLE
|
1369
|
+
57: \x81 NEWOBJ
|
1370
|
+
58: q BINPUT 2
|
1371
|
+
60: \x86 TUPLE2
|
1372
|
+
61: q BINPUT 3
|
1373
|
+
63: . STOP
|
1374
|
+
highest protocol among opcodes = 2
|
1375
|
+
explain_pickle in_current_sage=True:
|
1376
|
+
from sage.misc.explain_pickle import TestGlobalFunnyName
|
1377
|
+
(unpickle_newobj(TestGlobalFunnyName, ()), unpickle_newobj(TestGlobalFunnyName, ()))
|
1378
|
+
explain_pickle in_current_sage=False:
|
1379
|
+
pg_TestGlobalFunnyName = unpickle_global('sage.misc.explain_pickle', 'TestGlobalFunnyName')
|
1380
|
+
(unpickle_newobj(pg_TestGlobalFunnyName, ()), unpickle_newobj(pg_TestGlobalFunnyName, ()))
|
1381
|
+
result: (TestGlobalFunnyName, TestGlobalFunnyName)
|
1382
|
+
"""
|
1383
|
+
module, func = name.split(' ')
|
1384
|
+
|
1385
|
+
if self.default_assumptions:
|
1386
|
+
# Should the default assumption be that sage.all does, or
|
1387
|
+
# does not, have a conflicting variable name?
|
1388
|
+
# I'm going to go with "does not conflict".
|
1389
|
+
self.push(self.sib.import_name(module, func))
|
1390
|
+
return
|
1391
|
+
|
1392
|
+
name_ok = name_is_valid(func)
|
1393
|
+
|
1394
|
+
if self.in_current_sage and name_ok:
|
1395
|
+
override = unpickle_override.get((module, func))
|
1396
|
+
if override is None:
|
1397
|
+
__import__(module)
|
1398
|
+
f = getattr(sys.modules[module], func)
|
1399
|
+
else:
|
1400
|
+
f, new_mf = override
|
1401
|
+
if new_mf is not None:
|
1402
|
+
module, func = new_mf
|
1403
|
+
if override is None or new_mf is not None:
|
1404
|
+
# OK, we know what module and function name will actually
|
1405
|
+
# be used, as well as the actual function.
|
1406
|
+
# Is this already available at the command line?
|
1407
|
+
try:
|
1408
|
+
import sage.all
|
1409
|
+
except ImportError:
|
1410
|
+
cmdline_f = None
|
1411
|
+
else:
|
1412
|
+
cmdline_f = getattr(sage.all, func, None)
|
1413
|
+
if cmdline_f is f:
|
1414
|
+
self.push(PickleObject(f, self.sib.name(func)))
|
1415
|
+
return
|
1416
|
+
if cmdline_f is None:
|
1417
|
+
# OK, we'll go ahead and import it under the original
|
1418
|
+
# name.
|
1419
|
+
self.push(PickleObject(f, self.sib.import_name(module, func)))
|
1420
|
+
return
|
1421
|
+
# The original name is in use.
|
1422
|
+
self.push(PickleObject(f, self.sib.import_name(module, func, 'pg_' + func)))
|
1423
|
+
return
|
1424
|
+
|
1425
|
+
# We don't know the full name of the function that will
|
1426
|
+
# actually be used (either we're being generic, or
|
1427
|
+
# unpickle_override only has the function, not its name).
|
1428
|
+
v = self.sib.name('unpickle_global')(module, func)
|
1429
|
+
if name_ok:
|
1430
|
+
self.sib.use_variable(v, 'pg_' + func)
|
1431
|
+
self.push(v)
|
1432
|
+
|
1433
|
+
def INST(self, name):
|
1434
|
+
r"""
|
1435
|
+
TESTS::
|
1436
|
+
|
1437
|
+
sage: import pickle
|
1438
|
+
sage: from sage.misc.explain_pickle import *
|
1439
|
+
sage: test_pickle(pickle.dumps(EmptyOldstyleClass(), protocol=0)) # py2
|
1440
|
+
0: ( MARK
|
1441
|
+
1: i INST 'sage.misc.explain_pickle EmptyOldstyleClass' (MARK at 0)
|
1442
|
+
46: p PUT 0
|
1443
|
+
49: ( MARK
|
1444
|
+
50: d DICT (MARK at 49)
|
1445
|
+
51: p PUT 1
|
1446
|
+
54: b BUILD
|
1447
|
+
55: . STOP
|
1448
|
+
highest protocol among opcodes = 0
|
1449
|
+
explain_pickle in_current_sage=True:
|
1450
|
+
from types import InstanceType
|
1451
|
+
from sage.misc.explain_pickle import EmptyOldstyleClass
|
1452
|
+
InstanceType(EmptyOldstyleClass)
|
1453
|
+
explain_pickle in_current_sage=False:
|
1454
|
+
pg_EmptyOldstyleClass = unpickle_global('sage.misc.explain_pickle', 'EmptyOldstyleClass')
|
1455
|
+
pg = unpickle_instantiate(pg_EmptyOldstyleClass, ())
|
1456
|
+
unpickle_build(pg, {})
|
1457
|
+
pg
|
1458
|
+
result: EmptyOldstyleClass
|
1459
|
+
"""
|
1460
|
+
self.TUPLE()
|
1461
|
+
v = self.pop()
|
1462
|
+
self.GLOBAL(name)
|
1463
|
+
self.push(v)
|
1464
|
+
self.REDUCE()
|
1465
|
+
|
1466
|
+
def INT(self, n):
|
1467
|
+
r"""
|
1468
|
+
TESTS::
|
1469
|
+
|
1470
|
+
sage: from pickle import *
|
1471
|
+
sage: from sage.misc.explain_pickle import *
|
1472
|
+
sage: test_pickle(INT + "-12345\n") # py2
|
1473
|
+
0: I INT -12345
|
1474
|
+
8: . STOP
|
1475
|
+
highest protocol among opcodes = 0
|
1476
|
+
explain_pickle in_current_sage=True/False:
|
1477
|
+
-12345
|
1478
|
+
result: -12345
|
1479
|
+
|
1480
|
+
INT can also be used to record True and False::
|
1481
|
+
|
1482
|
+
sage: test_pickle(INT + "00\n") # py2
|
1483
|
+
0: I INT False
|
1484
|
+
4: . STOP
|
1485
|
+
highest protocol among opcodes = 0
|
1486
|
+
explain_pickle in_current_sage=True/False:
|
1487
|
+
False
|
1488
|
+
result: False
|
1489
|
+
sage: test_pickle(INT + "01\n") # py2
|
1490
|
+
0: I INT True
|
1491
|
+
4: . STOP
|
1492
|
+
highest protocol among opcodes = 0
|
1493
|
+
explain_pickle in_current_sage=True/False:
|
1494
|
+
True
|
1495
|
+
result: True
|
1496
|
+
"""
|
1497
|
+
self.push_and_share(self.sib(n))
|
1498
|
+
|
1499
|
+
def LIST(self):
|
1500
|
+
r"""
|
1501
|
+
TESTS::
|
1502
|
+
|
1503
|
+
sage: from pickle import *
|
1504
|
+
sage: from sage.misc.explain_pickle import *
|
1505
|
+
sage: test_pickle(MARK + NONE + NEWFALSE + LIST) # py2
|
1506
|
+
0: ( MARK
|
1507
|
+
1: N NONE
|
1508
|
+
2: \x89 NEWFALSE
|
1509
|
+
3: l LIST (MARK at 0)
|
1510
|
+
4: . STOP
|
1511
|
+
highest protocol among opcodes = 2
|
1512
|
+
explain_pickle in_current_sage=True/False:
|
1513
|
+
[None, False]
|
1514
|
+
result: [None, False]
|
1515
|
+
"""
|
1516
|
+
lst = self.pop_to_mark()
|
1517
|
+
self.push(PickleObject(lst, self.sib(lst)))
|
1518
|
+
|
1519
|
+
def LONG(self, n):
|
1520
|
+
r"""
|
1521
|
+
TESTS::
|
1522
|
+
|
1523
|
+
sage: from pickle import *
|
1524
|
+
sage: from sage.misc.explain_pickle import *
|
1525
|
+
sage: test_pickle(LONG + "12345678909876543210123456789L\n") # py2
|
1526
|
+
0: L LONG 12345678909876543210123456789L
|
1527
|
+
32: . STOP
|
1528
|
+
highest protocol among opcodes = 0
|
1529
|
+
explain_pickle in_current_sage=True/False:
|
1530
|
+
12345678909876543210123456789
|
1531
|
+
result: 12345678909876543210123456789L
|
1532
|
+
"""
|
1533
|
+
self.push(self.sib(n))
|
1534
|
+
|
1535
|
+
def LONG1(self, n):
|
1536
|
+
r"""
|
1537
|
+
TESTS::
|
1538
|
+
|
1539
|
+
sage: from sage.misc.explain_pickle import *
|
1540
|
+
sage: test_pickle(1L) # py2
|
1541
|
+
0: \x80 PROTO 2
|
1542
|
+
2: \x8a LONG1 1L
|
1543
|
+
5: . STOP
|
1544
|
+
highest protocol among opcodes = 2
|
1545
|
+
explain_pickle in_current_sage=True/False:
|
1546
|
+
1L
|
1547
|
+
result: 1L
|
1548
|
+
"""
|
1549
|
+
self.push(self.sib(n))
|
1550
|
+
|
1551
|
+
def LONG4(self, n):
|
1552
|
+
r"""
|
1553
|
+
TESTS::
|
1554
|
+
|
1555
|
+
sage: from pickle import *
|
1556
|
+
sage: from sage.misc.explain_pickle import *
|
1557
|
+
sage: test_pickle(LONG4 + '\014\0\0\0' + 'hello, world') # py2
|
1558
|
+
0: \x8b LONG4 31079605376604435891501163880L
|
1559
|
+
17: . STOP
|
1560
|
+
highest protocol among opcodes = 2
|
1561
|
+
explain_pickle in_current_sage=True/False:
|
1562
|
+
31079605376604435891501163880
|
1563
|
+
result: 31079605376604435891501163880L
|
1564
|
+
"""
|
1565
|
+
self.push(self.sib(n))
|
1566
|
+
|
1567
|
+
def LONG_BINGET(self, n):
|
1568
|
+
r"""
|
1569
|
+
TESTS::
|
1570
|
+
|
1571
|
+
sage: from pickle import *
|
1572
|
+
sage: from sage.misc.explain_pickle import *
|
1573
|
+
sage: test_pickle(EMPTY_LIST + LONG_BINPUT + 'Sage' + POP + LONG_BINGET + 'Sage') # py2
|
1574
|
+
0: ] EMPTY_LIST
|
1575
|
+
1: r LONG_BINPUT 1701273939
|
1576
|
+
6: 0 POP
|
1577
|
+
7: j LONG_BINGET 1701273939
|
1578
|
+
12: . STOP
|
1579
|
+
highest protocol among opcodes = 1
|
1580
|
+
explain_pickle in_current_sage=True/False:
|
1581
|
+
[]
|
1582
|
+
result: []
|
1583
|
+
"""
|
1584
|
+
self.push(self.memo[n])
|
1585
|
+
|
1586
|
+
def LONG_BINPUT(self, n):
|
1587
|
+
r"""
|
1588
|
+
TESTS::
|
1589
|
+
|
1590
|
+
sage: from pickle import *
|
1591
|
+
sage: from sage.misc.explain_pickle import *
|
1592
|
+
sage: test_pickle(EMPTY_LIST + LONG_BINPUT + 'Sage' + POP + LONG_BINGET + 'Sage') # py2
|
1593
|
+
0: ] EMPTY_LIST
|
1594
|
+
1: r LONG_BINPUT 1701273939
|
1595
|
+
6: 0 POP
|
1596
|
+
7: j LONG_BINGET 1701273939
|
1597
|
+
12: . STOP
|
1598
|
+
highest protocol among opcodes = 1
|
1599
|
+
explain_pickle in_current_sage=True/False:
|
1600
|
+
[]
|
1601
|
+
result: []
|
1602
|
+
"""
|
1603
|
+
v = self.pop()
|
1604
|
+
self.memo[n] = v
|
1605
|
+
self.push(v)
|
1606
|
+
|
1607
|
+
def MARK(self):
|
1608
|
+
r"""
|
1609
|
+
TESTS::
|
1610
|
+
|
1611
|
+
sage: from pickle import *
|
1612
|
+
sage: from sage.misc.explain_pickle import *
|
1613
|
+
sage: test_pickle(MARK + TUPLE) # py2
|
1614
|
+
0: ( MARK
|
1615
|
+
1: t TUPLE (MARK at 0)
|
1616
|
+
2: . STOP
|
1617
|
+
highest protocol among opcodes = 0
|
1618
|
+
explain_pickle in_current_sage=True/False:
|
1619
|
+
()
|
1620
|
+
result: ()
|
1621
|
+
"""
|
1622
|
+
self.push_mark()
|
1623
|
+
|
1624
|
+
def NEWFALSE(self):
|
1625
|
+
r"""
|
1626
|
+
TESTS::
|
1627
|
+
|
1628
|
+
sage: from pickle import *
|
1629
|
+
sage: from sage.misc.explain_pickle import *
|
1630
|
+
sage: test_pickle(NEWFALSE) # py2
|
1631
|
+
0: \x89 NEWFALSE
|
1632
|
+
1: . STOP
|
1633
|
+
highest protocol among opcodes = 2
|
1634
|
+
explain_pickle in_current_sage=True/False:
|
1635
|
+
False
|
1636
|
+
result: False
|
1637
|
+
"""
|
1638
|
+
self.push(self.sib.name('False'))
|
1639
|
+
|
1640
|
+
def NEWTRUE(self):
|
1641
|
+
r"""
|
1642
|
+
TESTS::
|
1643
|
+
|
1644
|
+
sage: from pickle import *
|
1645
|
+
sage: from sage.misc.explain_pickle import *
|
1646
|
+
sage: test_pickle(NEWTRUE) # py2
|
1647
|
+
0: \x88 NEWTRUE
|
1648
|
+
1: . STOP
|
1649
|
+
highest protocol among opcodes = 2
|
1650
|
+
explain_pickle in_current_sage=True/False:
|
1651
|
+
True
|
1652
|
+
result: True
|
1653
|
+
"""
|
1654
|
+
self.push(self.sib.name('True'))
|
1655
|
+
|
1656
|
+
def NEWOBJ(self):
|
1657
|
+
r"""
|
1658
|
+
TESTS::
|
1659
|
+
|
1660
|
+
sage: from sage.misc.explain_pickle import *
|
1661
|
+
sage: test_pickle(EmptyNewstyleClass())
|
1662
|
+
0: \x80 PROTO 2
|
1663
|
+
2: c GLOBAL 'sage.misc.explain_pickle EmptyNewstyleClass'
|
1664
|
+
47: q BINPUT 0
|
1665
|
+
49: ) EMPTY_TUPLE
|
1666
|
+
50: \x81 NEWOBJ
|
1667
|
+
51: q BINPUT 1
|
1668
|
+
53: . STOP
|
1669
|
+
highest protocol among opcodes = 2
|
1670
|
+
explain_pickle in_current_sage=True:
|
1671
|
+
from sage.misc.explain_pickle import EmptyNewstyleClass
|
1672
|
+
unpickle_newobj(EmptyNewstyleClass, ())
|
1673
|
+
explain_pickle in_current_sage=False:
|
1674
|
+
pg_EmptyNewstyleClass = unpickle_global('sage.misc.explain_pickle', 'EmptyNewstyleClass')
|
1675
|
+
unpickle_newobj(pg_EmptyNewstyleClass, ())
|
1676
|
+
result: EmptyNewstyleClass
|
1677
|
+
"""
|
1678
|
+
args = self.pop()
|
1679
|
+
klass = self.pop()
|
1680
|
+
obj = self.sib.name('unpickle_newobj')(klass, args)
|
1681
|
+
if isinstance(klass, PickleObject):
|
1682
|
+
self.push(PickleObject(PickleInstance(klass.value), obj))
|
1683
|
+
else:
|
1684
|
+
self.push(obj)
|
1685
|
+
|
1686
|
+
def NONE(self):
|
1687
|
+
r"""
|
1688
|
+
TESTS::
|
1689
|
+
|
1690
|
+
sage: from pickle import *
|
1691
|
+
sage: from sage.misc.explain_pickle import *
|
1692
|
+
sage: test_pickle(NONE) # py2
|
1693
|
+
0: N NONE
|
1694
|
+
1: . STOP
|
1695
|
+
highest protocol among opcodes = 0
|
1696
|
+
explain_pickle in_current_sage=True/False:
|
1697
|
+
None
|
1698
|
+
result: None
|
1699
|
+
"""
|
1700
|
+
self.push(PickleObject(None, self.sib.name('None')))
|
1701
|
+
|
1702
|
+
def OBJ(self):
|
1703
|
+
r"""
|
1704
|
+
TESTS::
|
1705
|
+
|
1706
|
+
sage: from sage.misc.explain_pickle import *
|
1707
|
+
sage: test_pickle(EmptyOldstyleClass())
|
1708
|
+
0: \x80 PROTO 2
|
1709
|
+
2: c GLOBAL 'sage.misc.explain_pickle EmptyOldstyleClass'
|
1710
|
+
47: q BINPUT 0
|
1711
|
+
49: ) EMPTY_TUPLE
|
1712
|
+
50: \x81 NEWOBJ
|
1713
|
+
51: q BINPUT 1
|
1714
|
+
53: . STOP
|
1715
|
+
highest protocol among opcodes = 2
|
1716
|
+
explain_pickle in_current_sage=True:
|
1717
|
+
from sage.misc.explain_pickle import EmptyOldstyleClass
|
1718
|
+
unpickle_newobj(EmptyOldstyleClass, ())
|
1719
|
+
explain_pickle in_current_sage=False:
|
1720
|
+
pg_EmptyOldstyleClass = unpickle_global('sage.misc.explain_pickle', 'EmptyOldstyleClass')
|
1721
|
+
unpickle_newobj(pg_EmptyOldstyleClass, ())
|
1722
|
+
result: EmptyOldstyleClass
|
1723
|
+
"""
|
1724
|
+
klass_args = self.pop_to_mark()
|
1725
|
+
klass = klass_args[0]
|
1726
|
+
args = klass_args[1:]
|
1727
|
+
self.push(klass)
|
1728
|
+
self.push(PickleObject(tuple(args), self.sib(tuple(args))))
|
1729
|
+
self.REDUCE()
|
1730
|
+
|
1731
|
+
def PERSID(self, id):
|
1732
|
+
r"""
|
1733
|
+
TESTS::
|
1734
|
+
|
1735
|
+
sage: from pickle import *
|
1736
|
+
sage: from sage.misc.explain_pickle import *
|
1737
|
+
sage: test_pickle(PERSID + "0\n" + '.', args=('Yo!',)) # py2
|
1738
|
+
0: P PERSID '0'
|
1739
|
+
3: . STOP
|
1740
|
+
highest protocol among opcodes = 0
|
1741
|
+
explain_pickle in_current_sage=True/False:
|
1742
|
+
unpickle_persistent('0')
|
1743
|
+
result: 'Yo!'
|
1744
|
+
"""
|
1745
|
+
self.push(self.sib.name('unpickle_persistent')(id))
|
1746
|
+
|
1747
|
+
def BINPERSID(self):
|
1748
|
+
r"""
|
1749
|
+
TESTS::
|
1750
|
+
|
1751
|
+
sage: from pickle import *
|
1752
|
+
sage: from sage.misc.explain_pickle import *
|
1753
|
+
sage: test_pickle(INT + "0\n" + BINPERSID + '.', args=('Yo!',)) # py2
|
1754
|
+
0: I INT 0
|
1755
|
+
3: Q BINPERSID
|
1756
|
+
4: . STOP
|
1757
|
+
highest protocol among opcodes = 1
|
1758
|
+
explain_pickle in_current_sage=True/False:
|
1759
|
+
unpickle_persistent(0)
|
1760
|
+
result: 'Yo!'
|
1761
|
+
"""
|
1762
|
+
id = self.pop()
|
1763
|
+
self.push(self.sib.name('unpickle_persistent')(id))
|
1764
|
+
|
1765
|
+
def POP(self):
|
1766
|
+
r"""
|
1767
|
+
TESTS::
|
1768
|
+
|
1769
|
+
sage: from pickle import *
|
1770
|
+
sage: from sage.misc.explain_pickle import *
|
1771
|
+
sage: test_pickle(INT + "0\n" + POP + INT + "42\n") # py2
|
1772
|
+
0: I INT 0
|
1773
|
+
3: 0 POP
|
1774
|
+
4: I INT 42
|
1775
|
+
8: . STOP
|
1776
|
+
highest protocol among opcodes = 0
|
1777
|
+
explain_pickle in_current_sage=True/False:
|
1778
|
+
42
|
1779
|
+
result: 42
|
1780
|
+
"""
|
1781
|
+
v = self.stack.pop()
|
1782
|
+
if v is not the_mark:
|
1783
|
+
self.check_value(v)
|
1784
|
+
|
1785
|
+
def POP_MARK(self):
|
1786
|
+
r"""
|
1787
|
+
TESTS::
|
1788
|
+
|
1789
|
+
sage: from pickle import *
|
1790
|
+
sage: from sage.misc.explain_pickle import *
|
1791
|
+
sage: test_pickle(MARK + NONE + NEWFALSE + POP_MARK + NEWTRUE) # py2
|
1792
|
+
0: ( MARK
|
1793
|
+
1: N NONE
|
1794
|
+
2: \x89 NEWFALSE
|
1795
|
+
3: 1 POP_MARK (MARK at 0)
|
1796
|
+
4: \x88 NEWTRUE
|
1797
|
+
5: . STOP
|
1798
|
+
highest protocol among opcodes = 2
|
1799
|
+
explain_pickle in_current_sage=True/False:
|
1800
|
+
True
|
1801
|
+
result: True
|
1802
|
+
"""
|
1803
|
+
self.pop_to_mark()
|
1804
|
+
|
1805
|
+
def PROTO(self, proto):
|
1806
|
+
r"""
|
1807
|
+
TESTS::
|
1808
|
+
|
1809
|
+
sage: from sage.misc.explain_pickle import *
|
1810
|
+
sage: test_pickle(0r) # py2
|
1811
|
+
0: \x80 PROTO 2
|
1812
|
+
2: K BININT1 0
|
1813
|
+
4: . STOP
|
1814
|
+
highest protocol among opcodes = 2
|
1815
|
+
explain_pickle in_current_sage=True/False:
|
1816
|
+
0
|
1817
|
+
result: 0
|
1818
|
+
"""
|
1819
|
+
if not 0 <= proto <= 2:
|
1820
|
+
raise ValueError("unsupported pickle protocol: {}".format(proto))
|
1821
|
+
|
1822
|
+
def PUT(self, n):
|
1823
|
+
r"""
|
1824
|
+
TESTS::
|
1825
|
+
|
1826
|
+
sage: from pickle import *
|
1827
|
+
sage: from sage.misc.explain_pickle import *
|
1828
|
+
sage: test_pickle(EMPTY_LIST + PUT + '1\n' + POP + GET + '1\n' + '.') # py2
|
1829
|
+
0: ] EMPTY_LIST
|
1830
|
+
1: p PUT 1
|
1831
|
+
4: 0 POP
|
1832
|
+
5: g GET 1
|
1833
|
+
8: . STOP
|
1834
|
+
highest protocol among opcodes = 1
|
1835
|
+
explain_pickle in_current_sage=True/False:
|
1836
|
+
[]
|
1837
|
+
result: []
|
1838
|
+
"""
|
1839
|
+
v = self.pop()
|
1840
|
+
self.memo[n] = v
|
1841
|
+
self.push(v)
|
1842
|
+
|
1843
|
+
def REDUCE(self):
|
1844
|
+
r"""
|
1845
|
+
TESTS::
|
1846
|
+
|
1847
|
+
sage: import pickle
|
1848
|
+
sage: from sage.misc.explain_pickle import *
|
1849
|
+
sage: test_pickle(pickle.dumps(EmptyNewstyleClass(), protocol=1)) # py2
|
1850
|
+
0: c GLOBAL 'copy_reg _reconstructor'
|
1851
|
+
25: q BINPUT 0
|
1852
|
+
27: ( MARK
|
1853
|
+
28: c GLOBAL 'sage.misc.explain_pickle EmptyNewstyleClass'
|
1854
|
+
73: q BINPUT 1
|
1855
|
+
75: c GLOBAL '__builtin__ object'
|
1856
|
+
95: q BINPUT 2
|
1857
|
+
97: N NONE
|
1858
|
+
98: t TUPLE (MARK at 27)
|
1859
|
+
99: q BINPUT 3
|
1860
|
+
101: R REDUCE
|
1861
|
+
102: q BINPUT 4
|
1862
|
+
104: . STOP
|
1863
|
+
highest protocol among opcodes = 1
|
1864
|
+
explain_pickle in_current_sage=True:
|
1865
|
+
from copy_reg import _reconstructor
|
1866
|
+
from sage.misc.explain_pickle import EmptyNewstyleClass
|
1867
|
+
from __builtin__ import object
|
1868
|
+
_reconstructor(EmptyNewstyleClass, object, None)
|
1869
|
+
explain_pickle in_current_sage=False:
|
1870
|
+
pg__reconstructor = unpickle_global('copy_reg', '_reconstructor')
|
1871
|
+
pg_EmptyNewstyleClass = unpickle_global('sage.misc.explain_pickle', 'EmptyNewstyleClass')
|
1872
|
+
pg_object = unpickle_global('__builtin__', 'object')
|
1873
|
+
pg__reconstructor(pg_EmptyNewstyleClass, pg_object, None)
|
1874
|
+
result: EmptyNewstyleClass
|
1875
|
+
|
1876
|
+
::
|
1877
|
+
|
1878
|
+
sage: test_pickle(TestReduceGetinitargs(), verbose_eval=True) # py2
|
1879
|
+
Running __init__ for TestReduceGetinitargs
|
1880
|
+
0: \x80 PROTO 2
|
1881
|
+
2: ( MARK
|
1882
|
+
3: c GLOBAL 'sage.misc.explain_pickle TestReduceGetinitargs'
|
1883
|
+
51: q BINPUT 1
|
1884
|
+
53: o OBJ (MARK at 2)
|
1885
|
+
54: q BINPUT 2
|
1886
|
+
56: } EMPTY_DICT
|
1887
|
+
57: q BINPUT 3
|
1888
|
+
59: b BUILD
|
1889
|
+
60: . STOP
|
1890
|
+
highest protocol among opcodes = 2
|
1891
|
+
explain_pickle in_current_sage=True:
|
1892
|
+
from sage.misc.explain_pickle import TestReduceGetinitargs
|
1893
|
+
TestReduceGetinitargs()
|
1894
|
+
explain_pickle in_current_sage=False:
|
1895
|
+
pg_TestReduceGetinitargs = unpickle_global('sage.misc.explain_pickle', 'TestReduceGetinitargs')
|
1896
|
+
pg = unpickle_instantiate(pg_TestReduceGetinitargs, ())
|
1897
|
+
unpickle_build(pg, {})
|
1898
|
+
pg
|
1899
|
+
evaluating explain_pickle in_current_sage=True:
|
1900
|
+
Running __init__ for TestReduceGetinitargs
|
1901
|
+
evaluating explain_pickle in_current_sage=False:
|
1902
|
+
Running __init__ for TestReduceGetinitargs
|
1903
|
+
loading pickle with cPickle:
|
1904
|
+
Running __init__ for TestReduceGetinitargs
|
1905
|
+
result: TestReduceGetinitargs
|
1906
|
+
|
1907
|
+
::
|
1908
|
+
|
1909
|
+
sage: test_pickle(TestReduceNoGetinitargs(), verbose_eval=True) # py2
|
1910
|
+
Running __init__ for TestReduceNoGetinitargs
|
1911
|
+
0: \x80 PROTO 2
|
1912
|
+
2: ( MARK
|
1913
|
+
3: c GLOBAL 'sage.misc.explain_pickle TestReduceNoGetinitargs'
|
1914
|
+
53: q BINPUT 1
|
1915
|
+
55: o OBJ (MARK at 2)
|
1916
|
+
56: q BINPUT 2
|
1917
|
+
58: } EMPTY_DICT
|
1918
|
+
59: q BINPUT 3
|
1919
|
+
61: b BUILD
|
1920
|
+
62: . STOP
|
1921
|
+
highest protocol among opcodes = 2
|
1922
|
+
explain_pickle in_current_sage=True:
|
1923
|
+
from types import InstanceType
|
1924
|
+
from sage.misc.explain_pickle import TestReduceNoGetinitargs
|
1925
|
+
InstanceType(TestReduceNoGetinitargs)
|
1926
|
+
explain_pickle in_current_sage=False:
|
1927
|
+
pg_TestReduceNoGetinitargs = unpickle_global('sage.misc.explain_pickle', 'TestReduceNoGetinitargs')
|
1928
|
+
pg = unpickle_instantiate(pg_TestReduceNoGetinitargs, ())
|
1929
|
+
unpickle_build(pg, {})
|
1930
|
+
pg
|
1931
|
+
evaluating explain_pickle in_current_sage=True:
|
1932
|
+
evaluating explain_pickle in_current_sage=False:
|
1933
|
+
loading pickle with cPickle:
|
1934
|
+
result: TestReduceNoGetinitargs
|
1935
|
+
"""
|
1936
|
+
|
1937
|
+
# Reading cPickle.c (in the Instance_New function),
|
1938
|
+
# I think that REDUCE is equivalent to a function call unless
|
1939
|
+
# all three of the following conditions are met:
|
1940
|
+
# obj is an old-style class
|
1941
|
+
# obj defines __getinitargs__
|
1942
|
+
# args is an empty tuple
|
1943
|
+
# in which case it is equivalent to PyInstance_NewRaw(obj)
|
1944
|
+
args = self.pop()
|
1945
|
+
obj = self.pop()
|
1946
|
+
simple_call = False
|
1947
|
+
new_inst = False
|
1948
|
+
if isinstance(args, PickleObject) and isinstance(args.value, tuple) \
|
1949
|
+
and len(args.value) > 0:
|
1950
|
+
simple_call = True
|
1951
|
+
if self.default_assumptions:
|
1952
|
+
simple_call = True
|
1953
|
+
if self.in_current_sage:
|
1954
|
+
if isinstance(obj, PickleObject):
|
1955
|
+
if isinstance(obj.value, type):
|
1956
|
+
simple_call = True
|
1957
|
+
elif isinstance(obj.value, ClassType):
|
1958
|
+
if hasattr(obj.value, '__getinitargs__'):
|
1959
|
+
simple_call = True
|
1960
|
+
else:
|
1961
|
+
new_inst = True
|
1962
|
+
|
1963
|
+
if simple_call:
|
1964
|
+
v = self.sib(obj)(*args.value)
|
1965
|
+
elif new_inst:
|
1966
|
+
v = self.new_instance(obj)
|
1967
|
+
else:
|
1968
|
+
v = self.sib.name('unpickle_instantiate')(obj, args)
|
1969
|
+
self.sib.use_variable(v, 'pg')
|
1970
|
+
if isinstance(obj, PickleObject):
|
1971
|
+
self.push(PickleObject(PickleInstance(obj.value), v))
|
1972
|
+
else:
|
1973
|
+
self.push(v)
|
1974
|
+
|
1975
|
+
def SETITEM(self):
|
1976
|
+
r"""
|
1977
|
+
TESTS::
|
1978
|
+
|
1979
|
+
sage: import pickle
|
1980
|
+
sage: from sage.misc.explain_pickle import *
|
1981
|
+
sage: test_pickle(pickle.dumps({'a': 'b'})) # py2
|
1982
|
+
0: ( MARK
|
1983
|
+
1: d DICT (MARK at 0)
|
1984
|
+
2: p PUT 0
|
1985
|
+
5: S STRING 'a'
|
1986
|
+
10: p PUT 1
|
1987
|
+
13: S STRING 'b'
|
1988
|
+
18: p PUT 2
|
1989
|
+
21: s SETITEM
|
1990
|
+
22: . STOP
|
1991
|
+
highest protocol among opcodes = 0
|
1992
|
+
explain_pickle in_current_sage=True/False:
|
1993
|
+
{'a':'b'}
|
1994
|
+
result: {'a': 'b'}
|
1995
|
+
|
1996
|
+
We see above that we output the result as a dictionary literal, when
|
1997
|
+
possible. This is impossible when a key or value is recursive. First
|
1998
|
+
we test recursive values::
|
1999
|
+
|
2000
|
+
sage: value_rec = dict()
|
2001
|
+
sage: value_rec['circular'] = value_rec
|
2002
|
+
sage: test_pickle(pickle.dumps(value_rec)) # py2
|
2003
|
+
0: ( MARK
|
2004
|
+
1: d DICT (MARK at 0)
|
2005
|
+
2: p PUT 0
|
2006
|
+
5: S STRING 'circular'
|
2007
|
+
17: p PUT 1
|
2008
|
+
20: g GET 0
|
2009
|
+
23: s SETITEM
|
2010
|
+
24: . STOP
|
2011
|
+
highest protocol among opcodes = 0
|
2012
|
+
explain_pickle in_current_sage=True/False:
|
2013
|
+
si = {}
|
2014
|
+
si['circular'] = si
|
2015
|
+
si
|
2016
|
+
result: {'circular': {...}}
|
2017
|
+
|
2018
|
+
Then we test recursive keys::
|
2019
|
+
|
2020
|
+
sage: key_rec = dict()
|
2021
|
+
sage: key = EmptyNewstyleClass()
|
2022
|
+
sage: key.circular = key_rec
|
2023
|
+
sage: key_rec[key] = 'circular'
|
2024
|
+
sage: test_pickle(pickle.dumps(key_rec)) # py2
|
2025
|
+
0: ( MARK
|
2026
|
+
1: d DICT (MARK at 0)
|
2027
|
+
2: p PUT 0
|
2028
|
+
5: c GLOBAL 'copy_reg _reconstructor'
|
2029
|
+
30: p PUT 1
|
2030
|
+
33: ( MARK
|
2031
|
+
34: c GLOBAL 'sage.misc.explain_pickle EmptyNewstyleClass'
|
2032
|
+
79: p PUT 2
|
2033
|
+
82: c GLOBAL '__builtin__ object'
|
2034
|
+
102: p PUT 3
|
2035
|
+
105: N NONE
|
2036
|
+
106: t TUPLE (MARK at 33)
|
2037
|
+
107: p PUT 4
|
2038
|
+
110: R REDUCE
|
2039
|
+
111: p PUT 5
|
2040
|
+
114: ( MARK
|
2041
|
+
115: d DICT (MARK at 114)
|
2042
|
+
116: p PUT 6
|
2043
|
+
119: S STRING 'circular'
|
2044
|
+
131: p PUT 7
|
2045
|
+
134: g GET 0
|
2046
|
+
137: s SETITEM
|
2047
|
+
138: b BUILD
|
2048
|
+
139: g GET 7
|
2049
|
+
142: s SETITEM
|
2050
|
+
143: . STOP
|
2051
|
+
highest protocol among opcodes = 0
|
2052
|
+
explain_pickle in_current_sage=True:
|
2053
|
+
si1 = {}
|
2054
|
+
from copy_reg import _reconstructor
|
2055
|
+
from sage.misc.explain_pickle import EmptyNewstyleClass
|
2056
|
+
from __builtin__ import object
|
2057
|
+
si2 = _reconstructor(EmptyNewstyleClass, object, None)
|
2058
|
+
si2.__dict__['circular'] = si1
|
2059
|
+
si1[si2] = 'circular'
|
2060
|
+
si1
|
2061
|
+
explain_pickle in_current_sage=False:
|
2062
|
+
si1 = {}
|
2063
|
+
pg__reconstructor = unpickle_global('copy_reg', '_reconstructor')
|
2064
|
+
pg_EmptyNewstyleClass = unpickle_global('sage.misc.explain_pickle', 'EmptyNewstyleClass')
|
2065
|
+
pg_object = unpickle_global('__builtin__', 'object')
|
2066
|
+
si2 = pg__reconstructor(pg_EmptyNewstyleClass, pg_object, None)
|
2067
|
+
unpickle_build(si2, {'circular':si1})
|
2068
|
+
si1[si2] = 'circular'
|
2069
|
+
si1
|
2070
|
+
result: {EmptyNewstyleClass: 'circular'}
|
2071
|
+
"""
|
2072
|
+
v = self.pop()
|
2073
|
+
k = self.pop()
|
2074
|
+
self._SETITEMS_helper([k, v])
|
2075
|
+
|
2076
|
+
def SETITEMS(self):
|
2077
|
+
r"""
|
2078
|
+
TESTS::
|
2079
|
+
|
2080
|
+
sage: import pickle
|
2081
|
+
sage: from sage.misc.explain_pickle import *
|
2082
|
+
sage: test_pickle(pickle.dumps({'a': 'b', 1r : 2r}, protocol=2)) # py2
|
2083
|
+
0: \x80 PROTO 2
|
2084
|
+
2: } EMPTY_DICT
|
2085
|
+
3: q BINPUT 0
|
2086
|
+
5: ( MARK
|
2087
|
+
6: U SHORT_BINSTRING 'a'
|
2088
|
+
9: q BINPUT 1
|
2089
|
+
11: U SHORT_BINSTRING 'b'
|
2090
|
+
14: q BINPUT 2
|
2091
|
+
16: K BININT1 1
|
2092
|
+
18: K BININT1 2
|
2093
|
+
20: u SETITEMS (MARK at 5)
|
2094
|
+
21: . STOP
|
2095
|
+
highest protocol among opcodes = 2
|
2096
|
+
explain_pickle in_current_sage=True/False:
|
2097
|
+
{'a':'b', 1:2}
|
2098
|
+
result: {'a': 'b', 1: 2}
|
2099
|
+
|
2100
|
+
Similar to the tests for SETITEM, we test recursive keys and values::
|
2101
|
+
|
2102
|
+
sage: recdict = {}
|
2103
|
+
sage: recdict['Circular value'] = recdict
|
2104
|
+
sage: key = EmptyOldstyleClass()
|
2105
|
+
sage: key.recdict = recdict
|
2106
|
+
sage: recdict[key] = 'circular_key'
|
2107
|
+
sage: test_pickle(pickle.dumps(recdict, protocol=2)) # py2
|
2108
|
+
0: \x80 PROTO 2
|
2109
|
+
2: } EMPTY_DICT
|
2110
|
+
3: q BINPUT 0
|
2111
|
+
5: ( MARK
|
2112
|
+
6: ( MARK
|
2113
|
+
7: c GLOBAL 'sage.misc.explain_pickle EmptyOldstyleClass'
|
2114
|
+
52: q BINPUT 1
|
2115
|
+
54: o OBJ (MARK at 6)
|
2116
|
+
55: q BINPUT 2
|
2117
|
+
57: } EMPTY_DICT
|
2118
|
+
58: q BINPUT 3
|
2119
|
+
60: U SHORT_BINSTRING 'recdict'
|
2120
|
+
69: q BINPUT 4
|
2121
|
+
71: h BINGET 0
|
2122
|
+
73: s SETITEM
|
2123
|
+
74: b BUILD
|
2124
|
+
75: U SHORT_BINSTRING 'circular_key'
|
2125
|
+
89: q BINPUT 5
|
2126
|
+
91: U SHORT_BINSTRING 'Circular value'
|
2127
|
+
107: q BINPUT 6
|
2128
|
+
109: h BINGET 0
|
2129
|
+
111: u SETITEMS (MARK at 5)
|
2130
|
+
112: . STOP
|
2131
|
+
highest protocol among opcodes = 2
|
2132
|
+
explain_pickle in_current_sage=True:
|
2133
|
+
si1 = {}
|
2134
|
+
from types import InstanceType
|
2135
|
+
from sage.misc.explain_pickle import EmptyOldstyleClass
|
2136
|
+
si2 = InstanceType(EmptyOldstyleClass)
|
2137
|
+
si2.__dict__['recdict'] = si1
|
2138
|
+
si1[si2] = 'circular_key'
|
2139
|
+
si1['Circular value'] = si1
|
2140
|
+
si1
|
2141
|
+
explain_pickle in_current_sage=False:
|
2142
|
+
si = {}
|
2143
|
+
pg_EmptyOldstyleClass = unpickle_global('sage.misc.explain_pickle', 'EmptyOldstyleClass')
|
2144
|
+
pg = unpickle_instantiate(pg_EmptyOldstyleClass, ())
|
2145
|
+
unpickle_build(pg, {'recdict':si})
|
2146
|
+
si[pg] = 'circular_key'
|
2147
|
+
si['Circular value'] = si
|
2148
|
+
si
|
2149
|
+
result: {EmptyOldstyleClass: 'circular_key', 'Circular value': {...}}
|
2150
|
+
"""
|
2151
|
+
slice = self.pop_to_mark()
|
2152
|
+
self._SETITEMS_helper(slice)
|
2153
|
+
|
2154
|
+
def _SETITEMS_helper(self, slice):
|
2155
|
+
r"""
|
2156
|
+
TESTS::
|
2157
|
+
|
2158
|
+
sage: import pickle
|
2159
|
+
sage: from sage.misc.explain_pickle import *
|
2160
|
+
sage: test_pickle(pickle.dumps({'a': 'b'})) # indirect doctest # py2
|
2161
|
+
0: ( MARK
|
2162
|
+
1: d DICT (MARK at 0)
|
2163
|
+
2: p PUT 0
|
2164
|
+
5: S STRING 'a'
|
2165
|
+
10: p PUT 1
|
2166
|
+
13: S STRING 'b'
|
2167
|
+
18: p PUT 2
|
2168
|
+
21: s SETITEM
|
2169
|
+
22: . STOP
|
2170
|
+
highest protocol among opcodes = 0
|
2171
|
+
explain_pickle in_current_sage=True/False:
|
2172
|
+
{'a':'b'}
|
2173
|
+
result: {'a': 'b'}
|
2174
|
+
"""
|
2175
|
+
updates = []
|
2176
|
+
i = 0
|
2177
|
+
while i < len(slice):
|
2178
|
+
k = slice[i]
|
2179
|
+
v = slice[i+1]
|
2180
|
+
# This marks d as immutable, if k or v happens to include d.
|
2181
|
+
self.sib(k)
|
2182
|
+
self.sib(v)
|
2183
|
+
updates.append((k, v))
|
2184
|
+
i += 2
|
2185
|
+
d = self.pop()
|
2186
|
+
if self.is_mutable_pickle_object(d) and isinstance(d.value, PickleDict):
|
2187
|
+
d.value = PickleDict(d.value.items + updates)
|
2188
|
+
d.expression = self.sib.dict(d.value.items)
|
2189
|
+
else:
|
2190
|
+
d = self.sib(d)
|
2191
|
+
for k, v in updates:
|
2192
|
+
self.sib.command(d, self.sib.assign(d[k], v))
|
2193
|
+
self.push(d)
|
2194
|
+
|
2195
|
+
def SHORT_BINSTRING(self, s):
|
2196
|
+
r"""
|
2197
|
+
TESTS::
|
2198
|
+
|
2199
|
+
sage: from sage.misc.explain_pickle import *
|
2200
|
+
sage: test_pickle(dumps('hello', compress=False)) # py2
|
2201
|
+
0: \x80 PROTO 2
|
2202
|
+
2: U SHORT_BINSTRING 'hello'
|
2203
|
+
9: q BINPUT 1
|
2204
|
+
11: . STOP
|
2205
|
+
highest protocol among opcodes = 2
|
2206
|
+
explain_pickle in_current_sage=True/False:
|
2207
|
+
'hello'
|
2208
|
+
result: 'hello'
|
2209
|
+
"""
|
2210
|
+
self.push(PickleObject(s, self.share(self.sib(s))))
|
2211
|
+
|
2212
|
+
def STOP(self):
|
2213
|
+
r"""
|
2214
|
+
TESTS::
|
2215
|
+
|
2216
|
+
sage: from pickle import *
|
2217
|
+
sage: from sage.misc.explain_pickle import *
|
2218
|
+
sage: test_pickle(EMPTY_TUPLE) # py2
|
2219
|
+
0: ) EMPTY_TUPLE
|
2220
|
+
1: . STOP
|
2221
|
+
highest protocol among opcodes = 1
|
2222
|
+
explain_pickle in_current_sage=True/False:
|
2223
|
+
()
|
2224
|
+
result: ()
|
2225
|
+
"""
|
2226
|
+
self.stopped = True
|
2227
|
+
|
2228
|
+
def STRING(self, s):
|
2229
|
+
r"""
|
2230
|
+
TESTS::
|
2231
|
+
|
2232
|
+
sage: from sage.misc.explain_pickle import *
|
2233
|
+
sage: test_pickle("S'Testing...'\n.") # py2
|
2234
|
+
0: S STRING 'Testing...'
|
2235
|
+
14: . STOP
|
2236
|
+
highest protocol among opcodes = 0
|
2237
|
+
explain_pickle in_current_sage=True/False:
|
2238
|
+
'Testing...'
|
2239
|
+
result: 'Testing...'
|
2240
|
+
"""
|
2241
|
+
self.push(PickleObject(s, self.share(self.sib(s))))
|
2242
|
+
|
2243
|
+
def TUPLE(self):
|
2244
|
+
r"""
|
2245
|
+
TESTS::
|
2246
|
+
|
2247
|
+
sage: import pickle
|
2248
|
+
sage: from sage.misc.explain_pickle import *
|
2249
|
+
sage: test_pickle(pickle.dumps(('a',))) # py2
|
2250
|
+
0: ( MARK
|
2251
|
+
1: S STRING 'a'
|
2252
|
+
6: p PUT 0
|
2253
|
+
9: t TUPLE (MARK at 0)
|
2254
|
+
10: p PUT 1
|
2255
|
+
13: . STOP
|
2256
|
+
highest protocol among opcodes = 0
|
2257
|
+
explain_pickle in_current_sage=True/False:
|
2258
|
+
('a',)
|
2259
|
+
result: ('a',)
|
2260
|
+
|
2261
|
+
We prefer to produce tuple literals, as above; but if the
|
2262
|
+
tuple is recursive, we need a more complicated
|
2263
|
+
construction. It used to be the case that the cPickle
|
2264
|
+
unpickler couldn't handle this case, but that's no longer true
|
2265
|
+
(see http://bugs.python.org/issue5794)::
|
2266
|
+
|
2267
|
+
sage: v = ([],)
|
2268
|
+
sage: v[0].append(v)
|
2269
|
+
sage: test_pickle(pickle.dumps(v)) # py2
|
2270
|
+
0: ( MARK
|
2271
|
+
1: ( MARK
|
2272
|
+
2: l LIST (MARK at 1)
|
2273
|
+
3: p PUT 0
|
2274
|
+
6: ( MARK
|
2275
|
+
7: g GET 0
|
2276
|
+
10: t TUPLE (MARK at 6)
|
2277
|
+
11: p PUT 1
|
2278
|
+
14: a APPEND
|
2279
|
+
15: 0 POP
|
2280
|
+
16: 0 POP (MARK at 0)
|
2281
|
+
17: g GET 1
|
2282
|
+
20: . STOP
|
2283
|
+
highest protocol among opcodes = 0
|
2284
|
+
explain_pickle in_current_sage=True/False:
|
2285
|
+
si1 = []
|
2286
|
+
si2 = (si1,)
|
2287
|
+
list.append(si1, si2)
|
2288
|
+
si2
|
2289
|
+
result: ([(...)],)
|
2290
|
+
"""
|
2291
|
+
v = self.pop_to_mark()
|
2292
|
+
self.push(PickleObject(tuple(v), self.sib(tuple(v))))
|
2293
|
+
|
2294
|
+
def TUPLE1(self):
|
2295
|
+
r"""
|
2296
|
+
TESTS::
|
2297
|
+
|
2298
|
+
sage: from sage.misc.explain_pickle import *
|
2299
|
+
sage: test_pickle(('a',))
|
2300
|
+
0: \x80 PROTO 2
|
2301
|
+
2: X BINUNICODE 'a'
|
2302
|
+
8: q BINPUT 0
|
2303
|
+
10: \x85 TUPLE1
|
2304
|
+
11: q BINPUT 1
|
2305
|
+
13: . STOP
|
2306
|
+
highest protocol among opcodes = 2
|
2307
|
+
explain_pickle in_current_sage=True/False:
|
2308
|
+
('a',)
|
2309
|
+
result: ('a',)
|
2310
|
+
"""
|
2311
|
+
v1 = self.pop()
|
2312
|
+
self.push(PickleObject((v1,), self.sib((v1,))))
|
2313
|
+
|
2314
|
+
def TUPLE2(self):
|
2315
|
+
r"""
|
2316
|
+
TESTS::
|
2317
|
+
|
2318
|
+
sage: from sage.misc.explain_pickle import *
|
2319
|
+
sage: test_pickle(('a','b'))
|
2320
|
+
0: \x80 PROTO 2
|
2321
|
+
2: X BINUNICODE 'a'
|
2322
|
+
8: q BINPUT 0
|
2323
|
+
10: X BINUNICODE 'b'
|
2324
|
+
16: q BINPUT 1
|
2325
|
+
18: \x86 TUPLE2
|
2326
|
+
19: q BINPUT 2
|
2327
|
+
21: . STOP
|
2328
|
+
highest protocol among opcodes = 2
|
2329
|
+
explain_pickle in_current_sage=True/False:
|
2330
|
+
('a', 'b')
|
2331
|
+
result: ('a', 'b')
|
2332
|
+
"""
|
2333
|
+
v2 = self.pop()
|
2334
|
+
v1 = self.pop()
|
2335
|
+
self.push(PickleObject((v1, v2), self.sib((v1, v2))))
|
2336
|
+
|
2337
|
+
def TUPLE3(self):
|
2338
|
+
r"""
|
2339
|
+
TESTS::
|
2340
|
+
|
2341
|
+
sage: from sage.misc.explain_pickle import *
|
2342
|
+
sage: test_pickle(('a','b','c'))
|
2343
|
+
0: \x80 PROTO 2
|
2344
|
+
2: X BINUNICODE 'a'
|
2345
|
+
8: q BINPUT 0
|
2346
|
+
10: X BINUNICODE 'b'
|
2347
|
+
16: q BINPUT 1
|
2348
|
+
18: X BINUNICODE 'c'
|
2349
|
+
24: q BINPUT 2
|
2350
|
+
26: \x87 TUPLE3
|
2351
|
+
27: q BINPUT 3
|
2352
|
+
29: . STOP
|
2353
|
+
highest protocol among opcodes = 2
|
2354
|
+
explain_pickle in_current_sage=True/False:
|
2355
|
+
('a', 'b', 'c')
|
2356
|
+
result: ('a', 'b', 'c')
|
2357
|
+
"""
|
2358
|
+
v3 = self.pop()
|
2359
|
+
v2 = self.pop()
|
2360
|
+
v1 = self.pop()
|
2361
|
+
self.push(PickleObject((v1, v2, v3), self.sib((v1, v2, v3))))
|
2362
|
+
|
2363
|
+
def UNICODE(self, s):
|
2364
|
+
r"""
|
2365
|
+
TESTS::
|
2366
|
+
|
2367
|
+
sage: import pickle
|
2368
|
+
sage: from sage.misc.explain_pickle import *
|
2369
|
+
sage: test_pickle(pickle.dumps(u'hi\u1234\U00012345')) # py2
|
2370
|
+
0: V UNICODE u'hi\u1234\U00012345'
|
2371
|
+
20: p PUT 0
|
2372
|
+
23: . STOP
|
2373
|
+
highest protocol among opcodes = 0
|
2374
|
+
explain_pickle in_current_sage=True/False:
|
2375
|
+
u'hi\u1234\U00012345'
|
2376
|
+
result: u'hi\u1234\U00012345'
|
2377
|
+
"""
|
2378
|
+
self.push_and_share(self.sib(s))
|
2379
|
+
|
2380
|
+
|
2381
|
+
# Helper routines for explain_pickle
|
2382
|
+
|
2383
|
+
def unpickle_newobj(klass, args):
|
2384
|
+
r"""
|
2385
|
+
Create a new object; this corresponds to the C code
|
2386
|
+
``klass->tp_new(klass, args, NULL)``. Used by :func:`explain_pickle`.
|
2387
|
+
|
2388
|
+
EXAMPLES::
|
2389
|
+
|
2390
|
+
sage: unpickle_newobj(tuple, ([1, 2, 3],))
|
2391
|
+
(1, 2, 3)
|
2392
|
+
|
2393
|
+
TESTS:
|
2394
|
+
|
2395
|
+
We can create a :class:`Sequence_generic` which would not work with
|
2396
|
+
a pure Python implementation. We just test that this does not raise
|
2397
|
+
an exception, we cannot do anything with ``s`` since ``s.__init__``
|
2398
|
+
was never called::
|
2399
|
+
|
2400
|
+
sage: from sage.structure.sequence import Sequence_generic
|
2401
|
+
sage: s = unpickle_newobj(Sequence_generic, ([1, 2, 3],))
|
2402
|
+
"""
|
2403
|
+
# We need to call klass->tp_new(klass, args, NULL).
|
2404
|
+
# This is almost but not quite the same as klass.__new__(klass, *args).
|
2405
|
+
#
|
2406
|
+
# The reason is that the __new__ method does additional checking:
|
2407
|
+
# When you try to unpickle a Sequence, cPickle -- which uses the
|
2408
|
+
# former -- works, and pickle.py -- which uses the latter -- fails,
|
2409
|
+
# with
|
2410
|
+
# TypeError: sage.structure.sage_object.SageObject.__new__(Sequence) is not safe, use list.__new__()
|
2411
|
+
#
|
2412
|
+
# It seems unlikely that you can implement this from pure-Python
|
2413
|
+
# code. As a hack, we use cPickle itself to make it work.
|
2414
|
+
# (Using Cython would also work, of course; but this is cooler, and
|
2415
|
+
# probably simpler.)
|
2416
|
+
|
2417
|
+
# This pickle is: load persistent object 0, load persistent object 1,
|
2418
|
+
# NEWOBJ, STOP.
|
2419
|
+
pickle = b"P0\nP1\n\x81."
|
2420
|
+
|
2421
|
+
pers = [klass, args]
|
2422
|
+
|
2423
|
+
def pers_load(id):
|
2424
|
+
return pers[int(id)]
|
2425
|
+
|
2426
|
+
return SageUnpickler.loads(pickle, persistent_load=pers_load)
|
2427
|
+
|
2428
|
+
|
2429
|
+
def unpickle_build(obj, state):
|
2430
|
+
r"""
|
2431
|
+
Set the state of an object. Used by :func:`explain_pickle`.
|
2432
|
+
|
2433
|
+
EXAMPLES::
|
2434
|
+
|
2435
|
+
sage: from sage.misc.explain_pickle import *
|
2436
|
+
sage: v = EmptyNewstyleClass()
|
2437
|
+
sage: unpickle_build(v, {'hello': 42})
|
2438
|
+
sage: v.hello
|
2439
|
+
42
|
2440
|
+
"""
|
2441
|
+
setstate = getattr(obj, '__setstate__', None)
|
2442
|
+
if setstate is not None:
|
2443
|
+
setstate(state)
|
2444
|
+
return
|
2445
|
+
|
2446
|
+
if isinstance(state, tuple) and len(state) == 2:
|
2447
|
+
state, slots = state
|
2448
|
+
else:
|
2449
|
+
slots = None
|
2450
|
+
|
2451
|
+
if state is not None:
|
2452
|
+
assert isinstance(state, dict)
|
2453
|
+
d = obj.__dict__
|
2454
|
+
for k, v in state.items():
|
2455
|
+
d[k] = v
|
2456
|
+
|
2457
|
+
if slots is not None:
|
2458
|
+
assert isinstance(slots, dict)
|
2459
|
+
for k, v in slots.items():
|
2460
|
+
setattr(obj, k, v)
|
2461
|
+
|
2462
|
+
|
2463
|
+
def unpickle_instantiate(fn, args):
|
2464
|
+
r"""
|
2465
|
+
Instantiate a new object of class ``fn`` with arguments ``args``. Almost always
|
2466
|
+
equivalent to ``fn(*args)``. Used by :func:`explain_pickle`.
|
2467
|
+
|
2468
|
+
EXAMPLES::
|
2469
|
+
|
2470
|
+
sage: unpickle_instantiate(Integer, ('42',))
|
2471
|
+
42
|
2472
|
+
"""
|
2473
|
+
if isinstance(fn, ClassType) and not args and not hasattr(fn, '__getinitargs__'):
|
2474
|
+
# types.InstanceType doesn't exist on Python 3, but that's not
|
2475
|
+
# a problem since the above condition is always False.
|
2476
|
+
return types.InstanceType(fn)
|
2477
|
+
|
2478
|
+
return fn(*args)
|
2479
|
+
|
2480
|
+
|
2481
|
+
unpickle_persistent_loader = None
|
2482
|
+
|
2483
|
+
|
2484
|
+
def unpickle_persistent(s):
|
2485
|
+
r"""
|
2486
|
+
Take an integer index and return the persistent object with that
|
2487
|
+
index; works by calling whatever callable is stored in
|
2488
|
+
``unpickle_persistent_loader``. Used by :func:`explain_pickle`.
|
2489
|
+
|
2490
|
+
EXAMPLES::
|
2491
|
+
|
2492
|
+
sage: import sage.misc.explain_pickle
|
2493
|
+
sage: sage.misc.explain_pickle.unpickle_persistent_loader = lambda n: n+7
|
2494
|
+
sage: unpickle_persistent(35)
|
2495
|
+
42
|
2496
|
+
"""
|
2497
|
+
return unpickle_persistent_loader(s)
|
2498
|
+
|
2499
|
+
|
2500
|
+
def unpickle_extension(code):
|
2501
|
+
r"""
|
2502
|
+
Take an integer index and return the extension object with that
|
2503
|
+
index. Used by :func:`explain_pickle`.
|
2504
|
+
|
2505
|
+
EXAMPLES::
|
2506
|
+
|
2507
|
+
sage: from copyreg import *
|
2508
|
+
sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 42)
|
2509
|
+
sage: unpickle_extension(42)
|
2510
|
+
<class 'sage.misc.explain_pickle.EmptyNewstyleClass'>
|
2511
|
+
sage: remove_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 42)
|
2512
|
+
"""
|
2513
|
+
from copyreg import _inverted_registry, _extension_cache
|
2514
|
+
# copied from .get_extension() in pickle.py
|
2515
|
+
nil = []
|
2516
|
+
obj = _extension_cache.get(code, nil)
|
2517
|
+
if obj is not nil:
|
2518
|
+
return obj
|
2519
|
+
key = _inverted_registry.get(code)
|
2520
|
+
if not key:
|
2521
|
+
raise ValueError("unregistered extension code %d" % code)
|
2522
|
+
obj = unpickle_global(*key)
|
2523
|
+
_extension_cache[code] = obj
|
2524
|
+
return obj
|
2525
|
+
|
2526
|
+
|
2527
|
+
def unpickle_appends(lst, vals):
|
2528
|
+
r"""
|
2529
|
+
Given a list (or list-like object) and a sequence of values, appends the
|
2530
|
+
values to the end of the list. This is careful to do so using the
|
2531
|
+
exact same technique that cPickle would use. Used by :func:`explain_pickle`.
|
2532
|
+
|
2533
|
+
EXAMPLES::
|
2534
|
+
|
2535
|
+
sage: v = []
|
2536
|
+
sage: unpickle_appends(v, (1, 2, 3))
|
2537
|
+
sage: v
|
2538
|
+
[1, 2, 3]
|
2539
|
+
"""
|
2540
|
+
if isinstance(lst, list):
|
2541
|
+
# If lst is a list (or a subtype of list)
|
2542
|
+
list.extend(lst, vals)
|
2543
|
+
else:
|
2544
|
+
append = lst.append
|
2545
|
+
for v in vals:
|
2546
|
+
append(v)
|
2547
|
+
|
2548
|
+
|
2549
|
+
def test_pickle(p, verbose_eval=False, pedantic=False, args=()):
|
2550
|
+
r"""
|
2551
|
+
Test :func:`explain_pickle` on a given pickle ``p``.
|
2552
|
+
|
2553
|
+
``p`` can be:
|
2554
|
+
|
2555
|
+
- a string containing an uncompressed pickle (which will always end
|
2556
|
+
with a '.')
|
2557
|
+
|
2558
|
+
- a string containing a pickle fragment (not ending with '.')
|
2559
|
+
test_pickle will synthesize a pickle that will push args onto
|
2560
|
+
the stack (using persistent IDs), run the pickle fragment, and then
|
2561
|
+
STOP (if the string 'mark' occurs in args, then a mark will be pushed)
|
2562
|
+
|
2563
|
+
- an arbitrary object; :func:`test_pickle` will pickle the object
|
2564
|
+
|
2565
|
+
Once it has a pickle, :func:`test_pickle` will print the pickle's
|
2566
|
+
disassembly, run :func:`explain_pickle` with ``in_current_sage=True`` and
|
2567
|
+
``False``, print the results, evaluate the results, unpickle the
|
2568
|
+
object with cPickle, and compare all three results.
|
2569
|
+
|
2570
|
+
If ``verbose_eval`` is ``True``, then :func:`test_pickle` will print messages
|
2571
|
+
before evaluating the pickles; this is to allow for tests where
|
2572
|
+
the unpickling prints messages (to verify that the same operations
|
2573
|
+
occur in all cases).
|
2574
|
+
|
2575
|
+
EXAMPLES::
|
2576
|
+
|
2577
|
+
sage: from sage.misc.explain_pickle import *
|
2578
|
+
sage: test_pickle(['a'])
|
2579
|
+
0: \x80 PROTO 2
|
2580
|
+
2: ] EMPTY_LIST
|
2581
|
+
3: q BINPUT 0
|
2582
|
+
5: X BINUNICODE 'a'
|
2583
|
+
11: q BINPUT 1
|
2584
|
+
13: a APPEND
|
2585
|
+
14: . STOP
|
2586
|
+
highest protocol among opcodes = 2
|
2587
|
+
explain_pickle in_current_sage=True/False:
|
2588
|
+
['a']
|
2589
|
+
result: ['a']
|
2590
|
+
"""
|
2591
|
+
start = ''
|
2592
|
+
for n in range(len(args)):
|
2593
|
+
a = args[n]
|
2594
|
+
if a == 'mark':
|
2595
|
+
start += '('
|
2596
|
+
else:
|
2597
|
+
start += "P%d\n" % n
|
2598
|
+
|
2599
|
+
if isinstance(p, str):
|
2600
|
+
if p[-1] != '.':
|
2601
|
+
p = start + p + '.'
|
2602
|
+
else:
|
2603
|
+
p = dumps(p, compress=False)
|
2604
|
+
|
2605
|
+
pickletools.dis(p)
|
2606
|
+
|
2607
|
+
current = explain_pickle(p, compress=False, in_current_sage=True, pedantic=pedantic, preparse=False)
|
2608
|
+
generic = explain_pickle(p, compress=False, pedantic=pedantic, preparse=False)
|
2609
|
+
|
2610
|
+
if current == generic:
|
2611
|
+
print("explain_pickle in_current_sage=True/False:")
|
2612
|
+
print(current)
|
2613
|
+
else:
|
2614
|
+
print("explain_pickle in_current_sage=True:")
|
2615
|
+
print(current)
|
2616
|
+
print("explain_pickle in_current_sage=False:")
|
2617
|
+
print(generic)
|
2618
|
+
|
2619
|
+
def pers_load(s):
|
2620
|
+
return args[int(s)]
|
2621
|
+
|
2622
|
+
global unpickle_persistent_loader
|
2623
|
+
unpickle_persistent_loader = pers_load
|
2624
|
+
|
2625
|
+
if verbose_eval:
|
2626
|
+
print("evaluating explain_pickle in_current_sage=True:")
|
2627
|
+
current_res = sage_eval(current, preparse=False)
|
2628
|
+
if verbose_eval:
|
2629
|
+
print("evaluating explain_pickle in_current_sage=False:")
|
2630
|
+
generic_res = sage_eval(generic, preparse=False)
|
2631
|
+
if verbose_eval:
|
2632
|
+
print("loading pickle with cPickle:")
|
2633
|
+
|
2634
|
+
try:
|
2635
|
+
cpickle_res = SageUnpickler.loads(p, persistent_load=pers_load)
|
2636
|
+
cpickle_ok = True
|
2637
|
+
except Exception:
|
2638
|
+
cpickle_ok = False
|
2639
|
+
|
2640
|
+
current_repr = repr(current_res)
|
2641
|
+
generic_repr = repr(generic_res)
|
2642
|
+
|
2643
|
+
if cpickle_ok:
|
2644
|
+
cpickle_repr = repr(cpickle_res)
|
2645
|
+
|
2646
|
+
assert current_repr == generic_repr == cpickle_repr
|
2647
|
+
|
2648
|
+
print("result: " + current_repr)
|
2649
|
+
else:
|
2650
|
+
assert current_repr == generic_repr
|
2651
|
+
print("result: " + current_repr + " (cPickle raised an exception!)")
|
2652
|
+
|
2653
|
+
|
2654
|
+
class EmptyOldstyleClass:
|
2655
|
+
r"""
|
2656
|
+
A featureless old-style class (does not inherit from object); used for
|
2657
|
+
testing :func:`explain_pickle`.
|
2658
|
+
"""
|
2659
|
+
def __repr__(self):
|
2660
|
+
r"""
|
2661
|
+
Print an EmptyOldstyleClass.
|
2662
|
+
|
2663
|
+
EXAMPLES::
|
2664
|
+
|
2665
|
+
sage: from sage.misc.explain_pickle import *
|
2666
|
+
sage: v = EmptyOldstyleClass()
|
2667
|
+
sage: v
|
2668
|
+
EmptyOldstyleClass
|
2669
|
+
sage: repr(v)
|
2670
|
+
'EmptyOldstyleClass'
|
2671
|
+
sage: v.__repr__()
|
2672
|
+
'EmptyOldstyleClass'
|
2673
|
+
"""
|
2674
|
+
return "EmptyOldstyleClass"
|
2675
|
+
|
2676
|
+
def __hash__(self):
|
2677
|
+
r"""
|
2678
|
+
Produce a predictable hash value for EmptyOldstyleClass.
|
2679
|
+
|
2680
|
+
EXAMPLES::
|
2681
|
+
|
2682
|
+
sage: from sage.misc.explain_pickle import *
|
2683
|
+
sage: v = EmptyOldstyleClass()
|
2684
|
+
sage: hash(v)
|
2685
|
+
0
|
2686
|
+
sage: v.__hash__()
|
2687
|
+
0
|
2688
|
+
"""
|
2689
|
+
return 0
|
2690
|
+
|
2691
|
+
|
2692
|
+
class EmptyNewstyleClass:
|
2693
|
+
r"""
|
2694
|
+
A featureless new-style class (inherits from object); used for
|
2695
|
+
testing :func:`explain_pickle`.
|
2696
|
+
"""
|
2697
|
+
def __repr__(self):
|
2698
|
+
r"""
|
2699
|
+
Print an EmptyNewstyleClass.
|
2700
|
+
|
2701
|
+
EXAMPLES::
|
2702
|
+
|
2703
|
+
sage: from sage.misc.explain_pickle import *
|
2704
|
+
sage: v = EmptyNewstyleClass()
|
2705
|
+
sage: v
|
2706
|
+
EmptyNewstyleClass
|
2707
|
+
sage: repr(v)
|
2708
|
+
'EmptyNewstyleClass'
|
2709
|
+
sage: v.__repr__()
|
2710
|
+
'EmptyNewstyleClass'
|
2711
|
+
"""
|
2712
|
+
return "EmptyNewstyleClass"
|
2713
|
+
|
2714
|
+
|
2715
|
+
class TestReduceGetinitargs:
|
2716
|
+
r"""
|
2717
|
+
An old-style class with a :func:`__getinitargs__` method. Used for testing
|
2718
|
+
:func:`explain_pickle`.
|
2719
|
+
"""
|
2720
|
+
def __init__(self):
|
2721
|
+
r"""
|
2722
|
+
Initialize a TestReduceGetinitargs object. Note that the
|
2723
|
+
constructor prints out a message.
|
2724
|
+
|
2725
|
+
EXAMPLES::
|
2726
|
+
|
2727
|
+
sage: from sage.misc.explain_pickle import *
|
2728
|
+
sage: TestReduceGetinitargs()
|
2729
|
+
Running __init__ for TestReduceGetinitargs
|
2730
|
+
TestReduceGetinitargs
|
2731
|
+
"""
|
2732
|
+
print("Running __init__ for TestReduceGetinitargs")
|
2733
|
+
|
2734
|
+
def __getinitargs__(self):
|
2735
|
+
r"""
|
2736
|
+
A simple __getinitargs__ method, used for testing explain_pickle.
|
2737
|
+
|
2738
|
+
EXAMPLES::
|
2739
|
+
|
2740
|
+
sage: from sage.misc.explain_pickle import *
|
2741
|
+
sage: v = TestReduceGetinitargs()
|
2742
|
+
Running __init__ for TestReduceGetinitargs
|
2743
|
+
sage: v.__getinitargs__()
|
2744
|
+
()
|
2745
|
+
"""
|
2746
|
+
return ()
|
2747
|
+
|
2748
|
+
def __repr__(self):
|
2749
|
+
r"""
|
2750
|
+
Print a TestReduceGetinitargs.
|
2751
|
+
|
2752
|
+
EXAMPLES::
|
2753
|
+
|
2754
|
+
sage: from sage.misc.explain_pickle import *
|
2755
|
+
sage: v = TestReduceGetinitargs()
|
2756
|
+
Running __init__ for TestReduceGetinitargs
|
2757
|
+
sage: v
|
2758
|
+
TestReduceGetinitargs
|
2759
|
+
sage: repr(v)
|
2760
|
+
'TestReduceGetinitargs'
|
2761
|
+
sage: v.__repr__()
|
2762
|
+
'TestReduceGetinitargs'
|
2763
|
+
"""
|
2764
|
+
return "TestReduceGetinitargs"
|
2765
|
+
|
2766
|
+
|
2767
|
+
class TestReduceNoGetinitargs:
|
2768
|
+
r"""
|
2769
|
+
An old-style class with no :meth:`__getinitargs__` method. Used for testing
|
2770
|
+
:func:`explain_pickle`.
|
2771
|
+
"""
|
2772
|
+
def __init__(self):
|
2773
|
+
r"""
|
2774
|
+
Initialize a TestReduceNoGetinitargs object. Note that the
|
2775
|
+
constructor prints out a message.
|
2776
|
+
|
2777
|
+
EXAMPLES::
|
2778
|
+
|
2779
|
+
sage: from sage.misc.explain_pickle import *
|
2780
|
+
sage: TestReduceNoGetinitargs()
|
2781
|
+
Running __init__ for TestReduceNoGetinitargs
|
2782
|
+
TestReduceNoGetinitargs
|
2783
|
+
"""
|
2784
|
+
print("Running __init__ for TestReduceNoGetinitargs")
|
2785
|
+
|
2786
|
+
def __repr__(self):
|
2787
|
+
r"""
|
2788
|
+
Print a TestReduceNoGetinitargs.
|
2789
|
+
|
2790
|
+
EXAMPLES::
|
2791
|
+
|
2792
|
+
sage: from sage.misc.explain_pickle import *
|
2793
|
+
sage: v = TestReduceNoGetinitargs()
|
2794
|
+
Running __init__ for TestReduceNoGetinitargs
|
2795
|
+
sage: v
|
2796
|
+
TestReduceNoGetinitargs
|
2797
|
+
sage: repr(v)
|
2798
|
+
'TestReduceNoGetinitargs'
|
2799
|
+
sage: v.__repr__()
|
2800
|
+
'TestReduceNoGetinitargs'
|
2801
|
+
"""
|
2802
|
+
return "TestReduceNoGetinitargs"
|
2803
|
+
|
2804
|
+
|
2805
|
+
class TestAppendList(list):
|
2806
|
+
r"""
|
2807
|
+
A subclass of :class:`list`, with deliberately-broken append and extend methods.
|
2808
|
+
Used for testing :func:`explain_pickle`.
|
2809
|
+
"""
|
2810
|
+
def append(self):
|
2811
|
+
r"""
|
2812
|
+
A deliberately broken append method.
|
2813
|
+
|
2814
|
+
EXAMPLES::
|
2815
|
+
|
2816
|
+
sage: from sage.misc.explain_pickle import *
|
2817
|
+
sage: v = TestAppendList()
|
2818
|
+
sage: v.append(7)
|
2819
|
+
Traceback (most recent call last):
|
2820
|
+
...
|
2821
|
+
TypeError: ...append() takes 1 positional argument but 2 were given
|
2822
|
+
|
2823
|
+
We can still append by directly using the list method::
|
2824
|
+
|
2825
|
+
sage: list.append(v, 7)
|
2826
|
+
sage: v
|
2827
|
+
[7]
|
2828
|
+
"""
|
2829
|
+
raise NotImplementedError
|
2830
|
+
|
2831
|
+
def extend(self):
|
2832
|
+
r"""
|
2833
|
+
A deliberately broken extend method.
|
2834
|
+
|
2835
|
+
EXAMPLES::
|
2836
|
+
|
2837
|
+
sage: from sage.misc.explain_pickle import *
|
2838
|
+
sage: v = TestAppendList()
|
2839
|
+
sage: v.extend([3,1,4,1,5,9])
|
2840
|
+
Traceback (most recent call last):
|
2841
|
+
...
|
2842
|
+
TypeError: ...extend() takes 1 positional argument but 2 were given
|
2843
|
+
|
2844
|
+
We can still extend by directly using the list method::
|
2845
|
+
|
2846
|
+
sage: list.extend(v, (3,1,4,1,5,9))
|
2847
|
+
sage: v
|
2848
|
+
[3, 1, 4, 1, 5, 9]
|
2849
|
+
"""
|
2850
|
+
raise NotImplementedError
|
2851
|
+
|
2852
|
+
|
2853
|
+
class TestAppendNonlist:
|
2854
|
+
r"""
|
2855
|
+
A list-like class, carefully designed to test exact unpickling
|
2856
|
+
behavior. Used for testing :func:`explain_pickle`.
|
2857
|
+
"""
|
2858
|
+
def __init__(self):
|
2859
|
+
r"""
|
2860
|
+
Construct a TestAppendNonlist.
|
2861
|
+
|
2862
|
+
EXAMPLES::
|
2863
|
+
|
2864
|
+
sage: from sage.misc.explain_pickle import *
|
2865
|
+
sage: v = TestAppendNonlist()
|
2866
|
+
sage: v
|
2867
|
+
[]
|
2868
|
+
"""
|
2869
|
+
self.list = []
|
2870
|
+
|
2871
|
+
def __getattr__(self, a):
|
2872
|
+
r"""
|
2873
|
+
Get an 'append' method from a TestAppendNonlist. We have this
|
2874
|
+
method so that we can distinguish how many times the append method
|
2875
|
+
is fetched.
|
2876
|
+
|
2877
|
+
EXAMPLES::
|
2878
|
+
|
2879
|
+
sage: from sage.misc.explain_pickle import *
|
2880
|
+
sage: v = TestAppendNonlist()
|
2881
|
+
sage: v.append(1)
|
2882
|
+
Fetching append attribute
|
2883
|
+
sage: v.append(2)
|
2884
|
+
Fetching append attribute
|
2885
|
+
sage: app = v.append
|
2886
|
+
Fetching append attribute
|
2887
|
+
sage: app(3)
|
2888
|
+
sage: app(4)
|
2889
|
+
sage: v
|
2890
|
+
[1, 2, 3, 4]
|
2891
|
+
"""
|
2892
|
+
if a == 'append':
|
2893
|
+
print("Fetching append attribute")
|
2894
|
+
return self.list.append
|
2895
|
+
|
2896
|
+
raise AttributeError
|
2897
|
+
|
2898
|
+
def __reduce__(self):
|
2899
|
+
r"""
|
2900
|
+
Implement __reduce__ for TestAppendNonlist. Note that the
|
2901
|
+
loads(dumps(...)) test only fetches the append method once.
|
2902
|
+
|
2903
|
+
EXAMPLES::
|
2904
|
+
|
2905
|
+
sage: from sage.misc.explain_pickle import *
|
2906
|
+
sage: v = TestAppendNonlist()
|
2907
|
+
sage: v.list = [1,2,3,4]
|
2908
|
+
sage: v.__reduce__()
|
2909
|
+
(<class 'sage.misc.explain_pickle.TestAppendNonlist'>, (), None, <...iterator object at 0x...>)
|
2910
|
+
sage: list(v.__reduce__()[3])
|
2911
|
+
[1, 2, 3, 4]
|
2912
|
+
sage: loads(dumps(v))
|
2913
|
+
Fetching append attribute
|
2914
|
+
[1, 2, 3, 4]
|
2915
|
+
"""
|
2916
|
+
return (TestAppendNonlist, (), None, iter(self.list))
|
2917
|
+
|
2918
|
+
def __repr__(self):
|
2919
|
+
r"""
|
2920
|
+
Print a TestAppendNonlist. Just prints as its underlying list.
|
2921
|
+
|
2922
|
+
EXAMPLES::
|
2923
|
+
|
2924
|
+
sage: from sage.misc.explain_pickle import *
|
2925
|
+
sage: v = TestAppendNonlist()
|
2926
|
+
sage: v.list = ['hello', 'world']
|
2927
|
+
sage: v
|
2928
|
+
['hello', 'world']
|
2929
|
+
sage: repr(v)
|
2930
|
+
"['hello', 'world']"
|
2931
|
+
sage: v.__repr__()
|
2932
|
+
"['hello', 'world']"
|
2933
|
+
"""
|
2934
|
+
return repr(self.list)
|
2935
|
+
|
2936
|
+
|
2937
|
+
class TestBuild:
|
2938
|
+
r"""
|
2939
|
+
A simple class with a :meth:`__getstate__` but no :meth:`__setstate__`. Used for testing
|
2940
|
+
:func:`explain_pickle`.
|
2941
|
+
"""
|
2942
|
+
def __getstate__(self):
|
2943
|
+
r"""
|
2944
|
+
A __getstate__ method for testing pickling.
|
2945
|
+
|
2946
|
+
EXAMPLES::
|
2947
|
+
|
2948
|
+
sage: from sage.misc.explain_pickle import *
|
2949
|
+
sage: TestBuild().__getstate__()
|
2950
|
+
({'x': 3}, {'y': 4})
|
2951
|
+
sage: loads(dumps(TestBuild()))
|
2952
|
+
TestBuild: x=3; y=4
|
2953
|
+
"""
|
2954
|
+
return ({'x': 3}, {'y': 4})
|
2955
|
+
|
2956
|
+
def __repr__(self):
|
2957
|
+
r"""
|
2958
|
+
Print a TestBuild.
|
2959
|
+
|
2960
|
+
EXAMPLES::
|
2961
|
+
|
2962
|
+
sage: from sage.misc.explain_pickle import *
|
2963
|
+
sage: v = TestBuild()
|
2964
|
+
sage: v
|
2965
|
+
TestBuild: x=None; y=None
|
2966
|
+
sage: repr(v)
|
2967
|
+
'TestBuild: x=None; y=None'
|
2968
|
+
sage: v.__repr__()
|
2969
|
+
'TestBuild: x=None; y=None'
|
2970
|
+
"""
|
2971
|
+
return "TestBuild: x=%s; y=%s" % (getattr(self, 'x', None), getattr(self, 'y', None))
|
2972
|
+
|
2973
|
+
|
2974
|
+
class TestBuildSetstate(TestBuild):
|
2975
|
+
r"""
|
2976
|
+
A simple class with a :meth:`__getstate__` and a :meth:`__setstate__`. Used for testing
|
2977
|
+
:func:`explain_pickle`.
|
2978
|
+
"""
|
2979
|
+
def __setstate__(self, state):
|
2980
|
+
r"""
|
2981
|
+
Set the state of a TestBuildSetstate. Both prints a message, and
|
2982
|
+
swaps x and y, to verify that it is being called.
|
2983
|
+
|
2984
|
+
EXAMPLES::
|
2985
|
+
|
2986
|
+
sage: from sage.misc.explain_pickle import *
|
2987
|
+
sage: loads(dumps(TestBuildSetstate())) # indirect doctest
|
2988
|
+
setting state from ({'x': 3}, {'y': 4})
|
2989
|
+
TestBuild: x=4; y=3
|
2990
|
+
"""
|
2991
|
+
print("setting state from {}".format(state))
|
2992
|
+
# Swap x and y, just for fun
|
2993
|
+
self.x = state[1]['y']
|
2994
|
+
self.y = state[0]['x']
|
2995
|
+
|
2996
|
+
|
2997
|
+
class TestGlobalOldName:
|
2998
|
+
r"""
|
2999
|
+
A featureless new-style class. When you try to unpickle an instance
|
3000
|
+
of this class, it is redirected to create a :class:`TestGlobalNewName` instead.
|
3001
|
+
Used for testing :func:`explain_pickle`.
|
3002
|
+
|
3003
|
+
EXAMPLES::
|
3004
|
+
|
3005
|
+
sage: from sage.misc.explain_pickle import *
|
3006
|
+
sage: loads(dumps(TestGlobalOldName()))
|
3007
|
+
TestGlobalNewName
|
3008
|
+
"""
|
3009
|
+
pass
|
3010
|
+
|
3011
|
+
|
3012
|
+
class TestGlobalNewName:
|
3013
|
+
r"""
|
3014
|
+
A featureless new-style class. When you try to unpickle an instance
|
3015
|
+
of :class:`TestGlobalOldName`, it is redirected to create an instance of this
|
3016
|
+
class instead. Used for testing :func:`explain_pickle`.
|
3017
|
+
|
3018
|
+
EXAMPLES::
|
3019
|
+
|
3020
|
+
sage: from sage.misc.explain_pickle import *
|
3021
|
+
sage: loads(dumps(TestGlobalOldName()))
|
3022
|
+
TestGlobalNewName
|
3023
|
+
"""
|
3024
|
+
def __repr__(self):
|
3025
|
+
r"""
|
3026
|
+
Print a TestGlobalNewName.
|
3027
|
+
|
3028
|
+
EXAMPLES::
|
3029
|
+
|
3030
|
+
sage: from sage.misc.explain_pickle import *
|
3031
|
+
sage: v = TestGlobalNewName()
|
3032
|
+
sage: v
|
3033
|
+
TestGlobalNewName
|
3034
|
+
sage: repr(v)
|
3035
|
+
'TestGlobalNewName'
|
3036
|
+
sage: v.__repr__()
|
3037
|
+
'TestGlobalNewName'
|
3038
|
+
"""
|
3039
|
+
return "TestGlobalNewName"
|
3040
|
+
|
3041
|
+
|
3042
|
+
register_unpickle_override('sage.misc.explain_pickle', 'TestGlobalOldName', TestGlobalNewName, call_name=('sage.misc.explain_pickle', 'TestGlobalNewName'))
|
3043
|
+
|
3044
|
+
|
3045
|
+
class TestGlobalFunnyName:
|
3046
|
+
r"""
|
3047
|
+
A featureless new-style class which has a name that's not a legal
|
3048
|
+
Python identifier.
|
3049
|
+
|
3050
|
+
EXAMPLES::
|
3051
|
+
|
3052
|
+
sage: from sage.misc.explain_pickle import *
|
3053
|
+
sage: globals()['funny$name'] = TestGlobalFunnyName # see comment at end of file
|
3054
|
+
sage: TestGlobalFunnyName.__name__
|
3055
|
+
'funny$name'
|
3056
|
+
sage: globals()['funny$name'] is TestGlobalFunnyName
|
3057
|
+
True
|
3058
|
+
"""
|
3059
|
+
def __repr__(self):
|
3060
|
+
r"""
|
3061
|
+
Print a TestGlobalFunnyName.
|
3062
|
+
|
3063
|
+
EXAMPLES::
|
3064
|
+
|
3065
|
+
sage: from sage.misc.explain_pickle import *
|
3066
|
+
sage: v = TestGlobalFunnyName()
|
3067
|
+
sage: v
|
3068
|
+
TestGlobalFunnyName
|
3069
|
+
sage: repr(v)
|
3070
|
+
'TestGlobalFunnyName'
|
3071
|
+
sage: v.__repr__()
|
3072
|
+
'TestGlobalFunnyName'
|
3073
|
+
"""
|
3074
|
+
return "TestGlobalFunnyName"
|
3075
|
+
|
3076
|
+
|
3077
|
+
TestGlobalFunnyName.__name__ = "funny$name"
|
3078
|
+
# This crashed Sphinx. Instead, we manually execute this just before the test.
|
3079
|
+
# globals()['funny$name'] = TestGlobalFunnyName
|