passagemath-objects 10.6.41__cp314-cp314t-macosx_13_0_arm64.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.

Potentially problematic release.


This version of passagemath-objects might be problematic. Click here for more details.

Files changed (280) hide show
  1. passagemath_objects/.dylibs/libgmp.10.dylib +0 -0
  2. passagemath_objects/__init__.py +3 -0
  3. passagemath_objects-10.6.41.dist-info/METADATA +115 -0
  4. passagemath_objects-10.6.41.dist-info/RECORD +280 -0
  5. passagemath_objects-10.6.41.dist-info/WHEEL +6 -0
  6. passagemath_objects-10.6.41.dist-info/top_level.txt +3 -0
  7. sage/all__sagemath_objects.py +37 -0
  8. sage/arith/all__sagemath_objects.py +5 -0
  9. sage/arith/long.pxd +411 -0
  10. sage/arith/numerical_approx.cpython-314t-darwin.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-314t-darwin.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-314t-darwin.so +0 -0
  17. sage/categories/action.pxd +29 -0
  18. sage/categories/action.pyx +641 -0
  19. sage/categories/algebra_functor.py +745 -0
  20. sage/categories/all__sagemath_objects.py +33 -0
  21. sage/categories/basic.py +62 -0
  22. sage/categories/cartesian_product.py +295 -0
  23. sage/categories/category.py +3401 -0
  24. sage/categories/category_cy_helper.cpython-314t-darwin.so +0 -0
  25. sage/categories/category_cy_helper.pxd +8 -0
  26. sage/categories/category_cy_helper.pyx +322 -0
  27. sage/categories/category_singleton.cpython-314t-darwin.so +0 -0
  28. sage/categories/category_singleton.pxd +3 -0
  29. sage/categories/category_singleton.pyx +342 -0
  30. sage/categories/category_types.py +637 -0
  31. sage/categories/category_with_axiom.py +2876 -0
  32. sage/categories/covariant_functorial_construction.py +703 -0
  33. sage/categories/facade_sets.py +228 -0
  34. sage/categories/functor.cpython-314t-darwin.so +0 -0
  35. sage/categories/functor.pxd +7 -0
  36. sage/categories/functor.pyx +691 -0
  37. sage/categories/homset.py +1338 -0
  38. sage/categories/homsets.py +364 -0
  39. sage/categories/isomorphic_objects.py +73 -0
  40. sage/categories/map.cpython-314t-darwin.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2112 -0
  43. sage/categories/morphism.cpython-314t-darwin.so +0 -0
  44. sage/categories/morphism.pxd +14 -0
  45. sage/categories/morphism.pyx +895 -0
  46. sage/categories/objects.py +167 -0
  47. sage/categories/primer.py +1696 -0
  48. sage/categories/pushout.py +4834 -0
  49. sage/categories/quotients.py +64 -0
  50. sage/categories/realizations.py +200 -0
  51. sage/categories/sets_cat.py +3228 -0
  52. sage/categories/sets_with_partial_maps.py +52 -0
  53. sage/categories/subobjects.py +64 -0
  54. sage/categories/subquotients.py +21 -0
  55. sage/categories/with_realizations.py +311 -0
  56. sage/cpython/__init__.py +19 -0
  57. sage/cpython/_py2_random.py +619 -0
  58. sage/cpython/all.py +3 -0
  59. sage/cpython/atexit.cpython-314t-darwin.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-314t-darwin.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-314t-darwin.so +0 -0
  64. sage/cpython/cython_metaclass.h +117 -0
  65. sage/cpython/cython_metaclass.pxd +3 -0
  66. sage/cpython/cython_metaclass.pyx +130 -0
  67. sage/cpython/debug.cpython-314t-darwin.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-314t-darwin.so +0 -0
  70. sage/cpython/dict_del_by_value.pxd +9 -0
  71. sage/cpython/dict_del_by_value.pyx +191 -0
  72. sage/cpython/dict_internal.h +245 -0
  73. sage/cpython/getattr.cpython-314t-darwin.so +0 -0
  74. sage/cpython/getattr.pxd +9 -0
  75. sage/cpython/getattr.pyx +439 -0
  76. sage/cpython/pycore_long.h +97 -0
  77. sage/cpython/pycore_long.pxd +10 -0
  78. sage/cpython/python_debug.h +44 -0
  79. sage/cpython/python_debug.pxd +47 -0
  80. sage/cpython/pyx_visit.h +13 -0
  81. sage/cpython/string.cpython-314t-darwin.so +0 -0
  82. sage/cpython/string.pxd +76 -0
  83. sage/cpython/string.pyx +34 -0
  84. sage/cpython/string_impl.h +60 -0
  85. sage/cpython/type.cpython-314t-darwin.so +0 -0
  86. sage/cpython/type.pxd +2 -0
  87. sage/cpython/type.pyx +40 -0
  88. sage/cpython/wrapperdescr.pxd +67 -0
  89. sage/ext/all__sagemath_objects.py +3 -0
  90. sage/ext/ccobject.h +64 -0
  91. sage/ext/cplusplus.pxd +17 -0
  92. sage/ext/mod_int.h +30 -0
  93. sage/ext/mod_int.pxd +24 -0
  94. sage/ext/stdsage.pxd +39 -0
  95. sage/groups/all__sagemath_objects.py +1 -0
  96. sage/groups/group.cpython-314t-darwin.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-314t-darwin.so +0 -0
  100. sage/groups/old.pxd +14 -0
  101. sage/groups/old.pyx +219 -0
  102. sage/libs/all__sagemath_objects.py +3 -0
  103. sage/libs/gmp/__init__.py +1 -0
  104. sage/libs/gmp/all.pxd +6 -0
  105. sage/libs/gmp/binop.pxd +23 -0
  106. sage/libs/gmp/misc.pxd +8 -0
  107. sage/libs/gmp/mpf.pxd +88 -0
  108. sage/libs/gmp/mpn.pxd +57 -0
  109. sage/libs/gmp/mpq.pxd +57 -0
  110. sage/libs/gmp/mpz.pxd +202 -0
  111. sage/libs/gmp/pylong.cpython-314t-darwin.so +0 -0
  112. sage/libs/gmp/pylong.pxd +12 -0
  113. sage/libs/gmp/pylong.pyx +150 -0
  114. sage/libs/gmp/random.pxd +25 -0
  115. sage/libs/gmp/randomize.pxd +59 -0
  116. sage/libs/gmp/types.pxd +53 -0
  117. sage/libs/gmpxx.pxd +19 -0
  118. sage/misc/abstract_method.py +276 -0
  119. sage/misc/all__sagemath_objects.py +43 -0
  120. sage/misc/bindable_class.py +253 -0
  121. sage/misc/c3_controlled.cpython-314t-darwin.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-314t-darwin.so +0 -0
  125. sage/misc/cachefunc.pxd +43 -0
  126. sage/misc/cachefunc.pyx +3781 -0
  127. sage/misc/call.py +188 -0
  128. sage/misc/classcall_metaclass.cpython-314t-darwin.so +0 -0
  129. sage/misc/classcall_metaclass.pxd +14 -0
  130. sage/misc/classcall_metaclass.pyx +599 -0
  131. sage/misc/constant_function.cpython-314t-darwin.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-314t-darwin.so +0 -0
  135. sage/misc/fast_methods.pxd +20 -0
  136. sage/misc/fast_methods.pyx +351 -0
  137. sage/misc/flatten.py +90 -0
  138. sage/misc/fpickle.cpython-314t-darwin.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-314t-darwin.so +0 -0
  141. sage/misc/function_mangling.pxd +11 -0
  142. sage/misc/function_mangling.pyx +308 -0
  143. sage/misc/inherit_comparison.cpython-314t-darwin.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-314t-darwin.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-314t-darwin.so +0 -0
  149. sage/misc/lazy_attribute.pyx +607 -0
  150. sage/misc/lazy_format.py +135 -0
  151. sage/misc/lazy_import.cpython-314t-darwin.so +0 -0
  152. sage/misc/lazy_import.pyx +1299 -0
  153. sage/misc/lazy_import_cache.py +36 -0
  154. sage/misc/lazy_list.cpython-314t-darwin.so +0 -0
  155. sage/misc/lazy_list.pxd +19 -0
  156. sage/misc/lazy_list.pyx +1187 -0
  157. sage/misc/lazy_string.cpython-314t-darwin.so +0 -0
  158. sage/misc/lazy_string.pxd +7 -0
  159. sage/misc/lazy_string.pyx +546 -0
  160. sage/misc/misc.py +1066 -0
  161. sage/misc/misc_c.cpython-314t-darwin.so +0 -0
  162. sage/misc/misc_c.pxd +3 -0
  163. sage/misc/misc_c.pyx +766 -0
  164. sage/misc/namespace_package.py +37 -0
  165. sage/misc/nested_class.cpython-314t-darwin.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-314t-darwin.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-314t-darwin.so +0 -0
  172. sage/misc/randstate.pxd +30 -0
  173. sage/misc/randstate.pyx +1059 -0
  174. sage/misc/repr.py +203 -0
  175. sage/misc/reset.cpython-314t-darwin.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-314t-darwin.so +0 -0
  178. sage/misc/sage_ostools.pyx +323 -0
  179. sage/misc/sage_timeit.py +276 -0
  180. sage/misc/sage_timeit_class.cpython-314t-darwin.so +0 -0
  181. sage/misc/sage_timeit_class.pyx +120 -0
  182. sage/misc/sage_unittest.py +637 -0
  183. sage/misc/sageinspect.py +2768 -0
  184. sage/misc/session.cpython-314t-darwin.so +0 -0
  185. sage/misc/session.pyx +392 -0
  186. sage/misc/superseded.py +557 -0
  187. sage/misc/test_nested_class.py +228 -0
  188. sage/misc/timing.py +264 -0
  189. sage/misc/unknown.py +222 -0
  190. sage/misc/verbose.py +253 -0
  191. sage/misc/weak_dict.cpython-314t-darwin.so +0 -0
  192. sage/misc/weak_dict.pxd +15 -0
  193. sage/misc/weak_dict.pyx +1231 -0
  194. sage/modules/all__sagemath_objects.py +1 -0
  195. sage/modules/module.cpython-314t-darwin.so +0 -0
  196. sage/modules/module.pxd +5 -0
  197. sage/modules/module.pyx +329 -0
  198. sage/rings/all__sagemath_objects.py +3 -0
  199. sage/rings/integer_fake.h +22 -0
  200. sage/rings/integer_fake.pxd +55 -0
  201. sage/sets/all__sagemath_objects.py +3 -0
  202. sage/sets/pythonclass.cpython-314t-darwin.so +0 -0
  203. sage/sets/pythonclass.pxd +9 -0
  204. sage/sets/pythonclass.pyx +247 -0
  205. sage/structure/__init__.py +4 -0
  206. sage/structure/all.py +30 -0
  207. sage/structure/category_object.cpython-314t-darwin.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-314t-darwin.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-314t-darwin.so +0 -0
  214. sage/structure/coerce_actions.pxd +27 -0
  215. sage/structure/coerce_actions.pyx +988 -0
  216. sage/structure/coerce_dict.cpython-314t-darwin.so +0 -0
  217. sage/structure/coerce_dict.pxd +51 -0
  218. sage/structure/coerce_dict.pyx +1557 -0
  219. sage/structure/coerce_exceptions.py +23 -0
  220. sage/structure/coerce_maps.cpython-314t-darwin.so +0 -0
  221. sage/structure/coerce_maps.pxd +28 -0
  222. sage/structure/coerce_maps.pyx +718 -0
  223. sage/structure/debug_options.cpython-314t-darwin.so +0 -0
  224. sage/structure/debug_options.pxd +6 -0
  225. sage/structure/debug_options.pyx +54 -0
  226. sage/structure/dynamic_class.py +541 -0
  227. sage/structure/element.cpython-314t-darwin.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-314t-darwin.so +0 -0
  231. sage/structure/element_wrapper.pxd +12 -0
  232. sage/structure/element_wrapper.pyx +582 -0
  233. sage/structure/factorization.py +1422 -0
  234. sage/structure/factorization_integer.py +105 -0
  235. sage/structure/factory.cpython-314t-darwin.so +0 -0
  236. sage/structure/factory.pyx +786 -0
  237. sage/structure/formal_sum.py +489 -0
  238. sage/structure/gens_py.py +73 -0
  239. sage/structure/global_options.py +1743 -0
  240. sage/structure/indexed_generators.py +863 -0
  241. sage/structure/list_clone.cpython-314t-darwin.so +0 -0
  242. sage/structure/list_clone.pxd +65 -0
  243. sage/structure/list_clone.pyx +1867 -0
  244. sage/structure/list_clone_demo.cpython-314t-darwin.so +0 -0
  245. sage/structure/list_clone_demo.pyx +248 -0
  246. sage/structure/list_clone_timings.py +179 -0
  247. sage/structure/list_clone_timings_cy.cpython-314t-darwin.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-314t-darwin.so +0 -0
  250. sage/structure/mutability.pxd +21 -0
  251. sage/structure/mutability.pyx +348 -0
  252. sage/structure/nonexact.py +69 -0
  253. sage/structure/parent.cpython-314t-darwin.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-314t-darwin.so +0 -0
  257. sage/structure/parent_base.pxd +13 -0
  258. sage/structure/parent_base.pyx +44 -0
  259. sage/structure/parent_gens.cpython-314t-darwin.so +0 -0
  260. sage/structure/parent_gens.pxd +22 -0
  261. sage/structure/parent_gens.pyx +377 -0
  262. sage/structure/parent_old.cpython-314t-darwin.so +0 -0
  263. sage/structure/parent_old.pxd +25 -0
  264. sage/structure/parent_old.pyx +294 -0
  265. sage/structure/proof/__init__.py +1 -0
  266. sage/structure/proof/all.py +243 -0
  267. sage/structure/proof/proof.py +300 -0
  268. sage/structure/richcmp.cpython-314t-darwin.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-314t-darwin.so +0 -0
  272. sage/structure/sage_object.pxd +3 -0
  273. sage/structure/sage_object.pyx +988 -0
  274. sage/structure/sage_object_test.py +19 -0
  275. sage/structure/sequence.py +937 -0
  276. sage/structure/set_factories.py +1178 -0
  277. sage/structure/set_factories_example.py +527 -0
  278. sage/structure/support_view.py +179 -0
  279. sage/structure/test_factory.py +56 -0
  280. sage/structure/unique_representation.py +1359 -0
