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