sage/misc/persist.pyx ADDED
@@ -0,0 +1,1251 @@
1
+ # sage_setup: distribution = sagemath-objects
2
+ # cython: old_style_globals=True
3
+ # The old_style_globals directive is important for load() to work correctly.
4
+ # However, this should be removed in favor of user_globals; see
5
+ # https://github.com/sagemath/sage/issues/18083
6
+
7
+ r"""
8
+ Object persistence
9
+
10
+ You can load and save most Sage object to disk using the load and
11
+ save member functions and commands.
12
+
13
+ .. NOTE::
14
+
15
+ It is impossible to save certain Sage objects to disk. For example,
16
+ if `x` is a MAGMA object, i.e., a wrapper around an object
17
+ that is defined in MAGMA, there is no way to save `x` to
18
+ disk, since MAGMA doesn't support saving of individual objects to
19
+ disk.
20
+
21
+
22
+ - Versions: Loading and saving of objects is guaranteed to work
23
+ even if the version of Python changes. Saved objects can be loaded
24
+ in future versions of Python. However, if the data structure that
25
+ defines the object, e.g., in Sage code, changes drastically (or
26
+ changes name or disappears), then the object might not load
27
+ correctly or work correctly.
28
+
29
+ - Objects are zlib compressed for space efficiency.
30
+ """
31
+
32
+ import io
33
+ import os
34
+ import pickle
35
+ import sys
36
+
37
+ from textwrap import dedent
38
+
39
+ # change to import zlib to use zlib instead; but this
40
+ # slows down loading any data stored in the other format
41
+ import zlib
42
+ import bz2
43
+ comp = zlib
44
+ comp_other = bz2
45
+
46
+ from sage.misc.sage_unittest import TestSuite
47
+ from sage.misc.superseded import deprecation
48
+
49
+ # We define two global dictionaries `already_pickled` and
50
+ # `already_unpickled`, which are intended to help you to implement
51
+ # pickling when cyclic definitions could happen.
52
+ #
53
+ # You have the guarantee that the dictionary `already_pickled`
54
+ # (resp. `already_unpickled`) will be cleared after the complete
55
+ # pickling (resp. unpickling) process has been completed (and not
56
+ # before!).
57
+ # Apart from this, you are free to use these variables as you like.
58
+ #
59
+ # However, the standard utilisation is the following.
60
+ # The pickling method (namely ``__reduce__``) checks if the id of the
61
+ # current element appears in the dictionary `already_pickled`. If it
62
+ # does not, the methods records that this element is about to be
63
+ # pickled by adding the entry { id: True } to `already_pickled`.
64
+ # In all cases, the pickling method pickles the element and includes
65
+ # the id in the tuple of arguments that will be passed in afterwards
66
+ # to the unpickling function.
67
+ # The unpickling function then receives all the necessary information
68
+ # to reconstruct the element, together with an id.
69
+ # If this id appears as a key of the dictionary `already_unpickled`, it
70
+ # returns the corresponding value.
71
+ # Otherwise, it builds the element, adds the entry { id: element } to
72
+ # `already_unpickled` and finally returns the element.
73
+ #
74
+ # For a working example, see sage.rings.padics.lazy_template.LazyElement_unknown
75
+ already_pickled = {}
76
+ already_unpickled = {}
77
+
78
+
79
+ cdef _normalize_filename(s):
80
+ """
81
+ Append the .sobj extension to a filename if it doesn't already have it.
82
+ """
83
+ if s[-5:] != '.sobj':
84
+ return s + '.sobj'
85
+
86
+ return s
87
+
88
+
89
+ def load(*filename, compress=True, verbose=True, **kwargs):
90
+ r"""
91
+ Load Sage object from the file with name filename, which will have
92
+ an ``.sobj`` extension added if it doesn't have one. Or, if the input
93
+ is a filename ending in ``.py``, ``.pyx``, ``.sage``, ``.spyx``,
94
+ ``.f``, ``.f90`` or ``.m``, load that file into the current running
95
+ session using :func:`sage.repl.load.load`.
96
+
97
+ Loaded files are not loaded into their own namespace, i.e., this is
98
+ much more like Python's ``execfile`` than Python's ``import``.
99
+
100
+ This function also loads a ``.sobj`` file over a network by
101
+ specifying the full URL. (Setting ``verbose = False`` suppresses
102
+ the loading progress indicator.)
103
+
104
+ When a pickle created with Python 2 is unpickled in Python 3, Sage
105
+ uses the default encoding ``latin1`` to unpickle data of type :class:`str`.
106
+
107
+ Finally, if you give multiple positional input arguments, then all
108
+ of those files are loaded, or all of the objects are loaded and a
109
+ list of the corresponding loaded objects is returned.
110
+
111
+ If ``compress`` is true (the default), then the data stored in the file
112
+ are supposed to be compressed. If ``verbose`` is true (the default), then
113
+ some logging is printed when accessing remote files. Further keyword
114
+ arguments are passed to :func:`pickle.load`.
115
+
116
+ EXAMPLES::
117
+
118
+ sage: u = 'https://www.sagemath.org/files/test.sobj'
119
+ sage: s = load(u) # optional - internet
120
+ Attempting to load remote file: https://www.sagemath.org/files/test.sobj
121
+ Loading started
122
+ Loading ended
123
+ sage: s # optional - internet
124
+ 'hello SageMath'
125
+
126
+ We test loading a file or multiple files or even mixing loading files and objects::
127
+
128
+ sage: t = tmp_filename(ext='.py')
129
+ sage: with open(t, 'w') as f:
130
+ ....: _ = f.write("print('hello world')")
131
+ sage: load(t)
132
+ hello world
133
+ sage: load(t,t)
134
+ hello world
135
+ hello world
136
+ sage: t2 = tmp_filename(); save(2/3,t2)
137
+ sage: load(t,t,t2)
138
+ hello world
139
+ hello world
140
+ [None, None, 2/3]
141
+
142
+ Files with a ``.sage`` extension are preparsed. Also note that we
143
+ can access global variables::
144
+
145
+ sage: t = tmp_filename(ext='.sage')
146
+ sage: with open(t, 'w') as f:
147
+ ....: _ = f.write("a += Mod(2/3, 11)") # This evaluates to Mod(8, 11)
148
+ sage: a = -1
149
+ sage: load(t)
150
+ sage: a
151
+ 7
152
+
153
+ We can load Fortran files::
154
+
155
+ sage: code = ' subroutine hello\n print *, "Hello World!"\n end subroutine hello\n'
156
+ sage: t = tmp_filename(ext='.F')
157
+ sage: with open(t, 'w') as f:
158
+ ....: _ = f.write(code)
159
+ sage: load(t) # needs numpy
160
+ sage: hello # needs numpy
161
+ <fortran ...>
162
+ """
163
+ import sage.repl.load
164
+ if len(filename) != 1:
165
+ v = [load(file, compress=compress, verbose=verbose, **kwargs) for file in filename]
166
+ # Return v if one of the filenames refers to an object and not
167
+ # a loadable filename.
168
+ for file in filename:
169
+ if not sage.repl.load.is_loadable_filename(file):
170
+ return v
171
+ return
172
+
173
+ filename = filename[0]
174
+
175
+ if sage.repl.load.is_loadable_filename(filename):
176
+ sage.repl.load.load(filename, globals())
177
+ return
178
+
179
+ # Check if filename starts with "http://" or "https://"
180
+ lower = filename.lower()
181
+ if lower.startswith("http://") or lower.startswith("https://"):
182
+ from sage.misc.remote_file import get_remote_file
183
+ filename = get_remote_file(filename, verbose=verbose)
184
+ tmpfile_flag = True
185
+ else:
186
+ tmpfile_flag = False
187
+ filename = _normalize_filename(filename)
188
+
189
+ # Load file by absolute filename
190
+ with open(filename, 'rb') as fobj:
191
+ X = loads(fobj.read(), compress=compress, **kwargs)
192
+ try:
193
+ X._default_filename = os.path.abspath(filename)
194
+ except AttributeError:
195
+ pass
196
+
197
+ # Delete the tempfile, if it exists
198
+ if tmpfile_flag:
199
+ os.unlink(filename)
200
+
201
+ return X
202
+
203
+
204
+ def _base_save(obj, filename, compress=True):
205
+ """
206
+ Base implementation for dumping an object to a ``.sobj`` file.
207
+
208
+ This is the implementation used by :meth:`SageObject.save` and by
209
+ :func:`save` unless the object being saved has a custom ``.save()`` method,
210
+ in which case that is tried first.
211
+
212
+ Otherwise this is equivalent to :func:`_base_dumps` just with the resulting
213
+ pickle data saved to a ``.sobj`` file.
214
+ """
215
+
216
+ filename = _normalize_filename(filename)
217
+
218
+ with open(filename, 'wb') as fobj:
219
+ fobj.write(_base_dumps(obj, compress=compress))
220
+
221
+ return filename
222
+
223
+
224
+ def save(obj, filename, compress=True, **kwargs):
225
+ """
226
+ Save ``obj`` to the file with name ``filename``, which will have an
227
+ ``.sobj`` extension added if it doesn't have one and if ``obj``
228
+ doesn't have its own ``save()`` method, like e.g. Python tuples.
229
+
230
+ For image objects and the like (which have their own ``save()`` method),
231
+ you may have to specify a specific extension, e.g. ``.png``, if you
232
+ don't want the object to be saved as a Sage object (or likewise, if
233
+ ``filename`` could be interpreted as already having some extension).
234
+
235
+ .. WARNING::
236
+
237
+ This will *replace* the contents of the file if it already exists.
238
+
239
+ EXAMPLES::
240
+
241
+ sage: import tempfile
242
+ sage: d = tempfile.TemporaryDirectory()
243
+ sage: a = matrix(2, [1,2, 3,-5/2]) # needs sage.modules
244
+ sage: objfile = os.path.join(d.name, 'test.sobj')
245
+ sage: objfile_short = os.path.join(d.name, 'test')
246
+ sage: save(a, objfile) # needs sage.modules
247
+ sage: load(objfile_short) # needs sage.modules
248
+ [ 1 2]
249
+ [ 3 -5/2]
250
+
251
+ sage: # needs sage.plot sage.schemes
252
+ sage: E = EllipticCurve([-1,0])
253
+ sage: P = plot(E)
254
+ sage: save(P, objfile_short) # saves the plot to "test.sobj"
255
+ sage: save(P, filename=os.path.join(d.name, "sage.png"), xmin=-2)
256
+ sage: save(P, os.path.join(d.name, "filename.with.some.wrong.ext"))
257
+ Traceback (most recent call last):
258
+ ...
259
+ ValueError: allowed file extensions for images are
260
+ '.eps', '.pdf', '.pgf', '.png', '.ps', '.sobj', '.svg'!
261
+ sage: print(load(objfile))
262
+ Graphics object consisting of 2 graphics primitives
263
+
264
+ sage: save("A python string", os.path.join(d.name, 'test'))
265
+ sage: load(objfile)
266
+ 'A python string'
267
+ sage: load(objfile_short)
268
+ 'A python string'
269
+ sage: d.cleanup()
270
+
271
+ TESTS:
272
+
273
+ Check that :issue:`11577` is fixed::
274
+
275
+ sage: import tempfile
276
+ sage: with tempfile.NamedTemporaryFile(suffix='.bar') as f:
277
+ ....: save((1,1), f.name)
278
+ ....: load(f.name)
279
+ (1, 1)
280
+ """
281
+
282
+ if not os.path.splitext(filename)[1] or not hasattr(obj, 'save'):
283
+ filename = _normalize_filename(filename)
284
+
285
+ if filename.endswith('.sobj'):
286
+ try:
287
+ obj.save(filename=filename, compress=compress, **kwargs)
288
+ except (AttributeError, RuntimeError, TypeError):
289
+ _base_save(obj, filename, compress=compress)
290
+ else:
291
+ # Saving an object to an image file.
292
+ obj.save(filename, **kwargs)
293
+
294
+
295
+ def _base_dumps(obj, compress=True):
296
+ """
297
+ Base implementation for dumping an object to a pickle in Sage.
298
+
299
+ This is the implementation used by :meth:`SageObject.dumps` and by
300
+ :func:`dumps` unless the object being dumped has a custom ``.dumps()``
301
+ method, in which case that is tried first.
302
+ """
303
+
304
+ global already_pickled
305
+ gherkin = SagePickler.dumps(obj)
306
+ already_pickled = {}
307
+
308
+ if compress:
309
+ return comp.compress(gherkin)
310
+
311
+ return gherkin
312
+
313
+
314
+ def dumps(obj, compress=True):
315
+ """
316
+ Dump obj to a string s. To recover obj, use ``loads(s)``.
317
+
318
+ .. SEEALSO:: :func:`loads`
319
+
320
+ EXAMPLES::
321
+
322
+ sage: a = 2/3
323
+ sage: s = dumps(a)
324
+ sage: a2 = loads(s)
325
+ sage: type(a) is type(a2)
326
+ True
327
+ sage: a2
328
+ 2/3
329
+ """
330
+ global already_pickled
331
+ if make_pickle_jar:
332
+ picklejar(obj)
333
+ try:
334
+ ans = obj.dumps(compress)
335
+ except (AttributeError, RuntimeError, TypeError):
336
+ ans = _base_dumps(obj, compress=compress)
337
+ already_pickled = {}
338
+ return ans
339
+
340
+
341
+ # This is used below, and also by explain_pickle.py
342
+ unpickle_override = {}
343
+
344
+
345
+ def register_unpickle_override(module, name, callable, call_name=None):
346
+ r"""
347
+ Python pickles include the module and class name of classes.
348
+ This means that rearranging the Sage source can invalidate old
349
+ pickles. To keep the old pickles working, you can call
350
+ register_unpickle_override with an old module name and class name,
351
+ and the Python callable (function, class with __call__ method, etc.)
352
+ to use for unpickling. (If this callable is a value in some module,
353
+ you can specify the module name and class name, for the benefit of
354
+ :func:`~sage.misc.explain_pickle.explain_pickle` when called with ``in_current_sage=True``).)
355
+
356
+ EXAMPLES:
357
+
358
+ Imagine that there used to be an ``old_integer`` module and old
359
+ pickles essentially trying to do the following::
360
+
361
+ sage: unpickle_global('sage.rings.old_integer', 'OldInteger')
362
+ Traceback (most recent call last):
363
+ ...
364
+ ImportError: cannot import OldInteger from sage.rings.old_integer, call register_unpickle_override('sage.rings.old_integer', 'OldInteger', ...) to fix this
365
+
366
+ After following the advice from the error message, unpickling
367
+ works::
368
+
369
+ sage: from sage.misc.persist import register_unpickle_override
370
+ sage: register_unpickle_override('sage.rings.old_integer', 'OldInteger', Integer)
371
+ sage: unpickle_global('sage.rings.old_integer', 'OldInteger')
372
+ <... 'sage.rings.integer.Integer'>
373
+
374
+ In many cases, unpickling problems for old pickles can be resolved with a
375
+ simple call to ``register_unpickle_override``, as in the example above and
376
+ in many of the ``sage`` source files. However, if the underlying data
377
+ structure has changed significantly then unpickling may fail and it
378
+ will be necessary to explicitly implement unpickling methods for the
379
+ associated objects. The python pickle protocol is described in detail on the
380
+ web and, in particular, in the `python pickling documentation`_. For example, the
381
+ following excerpt from this documentation shows that the unpickling of
382
+ classes is controlled by their :meth:`__setstate__` method.
383
+
384
+ ::
385
+
386
+ object.__setstate__(state)
387
+
388
+ Upon unpickling, if the class also defines the method :meth:`__setstate__`, it is
389
+ called with the unpickled state. If there is no :meth:`__setstate__` method,
390
+ the pickled state must be a dictionary and its items are assigned to the new
391
+ instance's dictionary. If a class defines both :meth:`__getstate__` and
392
+ :meth:`__setstate__`, the state object needn't be a dictionary and these methods
393
+ can do what they want.
394
+
395
+ .. _python pickling documentation: https://docs.python.org/library/pickle.html#pickle-protocol
396
+
397
+ By implementing a :meth:`__setstate__` method for a class it should be
398
+ possible to fix any unpickling problems for the class. As an example of what
399
+ needs to be done, we show how to unpickle a :class:`CombinatorialObject`
400
+ object using a class which also inherits from
401
+ :class:`~sage.structure.element.Element`. This exact problem often arises
402
+ when refactoring old code into the element framework. First we create a
403
+ pickle to play with::
404
+
405
+ sage: from sage.structure.element import Element
406
+ sage: class SourPickle(CombinatorialObject): pass
407
+ sage: class SweetPickle(CombinatorialObject, Element): pass
408
+ sage: import __main__
409
+ sage: __main__.SourPickle = SourPickle
410
+ sage: __main__.SweetPickle = SweetPickle # a hack to allow us to pickle command line classes
411
+ sage: gherkin = dumps(SourPickle([1, 2, 3]))
412
+
413
+ Using :func:`register_unpickle_override` we try to sweeten our pickle, but
414
+ we are unable to eat it::
415
+
416
+ sage: from sage.misc.persist import register_unpickle_override
417
+ sage: register_unpickle_override('__main__', 'SourPickle', SweetPickle)
418
+ sage: loads(gherkin)
419
+ Traceback (most recent call last):
420
+ ...
421
+ KeyError: 0
422
+
423
+ The problem is that the ``SweetPickle`` has inherited a
424
+ :meth:`~sage.structure.element.Element.__setstate__` method from
425
+ :class:`~sage.structure.element.Element` which is not compatible with
426
+ unpickling for :class:`CombinatorialObject`. We can fix this by explicitly
427
+ defining a new :meth:`__setstate__` method::
428
+
429
+ sage: class SweeterPickle(CombinatorialObject, Element):
430
+ ....: def __setstate__(self, state):
431
+ ....: # a pickle from CombinatorialObject is just its instance
432
+ ....: # dictionary
433
+ ....: if isinstance(state, dict):
434
+ ....: # this is a fudge: we need an appropriate parent here
435
+ ....: self._set_parent(Tableaux())
436
+ ....: self.__dict__ = state
437
+ ....: else:
438
+ ....: P, D = state
439
+ ....: if P is not None:
440
+ ....: self._set_parent(P)
441
+ ....: self.__dict__ = D
442
+ sage: __main__.SweeterPickle = SweeterPickle
443
+ sage: register_unpickle_override('__main__', 'SourPickle', SweeterPickle)
444
+ sage: loads(gherkin) # needs sage.combinat
445
+ [1, 2, 3]
446
+ sage: loads(dumps(SweeterPickle([1, 2, 3]))) # check that pickles work for SweeterPickle
447
+ [1, 2, 3]
448
+
449
+ The ``state`` passed to :meth:`__setstate__` will usually be something like
450
+ the instance dictionary of the pickled object, however, with some older
451
+ classes such as :class:`CombinatorialObject` it will be a tuple. In general,
452
+ the ``state`` can be any python object. ``Sage`` provides a special tool,
453
+ :func:`~sage.misc.explain_pickle.explain_pickle`, which can help in figuring
454
+ out the contents of an old pickle. Here is a second example.
455
+
456
+ ::
457
+
458
+ sage: class A():
459
+ ....: def __init__(self, value):
460
+ ....: self.original_attribute = value
461
+ ....: def __repr__(self):
462
+ ....: return 'A(%s)' % self.original_attribute
463
+ sage: class B():
464
+ ....: def __init__(self, value):
465
+ ....: self.new_attribute = value
466
+ ....: def __setstate__(self, state):
467
+ ....: try:
468
+ ....: self.new_attribute = state['new_attribute']
469
+ ....: except KeyError: # an old pickle
470
+ ....: self.new_attribute = state['original_attribute']
471
+ ....: def __repr__(self):
472
+ ....: return 'B(%s)' % self.new_attribute
473
+ sage: import __main__
474
+ sage: # a hack to allow us to pickle command line classes
475
+ sage: __main__.A = A
476
+ sage: __main__.B = B
477
+ sage: A(10)
478
+ A(10)
479
+ sage: loads(dumps(A(10)))
480
+ A(10)
481
+ sage: sage.misc.explain_pickle.explain_pickle(dumps(A(10)))
482
+ pg_A = unpickle_global('__main__', 'A')
483
+ si = unpickle_newobj(pg_A, ())
484
+ pg_make_integer = unpickle_global('sage.rings.integer', 'make_integer')
485
+ unpickle_build(si, {'original_attribute':pg_make_integer('a')})
486
+ si
487
+ sage: from sage.misc.persist import register_unpickle_override
488
+ sage: register_unpickle_override('__main__', 'A', B)
489
+ sage: loads(dumps(A(10)))
490
+ B(10)
491
+ sage: loads(dumps(B(10)))
492
+ B(10)
493
+
494
+ Pickling for python classes and extension classes, such as cython, is
495
+ different -- again this is discussed in the `python pickling
496
+ documentation`_. For the unpickling of extension classes you need to write
497
+ a :meth:`__reduce__` method which typically returns a tuple ``(f,
498
+ args,...)`` such that ``f(*args)`` returns (a copy of) the original object.
499
+ The following code snippet is the
500
+ :meth:`~sage.rings.integer.Integer.__reduce__` method from
501
+ :class:`sage.rings.integer.Integer`.
502
+
503
+ .. code-block:: cython
504
+
505
+ def __reduce__(self):
506
+ 'Including the documentation properly causes a doc-test failure so we include it as a comment:'
507
+ #* '''
508
+ #* This is used when pickling integers.
509
+ #*
510
+ #* EXAMPLES::
511
+ #*
512
+ #* sage: n = 5
513
+ #* sage: t = n.__reduce__(); t
514
+ #* (<built-in function make_integer>, ('5',))
515
+ #* sage: t[0](*t[1])
516
+ #* 5
517
+ #* sage: loads(dumps(n)) == n
518
+ #* True
519
+ #* '''
520
+ # This single line below took me HOURS to figure out.
521
+ # It is the *trick* needed to pickle Cython extension types.
522
+ # The trick is that you must put a pure Python function
523
+ # as the first argument, and that function must return
524
+ # the result of unpickling with the argument in the second
525
+ # tuple as input. All kinds of problems happen
526
+ # if we don't do this.
527
+ return sage.rings.integer.make_integer, (self.str(32),)
528
+ """
529
+ unpickle_override[(module, name)] = (callable, call_name)
530
+
531
+
532
+ def unpickle_global(module, name):
533
+ r"""
534
+ Given a module name and a name within that module (typically a class
535
+ name), retrieve the corresponding object. This normally just looks
536
+ up the name in the module, but it can be overridden by
537
+ register_unpickle_override. This is used in the Sage unpickling
538
+ mechanism, so if the Sage source code organization changes,
539
+ register_unpickle_override can allow old pickles to continue to work.
540
+
541
+ EXAMPLES::
542
+
543
+ sage: from sage.misc.persist import unpickle_override, register_unpickle_override
544
+ sage: unpickle_global('sage.rings.integer', 'Integer')
545
+ <class 'sage.rings.integer.Integer'>
546
+
547
+ Now we horribly break the pickling system::
548
+
549
+ sage: register_unpickle_override('sage.rings.integer', 'Integer', Rational, call_name=('sage.rings.rational', 'Rational'))
550
+ sage: unpickle_global('sage.rings.integer', 'Integer')
551
+ <class 'sage.rings.rational.Rational'>
552
+
553
+ and we reach into the internals and put it back::
554
+
555
+ sage: del unpickle_override[('sage.rings.integer', 'Integer')]
556
+ sage: unpickle_global('sage.rings.integer', 'Integer')
557
+ <class 'sage.rings.integer.Integer'>
558
+
559
+ A meaningful error message with resolution instructions is displayed for
560
+ old pickles that accidentally got broken because a class or entire module
561
+ was moved or renamed::
562
+
563
+ sage: unpickle_global('sage.all', 'some_old_class')
564
+ Traceback (most recent call last):
565
+ ...
566
+ ImportError: cannot import some_old_class from sage.all, call
567
+ register_unpickle_override('sage.all', 'some_old_class', ...)
568
+ to fix this
569
+
570
+ sage: unpickle_global('sage.some_old_module', 'some_old_class')
571
+ Traceback (most recent call last):
572
+ ...
573
+ ImportError: cannot import some_old_class from sage.some_old_module, call
574
+ register_unpickle_override('sage.some_old_module', 'some_old_class', ...)
575
+ to fix this
576
+
577
+ TESTS:
578
+
579
+ Test that :func:`register_unpickle_override` calls in lazily imported modules
580
+ are respected::
581
+
582
+ sage: unpickle_global('sage.combinat.root_system.type_A', 'ambient_space') # needs sage.modules
583
+ <class 'sage.combinat.root_system.type_A.AmbientSpace'>
584
+ """
585
+ unpickler = unpickle_override.get((module, name))
586
+ if unpickler is not None:
587
+ return unpickler[0]
588
+
589
+ def error():
590
+ raise ImportError("cannot import {1} from {0}, call "
591
+ "register_unpickle_override({0!r}, {1!r}, ...) to fix this".format(module, name))
592
+
593
+ mod = sys.modules.get(module)
594
+ if mod is not None:
595
+ try:
596
+ return getattr(mod, name)
597
+ except AttributeError:
598
+ error()
599
+ try:
600
+ __import__(module)
601
+ except ImportError:
602
+ error()
603
+
604
+ # Importing the module may have run register_unpickle_override.
605
+ unpickler = unpickle_override.get((module, name))
606
+ if unpickler is not None:
607
+ return unpickler[0]
608
+
609
+ mod = sys.modules[module]
610
+ return getattr(mod, name)
611
+
612
+
613
+ class _BasePickler(pickle.Pickler):
614
+ """
615
+ Provides the Python 3 implementation for
616
+ :class:`sage.misc.persist.SagePickler`.
617
+
618
+ This is simpler than the Python 2 case since ``pickle.Pickler`` is a
619
+ modern built-in type which can be easily subclassed to provide new
620
+ functionality.
621
+
622
+ See the documentation for that class for tests and examples.
623
+ """
624
+
625
+ def __init__(self, file_obj, protocol=None, persistent_id=None, *,
626
+ fix_imports=True):
627
+ super(_BasePickler, self).__init__(file_obj, protocol,
628
+ fix_imports=fix_imports)
629
+ self._persistent_id = persistent_id
630
+
631
+ def persistent_id(self, obj):
632
+ """
633
+ Implement persistence of external objects with the
634
+ ``persistent_id`` function given at instantiation, if any.
635
+ Otherwise returns ``None`` as in the base class.
636
+
637
+ See the documentation for
638
+ :class:`sage.misc.persist.SagePickler` for more details.
639
+ """
640
+
641
+ if self._persistent_id is not None:
642
+ return self._persistent_id(obj)
643
+
644
+ return super(_BasePickler, self).persistent_id(obj)
645
+
646
+
647
+ # Since Python 3.4, protocol version 4 is the "best" protocol, so we
648
+ # use that by default on Sage
649
+ _DEFAULT_PROTOCOL_VERSION = 4
650
+
651
+
652
+ class _BaseUnpickler(pickle.Unpickler):
653
+ """
654
+ Provides the Python 3 implementation for
655
+ :class:`sage.misc.persist.SageUnpickler`.
656
+
657
+ This is simpler than the Python 2 case since ``pickle.Unpickler`` is
658
+ a modern built-in type which can be easily subclassed to provide new
659
+ functionality.
660
+
661
+ See the documentation for that class for tests and examples.
662
+ """
663
+
664
+ def __init__(self, file_obj, persistent_load=None, *, **kwargs):
665
+ kwargs.setdefault('encoding', 'latin1')
666
+ super(_BaseUnpickler, self).__init__(file_obj, **kwargs)
667
+ self._persistent_load = persistent_load
668
+
669
+ def persistent_load(self, pid):
670
+ """
671
+ Implement persistent loading with the ``persistent_load`` function
672
+ given at instantiation, if any. Otherwise raises a
673
+ ``pickle.UnpicklingError`` as in the base class.
674
+
675
+ See the documentation for :class:`sage.misc.persist.SageUnpickler`
676
+ for more details.
677
+ """
678
+
679
+ if self._persistent_load is not None:
680
+ return self._persistent_load(pid)
681
+
682
+ return super(_BaseUnpickler, self).persistent_load(pid)
683
+
684
+ def find_class(self, module, name):
685
+ """
686
+ The Unpickler uses this class to load module-level objects.
687
+ Contrary to the name, it is used for functions as well as classes.
688
+
689
+ (This is equivalent to what was previously called ``find_global``
690
+ which seems like a better name, albeit still somewhat misleading).
691
+
692
+ This is just a thin wrapper around
693
+ :func:`sage.misc.persist.unpickle_global`
694
+ """
695
+
696
+ # First try using Sage's unpickle_global to go through the unpickle
697
+ # override system
698
+ try:
699
+ return unpickle_global(module, name)
700
+ except ImportError:
701
+ # Failing that, go through the base class's find_class to give
702
+ # it a try (this is necessary in particular to support
703
+ # fix_imports)
704
+ return super(_BaseUnpickler, self).find_class(module, name)
705
+
706
+
707
+ class SagePickler(_BasePickler):
708
+ r"""
709
+ Subclass ``pickle.Pickler`` with Sage-specific default options, and
710
+ built-in support for external object persistence.
711
+
712
+ INPUT:
713
+
714
+ - ``file_obj`` -- a readable file-like object returning ``bytes`` from
715
+ which the pickle data will be loaded
716
+
717
+ - ``persistent_id`` -- callable or None; if given this callable takes a
718
+ single object to be pickled, and returns an "ID" (a key with which to
719
+ restore the object upon unpickling, which may itself be any pickleable
720
+ object). See the Python documentation on `pickling and unpickling
721
+ external objects`_ for more details.
722
+
723
+ - ``py2compat`` -- on Python 3 only, this creates pickles that have a
724
+ better chance of being read on Python 2, by using protocol version 2
725
+ (instead of 4) and fixing up imports of standard library modules and
726
+ types whose names changed between Python 2 and 3. This is enabled by
727
+ default for the best chances of cross-Python compatibility.
728
+
729
+ - Further arguments are passed to :func:`pickle.load`, where in Python-3
730
+ Sage sets the default ``encoding='latin1'``. This is essential to make
731
+ pickles readable in Python-3 that were created in Python-2. See
732
+ :issue:`28444` for details.
733
+
734
+ .. _pickling and unpickling external objects: https://docs.python.org/2.7/library/pickle.html#pickling-and-unpickling-external-objects
735
+
736
+ EXAMPLES::
737
+
738
+ sage: from sage.misc.persist import (
739
+ ....: unpickle_override, register_unpickle_override, SageUnpickler)
740
+ sage: from sage.rings.integer import make_integer
741
+ sage: from io import BytesIO
742
+ sage: def fake_constructor(x):
743
+ ....: print("unpickling an Integer")
744
+ ....: return make_integer(x)
745
+ sage: register_unpickle_override('sage.rings.integer', 'make_integer',
746
+ ....: fake_constructor)
747
+ sage: unp = SageUnpickler(BytesIO(dumps(1, compress=False)))
748
+ sage: unp.load()
749
+ unpickling an Integer
750
+ 1
751
+ sage: del unpickle_override[('sage.rings.integer', 'make_integer')]
752
+
753
+ The ``SagePickler`` can also be passed a ``persistent_id`` function::
754
+
755
+ sage: table = {1: 'a', 2: 'b'}
756
+ sage: # in practice this might be a database or something...
757
+ sage: def load_object_from_table(obj_id):
758
+ ....: tag, obj_id
759
+ ....: return table[obj_id]
760
+
761
+ TESTS:
762
+
763
+ The following is an indirect doctest.
764
+ ::
765
+
766
+ sage: class Foo():
767
+ ....: def __init__(self, s):
768
+ ....: self.bar = s
769
+ ....: def __reduce__(self):
770
+ ....: return Foo, (self.bar,)
771
+ ....:
772
+ sage: import __main__
773
+ sage: __main__.Foo = Foo
774
+
775
+ The data that is passed to ``loads`` in the following line was created
776
+ by ``dumps(Foo('\x80\x07')`` in Python-2. We demonstrate that it can
777
+ be correctly unpickled in Python-3::
778
+
779
+ sage: g = loads(b'x\x9ck`J\x8e\x8f\xcfM\xcc\xcc\x8b\x8f\xe7r\xcb\xcf\xe7*d\x0cej`/dj\r*d\xd6\x03\x00\x89\xc5\x08{')
780
+ sage: type(g), g.bar
781
+ (<class '__main__.Foo'>, '\x80\x07')
782
+
783
+ The following line demonstrates what would happen without :issue:`28444`::
784
+
785
+ sage: loads(b'x\x9ck`J\x8e\x8f\xcfM\xcc\xcc\x8b\x8f\xe7r\xcb\xcf\xe7*d\x0cej`/dj\r*d\xd6\x03\x00\x89\xc5\x08{', encoding='ASCII')
786
+ Traceback (most recent call last):
787
+ ...
788
+ UnicodeDecodeError: 'ascii' codec can...t decode byte 0x80 in position 0: ordinal not in range(128)
789
+ """
790
+
791
+ def __init__(self, file_obj, persistent_id=None, py2compat=True):
792
+ protocol = _DEFAULT_PROTOCOL_VERSION
793
+
794
+ if py2compat:
795
+ protocol = 2
796
+
797
+ super(SagePickler, self).__init__(file_obj, protocol=protocol,
798
+ persistent_id=persistent_id)
799
+
800
+ @classmethod
801
+ def dumps(cls, obj, **kwargs):
802
+ """
803
+ Equivalent to :func:`pickle.dumps` but using the
804
+ :class:`sage.misc.persist.SagePickler`.
805
+
806
+ INPUT:
807
+
808
+ - ``obj`` -- the object to pickle
809
+
810
+ - ``kwargs`` -- keyword arguments passed to the
811
+ :class:`sage.misc.persist.SagePickler` constructor
812
+
813
+ OUTPUT: ``pickle`` -- the pickled object as ``bytes``
814
+
815
+ EXAMPLES::
816
+
817
+ sage: import pickle
818
+ sage: from sage.misc.persist import SagePickler
819
+ sage: gherkin = SagePickler.dumps(1)
820
+ sage: pickle.loads(gherkin)
821
+ 1
822
+ """
823
+ global already_pickled
824
+ buf = io.BytesIO()
825
+ pickler = cls(buf, **kwargs)
826
+ pickler.dump(obj)
827
+ already_pickled = {}
828
+ return buf.getvalue()
829
+
830
+
831
+ class SageUnpickler(_BaseUnpickler):
832
+ """
833
+ Subclass ``pickle.Unpickler`` to control how certain objects get unpickled
834
+ (registered overrides, specifically).
835
+
836
+ This is only needed in Python 3 and up. On Python 2 the behavior of the
837
+ ``cPickle`` module is customized differently.
838
+
839
+ This class simply overrides ``Unpickler.find_class`` to wrap
840
+ ``sage.misc.persist.unpickle_global``.
841
+
842
+ INPUT:
843
+
844
+ - ``file_obj`` -- a readable file-like object returning ``bytes`` from
845
+ which the pickle data will be loaded
846
+
847
+ - ``persistent_load`` -- callable or None; if given this callable
848
+ implements loading of persistent external objects. The function
849
+ should take a single argument, the persistent object ID. See the
850
+ Python documentation on `pickling and unpickling external objects`_
851
+ for more details.
852
+
853
+ - ``kwargs`` -- additional keyword arguments passed to the
854
+ ``pickle.Unpickler`` constructor
855
+
856
+ .. _pickling and unpickling external objects: https://docs.python.org/2.7/library/pickle.html#pickling-and-unpickling-external-objects
857
+
858
+ EXAMPLES::
859
+
860
+ sage: from sage.misc.persist import (
861
+ ....: unpickle_override, register_unpickle_override, SageUnpickler)
862
+ sage: from sage.rings.integer import make_integer
863
+ sage: from io import BytesIO
864
+ sage: def fake_constructor(x):
865
+ ....: print("unpickling an Integer")
866
+ ....: return make_integer(x)
867
+ sage: register_unpickle_override('sage.rings.integer', 'make_integer',
868
+ ....: fake_constructor)
869
+ sage: unp = SageUnpickler(BytesIO(dumps(1, compress=False)))
870
+ sage: unp.load()
871
+ unpickling an Integer
872
+ 1
873
+ sage: del unpickle_override[('sage.rings.integer', 'make_integer')]
874
+
875
+ The ``SageUnpickler`` can also be passed a ``persistent_load`` function::
876
+
877
+ sage: table = {1: 'a', 2: 'b'}
878
+ sage: # in practice this might be a database or something...
879
+ sage: def load_object_from_table(obj_id):
880
+ ....: tag, obj_id
881
+ ....: return table[obj_id]
882
+ """
883
+
884
+ @classmethod
885
+ def loads(cls, data, **kwargs):
886
+ """
887
+ Equivalent to :func:`pickle.loads` but using the
888
+ :class:`sage.misc.persist.SagePickler`.
889
+
890
+ INPUT:
891
+
892
+ - ``data`` -- the pickle data as ``bytes``
893
+
894
+ - ``kwargs`` -- keyword arguments passed to the
895
+ :class:`sage.misc.persist.SageUnpickler` constructor
896
+
897
+ OUTPUT: ``obj`` -- the object that was serialized to the given pickle data
898
+
899
+ EXAMPLES::
900
+
901
+ sage: import pickle
902
+ sage: from sage.misc.persist import SageUnpickler
903
+ sage: gherkin = pickle.dumps(1)
904
+ sage: SageUnpickler.loads(gherkin)
905
+ 1
906
+ """
907
+
908
+ return cls(io.BytesIO(data), **kwargs).load()
909
+
910
+
911
+ def loads(s, compress=True, **kwargs):
912
+ r"""
913
+ Recover an object x that has been dumped to a string s
914
+ using ``s = dumps(x)``.
915
+
916
+ .. SEEALSO:: :func:`dumps`
917
+
918
+ EXAMPLES::
919
+
920
+ sage: a = matrix(2, [1,2, 3,-4/3]) # needs sage.modules
921
+ sage: s = dumps(a) # needs sage.modules
922
+ sage: loads(s) # needs sage.modules
923
+ [ 1 2]
924
+ [ 3 -4/3]
925
+
926
+ If compress is ``True`` (the default), it will try to decompress
927
+ the data with zlib and with bz2 (in turn); if neither succeeds,
928
+ it will assume the data is actually uncompressed. If ``compress==False``
929
+ is explicitly specified, then no decompression is attempted.
930
+ Further arguments are passed to python's :func:`pickle.load`.
931
+
932
+ ::
933
+
934
+ sage: v = [1..10]
935
+ sage: loads(dumps(v, compress=False)) == v
936
+ True
937
+ sage: loads(dumps(v, compress=False), compress=True) == v
938
+ True
939
+ sage: loads(dumps(v, compress=True), compress=False)
940
+ Traceback (most recent call last):
941
+ ...
942
+ UnpicklingError: invalid load key, 'x'.
943
+
944
+ The next example demonstrates that Sage strives to avoid data loss
945
+ in the transition from Python-2 to Python-3. The problem is that Python-3
946
+ by default would not be able to unpickle a non-ASCII Python-2 string appearing
947
+ in a pickle. See :issue:`28444` for details.
948
+ ::
949
+
950
+ sage: class Foo():
951
+ ....: def __init__(self, s):
952
+ ....: self.bar = s
953
+ ....: def __reduce__(self):
954
+ ....: return Foo, (self.bar,)
955
+ ....:
956
+ sage: import __main__
957
+ sage: __main__.Foo = Foo
958
+
959
+ The data that is passed to ``loads`` in the following line was created
960
+ by ``dumps(Foo('\x80\x07')`` in Python-2.
961
+ ::
962
+
963
+ sage: g = loads(b'x\x9ck`J\x8e\x8f\xcfM\xcc\xcc\x8b\x8f\xe7r\xcb\xcf\xe7*d\x0cej`/dj\r*d\xd6\x03\x00\x89\xc5\x08{')
964
+ sage: type(g), g.bar
965
+ (<class '__main__.Foo'>, '\x80\x07')
966
+
967
+ The following line demonstrates what would happen without :issue:`28444`::
968
+
969
+ sage: loads(b'x\x9ck`J\x8e\x8f\xcfM\xcc\xcc\x8b\x8f\xe7r\xcb\xcf\xe7*d\x0cej`/dj\r*d\xd6\x03\x00\x89\xc5\x08{', encoding='ASCII')
970
+ Traceback (most recent call last):
971
+ ...
972
+ UnicodeDecodeError: 'ascii' codec can...t decode byte 0x80 in position 0: ordinal not in range(128)
973
+ """
974
+ if not isinstance(s, bytes):
975
+ raise TypeError("s must be bytes")
976
+
977
+ if compress:
978
+ try:
979
+ s = comp.decompress(s)
980
+ except Exception as msg1:
981
+ try:
982
+ s = comp_other.decompress(s)
983
+ except Exception as msg2:
984
+ # Maybe data is uncompressed?
985
+ pass
986
+
987
+ unpickler = SageUnpickler(io.BytesIO(s), **kwargs)
988
+ global already_unpickled
989
+ ans = unpickler.load()
990
+ already_unpickled = {}
991
+ return ans
992
+
993
+
994
+ cdef bint make_pickle_jar = 'SAGE_PICKLE_JAR' in os.environ
995
+
996
+
997
+ def picklejar(obj, dir=None):
998
+ """
999
+ Create pickled sobj of ``obj`` in ``dir``, with name the absolute
1000
+ value of the hash of the pickle of obj. This is used in conjunction
1001
+ with :func:`unpickle_all`.
1002
+
1003
+ To use this to test the whole Sage library right now, set the
1004
+ environment variable ``SAGE_PICKLE_JAR``, which will make it so
1005
+ :func:`dumps` will by default call :func:`picklejar` with the
1006
+ default dir. Once you do that and doctest Sage, you'll find that
1007
+ the ``DOT_SAGE/pickle_jar`` directory contains a bunch of
1008
+ pickled objects along with corresponding txt descriptions of them.
1009
+ Use the :func:`unpickle_all` to see if they unpickle later.
1010
+
1011
+ INPUT:
1012
+
1013
+ - ``obj`` -- a pickleable object
1014
+
1015
+ - ``dir`` -- string or ``None``; if ``None`` then ``dir`` defaults to
1016
+ ``DOT_SAGE/pickle_jar``
1017
+
1018
+ EXAMPLES::
1019
+
1020
+ sage: dir = tmp_dir()
1021
+ sage: sage.misc.persist.picklejar(1, dir)
1022
+ sage: sage.misc.persist.picklejar('test', dir)
1023
+ sage: len(os.listdir(dir)) # Two entries (sobj and txt) for each object
1024
+ 4
1025
+
1026
+ TESTS:
1027
+
1028
+ Test an unaccessible directory::
1029
+
1030
+ sage: import os, sys
1031
+ sage: s = os.stat(dir)
1032
+ sage: os.chmod(dir, 0o000)
1033
+ sage: try:
1034
+ ....: uid = os.getuid()
1035
+ ....: except AttributeError:
1036
+ ....: uid = -1
1037
+ sage: if uid == 0:
1038
+ ....: print("OK (cannot test this as root)")
1039
+ ....: else:
1040
+ ....: try:
1041
+ ....: sage.misc.persist.picklejar(1, dir + '/noaccess')
1042
+ ....: except PermissionError:
1043
+ ....: print("OK (correctly raised PermissionError)")
1044
+ ....: else:
1045
+ ....: print("FAIL (did not raise an exception")
1046
+ OK...
1047
+ sage: os.chmod(dir, s.st_mode)
1048
+ """
1049
+ if dir is None:
1050
+ from sage.env import DOT_SAGE
1051
+ dir = os.path.join(DOT_SAGE, 'pickle_jar')
1052
+ try:
1053
+ os.makedirs(dir)
1054
+ except OSError as err:
1055
+ # It is not an error if the directory exists
1056
+ import errno
1057
+ if not err.errno == errno.EEXIST:
1058
+ raise
1059
+
1060
+ global already_pickled
1061
+ s = comp.compress(SagePickler.dumps(obj))
1062
+ already_pickled = {}
1063
+
1064
+ typ = str(type(obj))
1065
+ name = ''.join([x if (x.isalnum() or x == '_') else '_' for x in typ])
1066
+ base = os.path.join(dir, name)
1067
+ if os.path.exists(base):
1068
+ i = 0
1069
+ while os.path.exists(f'{base}-{i}'):
1070
+ i += 1
1071
+ base += f'-{i}'
1072
+
1073
+ with open(base + '.sobj', 'wb') as fobj:
1074
+ fobj.write(s)
1075
+
1076
+ import sage.version
1077
+ stamp = dedent("""\
1078
+ type(obj) = {typ}
1079
+ version = {ver}
1080
+ obj = {obj}
1081
+ """.format(typ=typ, ver=sage.version.version, obj=obj))
1082
+
1083
+ with open(base + '.txt', 'w') as fobj:
1084
+ fobj.write(stamp)
1085
+
1086
+
1087
+ def unpickle_all(target, debug=False, run_test_suite=False):
1088
+ """
1089
+ Unpickle all ``.sobj`` files in a directory or tar archive.
1090
+
1091
+ INPUT:
1092
+
1093
+ - ``target`` -- string; the name of a directory or of a (possibly
1094
+ compressed) tar archive that contains a single directory of
1095
+ ``.sobj`` files. The tar archive can be in any format that
1096
+ python's ``tarfile`` module understands; for example,
1097
+ ``.tar.gz`` or ``.tar.bz2``.
1098
+ - ``debug`` -- boolean (default: ``False``)
1099
+ whether to report a stacktrace in case of failure
1100
+ - ``run_test_suite`` -- boolean (default: ``False``)
1101
+ whether to run ``TestSuite(x).run()`` on the unpickled objects
1102
+
1103
+ OUTPUT:
1104
+
1105
+ Typically, two lines are printed: the first reporting the number
1106
+ of successfully unpickled files, and the second reporting the
1107
+ number (zero) of failures. If there are failures, however, then a
1108
+ list of failed files will be printed before either of those lines,
1109
+ and the failure count will of course be nonzero.
1110
+
1111
+ .. WARNING::
1112
+
1113
+ You must only pass trusted data to this function, including tar
1114
+ archives. We use the "data" filter from PEP 706 if possible
1115
+ while extracting the archive, but even that is not a perfect
1116
+ solution, and it is only available since Python 3.11.4.
1117
+
1118
+ EXAMPLES::
1119
+
1120
+ sage: dir = tmp_dir()
1121
+ sage: sage.misc.persist.picklejar('hello', dir)
1122
+ sage: sage.misc.persist.unpickle_all(dir)
1123
+ Successfully unpickled 1 objects.
1124
+ Failed to unpickle 0 objects.
1125
+ """
1126
+ import os.path
1127
+ import tarfile
1128
+
1129
+ ok_count = 0
1130
+ fail_count = 0
1131
+ failed = []
1132
+ tracebacks = []
1133
+
1134
+ if os.path.isfile(target) and tarfile.is_tarfile(target):
1135
+ import tempfile
1136
+ with tempfile.TemporaryDirectory() as T:
1137
+ # Extract the tarball to a temporary directory. The "data"
1138
+ # filter only became available in python-3.11.4. See PEP
1139
+ # 706 for background.
1140
+ with tarfile.open(target) as tf:
1141
+ if hasattr(tarfile, "data_filter"):
1142
+ tf.extractall(T, filter='data')
1143
+ else:
1144
+ tf.extractall(T)
1145
+
1146
+ # Ensure that the tarball contained exactly one thing, a
1147
+ # directory.
1148
+ bad_tarball_msg = "tar archive must contain only a single directory"
1149
+ contents = os.listdir(T)
1150
+ if len(contents) != 1:
1151
+ raise ValueError(bad_tarball_msg)
1152
+
1153
+ dir = os.path.join(T, contents[0])
1154
+ if not os.path.isdir(dir):
1155
+ raise ValueError(bad_tarball_msg)
1156
+
1157
+ # If everything looks OK, start this function over again
1158
+ # inside the extracted directory. Note: PEP 343 says the
1159
+ # temporary directory will be cleaned up even when the
1160
+ # "with" block is exited via a "return" statement. But
1161
+ # also note that "return" doesn't happen until the
1162
+ # recursive call to unpickle_all() has completed.
1163
+ return unpickle_all(dir, debug, run_test_suite)
1164
+
1165
+ if not os.path.isdir(target):
1166
+ raise ValueError("target is neither a directory nor a tar archive")
1167
+
1168
+ for A in sorted(os.listdir(target)):
1169
+ f = os.path.join(target, A)
1170
+ if os.path.isfile(f) and f.endswith('.sobj'):
1171
+ try:
1172
+ obj = load(f)
1173
+ if run_test_suite:
1174
+ TestSuite(obj).run(catch = False)
1175
+ ok_count += 1
1176
+ except Exception:
1177
+ fail_count += 1
1178
+ if run_test_suite:
1179
+ print(" * unpickle failure: TestSuite(load('%s')).run()" % f)
1180
+ else:
1181
+ print(" * unpickle failure: load('%s')" % f)
1182
+ from traceback import print_exc
1183
+ print_exc()
1184
+ failed.append(A)
1185
+ if debug:
1186
+ tracebacks.append(sys.exc_info())
1187
+
1188
+ if failed:
1189
+ print("Failed:\n%s" % ('\n'.join(failed)))
1190
+ print("Successfully unpickled %s objects." % ok_count)
1191
+ print("Failed to unpickle %s objects." % fail_count)
1192
+ if debug:
1193
+ return tracebacks
1194
+
1195
+
1196
+ def make_None(*args, **kwds):
1197
+ """
1198
+ Do nothing and return ``None``. Used for overriding pickles when
1199
+ that pickle is no longer needed.
1200
+
1201
+ EXAMPLES::
1202
+
1203
+ sage: from sage.misc.persist import make_None
1204
+ sage: print(make_None(42, pi, foo='bar')) # needs sage.symbolic
1205
+ None
1206
+ """
1207
+ return None
1208
+
1209
+
1210
+ def load_sage_object(cls, dic): # not used
1211
+ X = cls.__new__(cls)
1212
+ try:
1213
+ X.__setstate__(dic)
1214
+ except AttributeError:
1215
+ X.__dict__ = dic
1216
+ return X
1217
+
1218
+
1219
+ def load_sage_element(cls, parent, dic_pic):
1220
+ X = cls.__new__(cls)
1221
+ X._set_parent(parent)
1222
+ X.__dict__ = SageUnpickler.loads(dic_pic)
1223
+ return X
1224
+
1225
+
1226
+ def db(name):
1227
+ r"""
1228
+ Load object with given name from the Sage database. Use x.db(name)
1229
+ or db_save(x, name) to save objects to the database.
1230
+
1231
+ The database directory is ``$HOME/.sage/db``.
1232
+ """
1233
+ deprecation(39012, "Directly use pickle/unpickle instead of db/db_save.")
1234
+
1235
+ from sage.misc.misc import SAGE_DB
1236
+ return load('%s/%s' % (SAGE_DB, name))
1237
+
1238
+
1239
+ def db_save(x, name=None):
1240
+ r"""
1241
+ Save x to the Sage database.
1242
+
1243
+ The database directory is ``$HOME/.sage/db``.
1244
+ """
1245
+ deprecation(39012, "Directly use pickle/unpickle instead of db/db_save.")
1246
+
1247
+ try:
1248
+ x.db(name)
1249
+ except AttributeError:
1250
+ from sage.misc.misc import SAGE_DB
1251
+ save(x, '%s/%s' % (SAGE_DB, name))