passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_x86_64.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/__init__.py +3 -0
  2. passagemath_objects-10.6.45.dist-info/METADATA +115 -0
  3. passagemath_objects-10.6.45.dist-info/RECORD +280 -0
  4. passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
  5. passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
  6. passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -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-313-x86_64-linux-musl.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2106 -0
  43. sage/categories/morphism.cpython-313-x86_64-linux-musl.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 +3290 -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-313-x86_64-linux-musl.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.so +0 -0
  178. sage/misc/sage_ostools.pyx +323 -0
  179. sage/misc/sage_timeit.py +275 -0
  180. sage/misc/sage_timeit_class.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-313-x86_64-linux-musl.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
@@ -0,0 +1,1231 @@
1
+ # sage_setup: distribution = sagemath-objects
2
+ """
3
+ Fast and safe weak value dictionary
4
+
5
+ AUTHORS:
6
+
7
+ - Simon King (2013-10)
8
+ - Nils Bruin (2013-10)
9
+ - Julian Rueth (2014-03-16): improved handling of unhashable objects
10
+
11
+ Python's :mod:`weakref` module provides
12
+ :class:`~weakref.WeakValueDictionary`. This behaves similar to a dictionary,
13
+ but it does not prevent its values from garbage collection. Hence, it stores
14
+ the values by weak references with callback functions: The callback function
15
+ deletes a key-value pair from the dictionary, as soon as the value becomes
16
+ subject to garbage collection.
17
+
18
+ However, a problem arises if hash and comparison of the key depend on the
19
+ value that is being garbage collected::
20
+
21
+ sage: import weakref
22
+ sage: class Vals(): pass
23
+ sage: class Keys:
24
+ ....: def __init__(self, val):
25
+ ....: self.val = weakref.ref(val)
26
+ ....: def __hash__(self):
27
+ ....: return hash(self.val())
28
+ ....: def __eq__(self, other):
29
+ ....: return self.val() == other.val()
30
+ ....: def __ne__(self, other):
31
+ ....: return self.val() != other.val()
32
+ sage: ValList = [Vals() for _ in range(10)]
33
+ sage: D = weakref.WeakValueDictionary()
34
+ sage: for v in ValList:
35
+ ....: D[Keys(v)] = v
36
+ sage: len(D)
37
+ 10
38
+ sage: del ValList, v
39
+ sage: len(D) > 1
40
+ True
41
+
42
+ Hence, the defunct items have not been removed from the dictionary.
43
+
44
+ Therefore, Sage provides an alternative implementation
45
+ :class:`sage.misc.weak_dict.WeakValueDictionary`, using a callback that
46
+ removes the defunct item not based on hash and equality check of the key (this
47
+ is what fails in the example above), but based on comparison by identity. This
48
+ is possible, since references with callback function are distinct even if they
49
+ point to the same object. Hence, even if the same object ``O`` occurs as value
50
+ for several keys, each reference to ``O`` corresponds to a unique key. We see
51
+ no error messages, and the items get correctly removed::
52
+
53
+ sage: ValList = [Vals() for _ in range(10)]
54
+ sage: import sage.misc.weak_dict
55
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
56
+ sage: for v in ValList:
57
+ ....: D[Keys(v)] = v
58
+ sage: len(D)
59
+ 10
60
+ sage: del ValList
61
+ sage: len(D)
62
+ 1
63
+ sage: del v
64
+ sage: len(D)
65
+ 0
66
+
67
+ Another problem arises when iterating over the items of a dictionary: If
68
+ garbage collection occurs during iteration, then the content of the dictionary
69
+ changes, and the iteration breaks for :class:`weakref.WeakValueDictionary`::
70
+
71
+ sage: class Cycle:
72
+ ....: def __init__(self):
73
+ ....: self.selfref = self
74
+ sage: C = [Cycle() for n in range(10)]
75
+ sage: D = weakref.WeakValueDictionary(enumerate(C))
76
+ sage: import gc
77
+ sage: gc.disable()
78
+ sage: del C[:5]
79
+ sage: len(D)
80
+ 10
81
+
82
+ With :class:`~sage.misc.weak_dict.WeakValueDictionary`, the behaviour is
83
+ safer. Note that iteration over a WeakValueDictionary is non-deterministic,
84
+ since the lifetime of values (and hence the presence of keys) in the dictionary
85
+ may depend on when garbage collection occurs. The method implemented here
86
+ will at least postpone dictionary mutations due to garbage collection callbacks.
87
+ This means that as long as there is at least one iterator active on a dictionary,
88
+ none of its keys will be deallocated (which could have side-effects).
89
+ Which entries are returned is of course still dependent on when garbage
90
+ collection occurs. Note that when a key gets returned as "present" in the
91
+ dictionary, there is no guarantee one can actually retrieve its value: it may
92
+ have been garbage collected in the mean time.
93
+
94
+ The variant :class:`~sage.misc.weak_dict.CachedWeakValueDictionary`
95
+ additionally adds strong references to the most recently added values.
96
+ This ensures that values will not be immediately deleted after adding
97
+ them to the dictionary. This is mostly useful to implement cached
98
+ functions.
99
+
100
+ Note that Sage's weak value dictionary is actually an instance of
101
+ :class:`dict`, in contrast to :mod:`weakref`'s weak value dictionary::
102
+
103
+ sage: issubclass(weakref.WeakValueDictionary, dict)
104
+ False
105
+ sage: issubclass(sage.misc.weak_dict.WeakValueDictionary, dict)
106
+ True
107
+
108
+ See :issue:`13394` for a discussion of some of the design considerations.
109
+ """
110
+ # ****************************************************************************
111
+ # Copyright (C) 2013 Simon King <simon.king@uni-jena.de>
112
+ # Nils Bruin <nbruin@sfu.ca>
113
+ # Julian Rueth <julian.rueth@fsfe.org>
114
+ #
115
+ # This program is free software: you can redistribute it and/or modify
116
+ # it under the terms of the GNU General Public License as published by
117
+ # the Free Software Foundation, either version 2 of the License, or
118
+ # (at your option) any later version.
119
+ # https://www.gnu.org/licenses/
120
+ # ****************************************************************************
121
+
122
+ from weakref import KeyedRef
123
+ from copy import deepcopy
124
+
125
+ from cpython.dict cimport PyDict_SetItem, PyDict_Next
126
+ from cpython.tuple cimport PyTuple_GET_SIZE, PyTuple_New
127
+ from cpython.weakref cimport PyWeakref_NewRef
128
+ from cpython.ref cimport Py_INCREF
129
+ from sage.cpython.dict_del_by_value cimport *
130
+
131
+ from sage.misc.superseded import deprecation
132
+
133
+ cdef extern from "Python.h":
134
+ PyObject* Py_None
135
+ # we need this redefinition because we want to be able to call
136
+ # PyWeakref_GetObject with borrowed references. This is the recommended
137
+ # strategy according to Cython/Includes/cpython/__init__.pxd
138
+ PyObject* PyWeakref_GetObject(PyObject *ref)
139
+ int PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem) except -1
140
+
141
+
142
+ cdef class WeakValueDictEraser:
143
+ """
144
+ Erases items from a :class:`sage.misc.weak_dict.WeakValueDictionary` when
145
+ a weak reference becomes invalid.
146
+
147
+ This is of internal use only. Instances of this class will be passed as a
148
+ callback function when creating a weak reference.
149
+
150
+ EXAMPLES::
151
+
152
+ sage: from sage.misc.weak_dict import WeakValueDictionary
153
+ sage: v = frozenset([1])
154
+ sage: D = WeakValueDictionary({1 : v})
155
+ sage: len(D)
156
+ 1
157
+ sage: del v
158
+ sage: len(D)
159
+ 0
160
+
161
+ AUTHOR:
162
+
163
+ - Nils Bruin (2013-11)
164
+ """
165
+ cdef D
166
+
167
+ def __init__(self, D):
168
+ """
169
+ INPUT:
170
+
171
+ - ``D`` -- a :class:`sage.misc.weak_dict.WeakValueDictionary`
172
+
173
+ EXAMPLES::
174
+
175
+ sage: v = frozenset([1])
176
+ sage: D = sage.misc.weak_dict.WeakValueDictionary({ 1 : v })
177
+ sage: len(D)
178
+ 1
179
+ sage: del v
180
+ sage: len(D) # indirect doctest
181
+ 0
182
+ """
183
+ self.D = PyWeakref_NewRef(D, None)
184
+
185
+ def __call__(self, r):
186
+ """
187
+ INPUT:
188
+
189
+ - ``r`` -- a weak reference with key
190
+
191
+ When this is called with a weak reference ``r``, then an entry from the
192
+ dictionary pointed to by ``self.D`` is removed that has ``r`` as a value
193
+ identically, stored under a key with hash ``r.key``. If no such key
194
+ exists, or if the dictionary itself does not exist any more, then nothing
195
+ happens.
196
+
197
+ If the dictionary has an iterator active on it then the object is
198
+ queued for removal when all iterators have concluded.
199
+
200
+ EXAMPLES::
201
+
202
+ sage: v = frozenset([1])
203
+ sage: D = sage.misc.weak_dict.WeakValueDictionary({ 1 : v })
204
+ sage: len(D)
205
+ 1
206
+ sage: del v
207
+ sage: len(D) # indirect doctest
208
+ 0
209
+ """
210
+ cdef WeakValueDictionary D = <object> PyWeakref_GetObject(<PyObject*> self.D)
211
+ if D is None:
212
+ return
213
+ # The situation is the following:
214
+ # in the underlying dictionary, we have stored a KeyedRef r
215
+ # under a key k. The attribute r.key is the hash of k.
216
+ if D._guard_level:
217
+ D._pending_removals.append(r)
218
+ else:
219
+ del_dictitem_by_exact_value(<PyDictObject *>D, <PyObject *>r, r.key)
220
+
221
+
222
+ cdef class WeakValueDictionary(dict):
223
+ """
224
+ IMPLEMENTATION:
225
+
226
+ The :class:`WeakValueDictionary` inherits from :class:`dict`. In its
227
+ implementation, it stores weakrefs to the actual values under the keys.
228
+ All access routines are wrapped to transparently place and remove these
229
+ weakrefs.
230
+
231
+ NOTE:
232
+
233
+ In contrast to :class:`weakref.WeakValueDictionary` in Python's
234
+ :mod:`weakref` module, the callback does not need to assume that the
235
+ dictionary key is a valid Python object when it is called. There is no
236
+ need to compute the hash or compare the dictionary keys. This is why
237
+ the example below would not work with
238
+ :class:`weakref.WeakValueDictionary`, but does work with
239
+ :class:`sage.misc.weak_dict.WeakValueDictionary`.
240
+
241
+ EXAMPLES::
242
+
243
+ sage: import weakref
244
+ sage: class Vals(): pass
245
+ sage: class Keys:
246
+ ....: def __init__(self, val):
247
+ ....: self.val = weakref.ref(val)
248
+ ....: def __hash__(self):
249
+ ....: return hash(self.val())
250
+ ....: def __eq__(self, other):
251
+ ....: return self.val() == other.val()
252
+ ....: def __ne__(self, other):
253
+ ....: return self.val() != other.val()
254
+ sage: ValList = [Vals() for _ in range(10)]
255
+ sage: import sage.misc.weak_dict
256
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
257
+ sage: for v in ValList:
258
+ ....: D[Keys(v)] = v
259
+ sage: len(D)
260
+ 10
261
+ sage: del ValList
262
+ sage: len(D)
263
+ 1
264
+ sage: del v
265
+ sage: len(D)
266
+ 0
267
+
268
+ TESTS:
269
+
270
+ The following reflects the behaviour of the callback on weak dict values,
271
+ as discussed on :issue:`13394`. ::
272
+
273
+ sage: from sage.misc.weak_dict import WeakValueDictionary
274
+ sage: V = [set(range(n)) for n in range(5)]
275
+ sage: D = WeakValueDictionary(enumerate(V))
276
+
277
+ The line ``V[k] = None`` triggers execution of the callback functions of
278
+ the dictionary values. However, the actual deletion is postponed till
279
+ after the iteration over the dictionary has finished. Hence, when the
280
+ callbacks are executed, the values which the callback belongs to has
281
+ already been overridden by a new value. Therefore, the callback does not
282
+ delete the item::
283
+
284
+ sage: for k in D: # indirect doctest
285
+ ....: V[k] = None
286
+ ....: D[k] = ZZ
287
+ sage: len(D)
288
+ 5
289
+ sage: D[1]
290
+ Integer Ring
291
+
292
+ The following is a stress test for weak value dictionaries::
293
+
294
+ sage: class C():
295
+ ....: def __init__(self, n):
296
+ ....: self.n = n
297
+ ....: def __lt__(self, other):
298
+ ....: return self.n < other.n
299
+ ....: def __eq__(self, other):
300
+ ....: return self.n == other.n
301
+ ....: def __ne__(self, other):
302
+ ....: return self.val() != other.val()
303
+ sage: B = 100
304
+ sage: L = [None]*B
305
+ sage: D1 = WeakValueDictionary()
306
+ sage: D2 = WeakValueDictionary()
307
+ sage: for i in range(10000):
308
+ ....: ki = floor(random()*B)
309
+ ....: vi = C(floor(random()*B))
310
+ ....: D1[ki] = vi
311
+ ....: D2[ki] = vi
312
+ ....: L[ki] = vi
313
+ ....: del vi
314
+ ....: ko = floor(random()*B)
315
+ ....: if ko in D1:
316
+ ....: del D1[ko]
317
+ ....: L[ko] = None
318
+ ....: assert D1 == D2
319
+ """
320
+
321
+ def __cinit__(self):
322
+ """
323
+ EXAMPLES::
324
+
325
+ sage: from sage.misc.weak_dict import WeakValueDictionary
326
+ sage: WeakValueDictionary.__new__(WeakValueDictionary)
327
+ <WeakValueDictionary at ...>
328
+ """
329
+ self.callback = WeakValueDictEraser(self)
330
+ self._pending_removals = []
331
+
332
+ def __init__(self, data=()):
333
+ """
334
+ Create a :class:`WeakValueDictionary` with given initial data.
335
+
336
+ INPUT:
337
+
338
+ - ``data`` -- (optional) iterable of key-value pairs
339
+
340
+ EXAMPLES::
341
+
342
+ sage: # needs sage.rings.finite_rings
343
+ sage: L = [(p, GF(p)) for p in prime_range(10)]
344
+ sage: import sage.misc.weak_dict
345
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
346
+ sage: len(D)
347
+ 0
348
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(L)
349
+ sage: len(D) == len(L)
350
+ True
351
+ """
352
+ try:
353
+ data = data.items()
354
+ except AttributeError:
355
+ pass
356
+ for k, v in data:
357
+ self._set_item(k, v)
358
+
359
+ def __copy__(self):
360
+ """
361
+ Return a copy of this weak dictionary.
362
+
363
+ EXAMPLES::
364
+
365
+ sage: import sage.misc.weak_dict
366
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
367
+ sage: D[1] = QQ
368
+ sage: D[2] = ZZ
369
+ sage: D[None] = CC # needs sage.rings.real_mpfr
370
+ sage: E = copy(D) # indirect doctest
371
+ sage: set(E.items()) == set(D.items())
372
+ True
373
+ """
374
+ return WeakValueDictionary(self.items())
375
+
376
+ def __deepcopy__(self, memo):
377
+ """
378
+ Return a copy of this dictionary using copies of the keys.
379
+
380
+ .. NOTE::
381
+
382
+ The values of the dictionary are not copied, since we
383
+ cannot copy the external strong references to the values,
384
+ which are decisive for garbage collection.
385
+
386
+ EXAMPLES::
387
+
388
+ sage: class C(): pass
389
+ sage: V = [C(),C()]
390
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
391
+ sage: D[C()] = V[0]
392
+ sage: D[C()] = V[1]
393
+ sage: E = deepcopy(D) # indirect doctest
394
+
395
+ The keys are copied (in this silly example, the copies of the keys are
396
+ actually not equal to the original keys)::
397
+
398
+ sage: set(E.keys()) == set(D.keys())
399
+ False
400
+
401
+ However, the values are not copied::
402
+
403
+ sage: set(E.values()) == set(D.values()) == set(V)
404
+ True
405
+ """
406
+ out = WeakValueDictionary()
407
+ for k, v in self.items():
408
+ out[deepcopy(k, memo)] = v
409
+ return out
410
+
411
+ def __repr__(self):
412
+ """
413
+ EXAMPLES::
414
+
415
+ sage: import sage.misc.weak_dict
416
+ sage: repr(sage.misc.weak_dict.WeakValueDictionary([(1,ZZ),(2,QQ)])) # indirect doctest
417
+ '<WeakValueDictionary at 0x...>'
418
+ sage: str(sage.misc.weak_dict.WeakValueDictionary([(1,ZZ),(2,QQ)])) # indirect doctest
419
+ '<WeakValueDictionary at 0x...>'
420
+ """
421
+ return "<%s at 0x%x>" % (type(self).__name__, id(self))
422
+
423
+ def setdefault(self, k, default=None):
424
+ """
425
+ Return the stored value for a given key; return and store a default
426
+ value if no previous value is stored.
427
+
428
+ EXAMPLES::
429
+
430
+ sage: import sage.misc.weak_dict
431
+
432
+ sage: # needs sage.libs.pari
433
+ sage: L = [(p, GF(p)) for p in prime_range(10)]
434
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(L)
435
+ sage: len(D)
436
+ 4
437
+
438
+ The value for an existing key is returned and not overridden::
439
+
440
+ sage: # needs sage.libs.pari
441
+ sage: D.setdefault(5, ZZ)
442
+ Finite Field of size 5
443
+ sage: D[5]
444
+ Finite Field of size 5
445
+
446
+ For a non-existing key, the default value is stored and returned::
447
+
448
+ sage: # needs sage.libs.pari
449
+ sage: 4 in D
450
+ False
451
+ sage: D.setdefault(4, ZZ)
452
+ Integer Ring
453
+ sage: 4 in D
454
+ True
455
+ sage: D[4]
456
+ Integer Ring
457
+ sage: len(D)
458
+ 5
459
+
460
+ TESTS:
461
+
462
+ Check that :issue:`15956` has been fixed, i.e., a :exc:`TypeError` is
463
+ raised for unhashable objects::
464
+
465
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
466
+ sage: D.setdefault(matrix([]), ZZ) # needs sage.modules
467
+ Traceback (most recent call last):
468
+ ...
469
+ TypeError: ...mutable matrices are unhashable...
470
+ """
471
+ cdef PyObject* wr = PyDict_GetItemWithError(self, k)
472
+ if wr != NULL:
473
+ out = PyWeakref_GetObject(wr)
474
+ if out != Py_None:
475
+ return <object>out
476
+ self._set_item(k, default)
477
+ return default
478
+
479
+ def __setitem__(self, k, v):
480
+ """
481
+ EXAMPLES::
482
+
483
+ sage: import sage.misc.weak_dict
484
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
485
+ sage: ZZ in D
486
+ False
487
+
488
+ One can set new items::
489
+
490
+ sage: D[ZZ] = QQ # indirect doctest
491
+ sage: D[ZZ]
492
+ Rational Field
493
+ sage: len(D)
494
+ 1
495
+ sage: ZZ in D
496
+ True
497
+
498
+ One can also override existing items::
499
+
500
+ sage: D[ZZ] = RLF
501
+ sage: ZZ in D
502
+ True
503
+ sage: D[ZZ]
504
+ Real Lazy Field
505
+ sage: len(D)
506
+ 1
507
+
508
+ TESTS:
509
+
510
+ One may wonder whether it causes problems when garbage collection for
511
+ a previously existing item happens *after* overriding the item. The
512
+ example shows that it is not a problem::
513
+
514
+ sage: class Cycle:
515
+ ....: def __init__(self):
516
+ ....: self.selfref = self
517
+ sage: L = [Cycle() for _ in range(5)]
518
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
519
+ sage: len(D)
520
+ 5
521
+ sage: import gc
522
+ sage: gc.disable()
523
+ sage: del L
524
+ sage: len(D)
525
+ 5
526
+ sage: D[2] = ZZ
527
+ sage: len(D)
528
+ 5
529
+ sage: gc.enable()
530
+ sage: _ = gc.collect()
531
+ sage: len(D)
532
+ 1
533
+ sage: list(D.items())
534
+ [(2, Integer Ring)]
535
+
536
+ Check that :issue:`15956` has been fixed, i.e., a :exc:`TypeError` is
537
+ raised for unhashable objects::
538
+
539
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
540
+ sage: D[matrix([])] = ZZ # needs sage.modules
541
+ Traceback (most recent call last):
542
+ ...
543
+ TypeError: ...mutable matrices are unhashable...
544
+ """
545
+ self._set_item(k, v)
546
+
547
+ cdef int _set_item(self, k, v) except -1:
548
+ """
549
+ Common implementation for ``__setitem__`` and ``setdefault``:
550
+ add a weak reference to ``v`` under the key ``k`` in the actual
551
+ dict underlying ``self``.
552
+ """
553
+ PyDict_SetItem(self, k, KeyedRef(v, self.callback, hash(k)))
554
+
555
+ # def __delitem__(self, k):
556
+ # we do not really have to override this method.
557
+
558
+ def pop(self, k):
559
+ """
560
+ Return the value for a given key, and delete it from the dictionary.
561
+
562
+ EXAMPLES::
563
+
564
+ sage: import sage.misc.weak_dict
565
+
566
+ sage: # needs sage.libs.pari
567
+ sage: L = [GF(p) for p in prime_range(10^3)]
568
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
569
+ sage: 20 in D
570
+ True
571
+ sage: D.pop(20)
572
+ Finite Field of size 73
573
+ sage: 20 in D
574
+ False
575
+ sage: D.pop(20)
576
+ Traceback (most recent call last):
577
+ ...
578
+ KeyError: 20
579
+
580
+ TESTS:
581
+
582
+ Check that :issue:`15956` has been fixed, i.e., a :exc:`TypeError` is
583
+ raised for unhashable objects::
584
+
585
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
586
+ sage: D.pop(matrix([])) # needs sage.modules
587
+ Traceback (most recent call last):
588
+ ...
589
+ TypeError: ...mutable matrices are unhashable...
590
+ """
591
+ cdef PyObject* wr = PyDict_GetItemWithError(self, k)
592
+ if wr == NULL:
593
+ raise KeyError(k)
594
+ cdef PyObject* outref = PyWeakref_GetObject(wr)
595
+ if outref == Py_None:
596
+ raise KeyError(k)
597
+ # we turn the output into a new reference before deleting the item,
598
+ # because the deletion can cause any kind of havoc.
599
+ out = <object>outref
600
+ del self[k]
601
+ return out
602
+
603
+ def popitem(self):
604
+ """
605
+ Return and delete some item from the dictionary.
606
+
607
+ EXAMPLES::
608
+
609
+ sage: import sage.misc.weak_dict
610
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
611
+ sage: D[1] = ZZ
612
+
613
+ The dictionary only contains a single item, hence, it is clear which
614
+ one will be returned::
615
+
616
+ sage: D.popitem()
617
+ (1, Integer Ring)
618
+
619
+ Now, the dictionary is empty, and hence the next attempt to pop an
620
+ item will fail with a :exc:`KeyError`::
621
+
622
+ sage: D.popitem()
623
+ Traceback (most recent call last):
624
+ ...
625
+ KeyError: 'popitem(): weak value dictionary is empty'
626
+ """
627
+ for k, v in self.items():
628
+ del self[k]
629
+ return k, v
630
+ raise KeyError('popitem(): weak value dictionary is empty')
631
+
632
+ def get(self, k, d=None):
633
+ """
634
+ Return the stored value for a key, or a default value for unknown keys.
635
+
636
+ The default value defaults to ``None``.
637
+
638
+ EXAMPLES::
639
+
640
+ sage: import sage.misc.weak_dict
641
+
642
+ sage: # needs sage.libs.pari
643
+ sage: L = [GF(p) for p in prime_range(10^3)]
644
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
645
+ sage: 100 in D
646
+ True
647
+ sage: 200 in D
648
+ False
649
+ sage: D.get(100, "not found")
650
+ Finite Field of size 547
651
+ sage: D.get(200, "not found")
652
+ 'not found'
653
+ sage: D.get(200) is None
654
+ True
655
+
656
+ TESTS:
657
+
658
+ Check that :issue:`15956` has been fixed, i.e., a :exc:`TypeError` is
659
+ raised for unhashable objects::
660
+
661
+ sage: # needs sage.libs.pari
662
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
663
+ sage: D.get(matrix([])) # needs sage.modules
664
+ Traceback (most recent call last):
665
+ ...
666
+ TypeError: ...mutable matrices are unhashable...
667
+ """
668
+ cdef PyObject * wr = PyDict_GetItemWithError(self, k)
669
+ if wr == NULL:
670
+ return d
671
+ out = PyWeakref_GetObject(wr)
672
+ if out == Py_None:
673
+ return d
674
+ else:
675
+ return <object>out
676
+
677
+ def __getitem__(self, k):
678
+ """
679
+ TESTS::
680
+
681
+ sage: import sage.misc.weak_dict
682
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
683
+ sage: D[ZZ] = QQ
684
+ sage: D[QQ]
685
+ Traceback (most recent call last):
686
+ ...
687
+ KeyError: Rational Field
688
+ sage: D[ZZ] # indirect doctest
689
+ Rational Field
690
+
691
+ As usual, the dictionary keys are compared by ``==`` and not by
692
+ identity::
693
+
694
+ sage: D[10] = ZZ
695
+ sage: D[int(10)]
696
+ Integer Ring
697
+
698
+ Check that :issue:`15956` has been fixed, i.e., a :exc:`TypeError` is
699
+ raised for unhashable objects::
700
+
701
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
702
+ sage: D[matrix([])] # needs sage.modules
703
+ Traceback (most recent call last):
704
+ ...
705
+ TypeError: ...mutable matrices are unhashable...
706
+ """
707
+ cdef PyObject* wr = PyDict_GetItemWithError(self, k)
708
+ if wr == NULL:
709
+ raise KeyError(k)
710
+ out = PyWeakref_GetObject(wr)
711
+ if out == Py_None:
712
+ raise KeyError(k)
713
+ return <object>out
714
+
715
+ def __contains__(self, k):
716
+ """
717
+ Containment in the set of keys.
718
+
719
+ TESTS::
720
+
721
+ sage: import sage.misc.weak_dict
722
+ sage: class Vals(): pass
723
+ sage: L = [Vals() for _ in range(10)]
724
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
725
+ sage: 3 in D # indirect doctest
726
+ True
727
+
728
+ As usual, keys are compared by equality and not by identity::
729
+
730
+ sage: int(3) in D
731
+ True
732
+
733
+ This is a weak value dictionary. Hence, the existence of the
734
+ dictionary does not prevent the values from garbage collection,
735
+ thereby removing the corresponding key-value pairs::
736
+
737
+ sage: del L[3]
738
+ sage: 3 in D
739
+ False
740
+
741
+ Check that :issue:`15956` has been fixed, i.e., a :exc:`TypeError` is
742
+ raised for unhashable objects::
743
+
744
+ sage: D = sage.misc.weak_dict.WeakValueDictionary()
745
+ sage: matrix([]) in D # needs sage.modules
746
+ Traceback (most recent call last):
747
+ ...
748
+ TypeError: ...mutable matrices are unhashable...
749
+ """
750
+ cdef PyObject* wr = PyDict_GetItemWithError(self, k)
751
+ return (wr != NULL) and (PyWeakref_GetObject(wr) != Py_None)
752
+
753
+ # def __len__(self):
754
+ # since GC is not deterministic, neither is the length of a WeakValueDictionary,
755
+ # so we might as well just return the normal dictionary length.
756
+
757
+ def __iter__(self):
758
+ """
759
+ Iterate over the keys of this dictionary.
760
+
761
+ .. WARNING::
762
+
763
+ Iteration is unsafe, if the length of the dictionary changes
764
+ during the iteration! This can also happen by garbage collection.
765
+
766
+ EXAMPLES::
767
+
768
+ sage: import sage.misc.weak_dict
769
+ sage: class Vals(): pass
770
+ sage: L = [Vals() for _ in range(10)]
771
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
772
+ sage: del L[4]
773
+
774
+ One item got deleted from the list ``L`` and hence the corresponding
775
+ item in the dictionary got deleted as well. Therefore, the
776
+ corresponding key 4 is missing in the list of keys::
777
+
778
+ sage: sorted(D)
779
+ [0, 1, 2, 3, 5, 6, 7, 8, 9]
780
+ """
781
+ cdef PyObject *key
782
+ cdef PyObject *wr
783
+ cdef Py_ssize_t pos = 0
784
+ try:
785
+ self._enter_iter()
786
+ while PyDict_Next(self, &pos, &key, &wr):
787
+ # this check does not really say anything: by the time
788
+ # the key makes it to the customer, it may have already turned
789
+ # invalid. It's a cheap check, though.
790
+ if PyWeakref_GetObject(wr)!=Py_None:
791
+ yield <object>key
792
+ finally:
793
+ self._exit_iter()
794
+
795
+ def keys(self):
796
+ """
797
+ The list of keys.
798
+
799
+ EXAMPLES::
800
+
801
+ sage: import sage.misc.weak_dict
802
+ sage: class Vals(): pass
803
+ sage: L = [Vals() for _ in range(10)]
804
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
805
+ sage: del L[4]
806
+
807
+ One item got deleted from the list ``L`` and hence the corresponding
808
+ item in the dictionary got deleted as well. Therefore, the
809
+ corresponding key 4 is missing in the list of keys::
810
+
811
+ sage: sorted(D.keys())
812
+ [0, 1, 2, 3, 5, 6, 7, 8, 9]
813
+ """
814
+ return list(iter(self))
815
+
816
+ def itervalues(self):
817
+ """
818
+ Deprecated.
819
+
820
+ EXAMPLES::
821
+
822
+ sage: import sage.misc.weak_dict
823
+ sage: class Vals(): pass
824
+ sage: L = [Vals() for _ in range(10)]
825
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
826
+ sage: T = list(D.itervalues())
827
+ doctest:warning...:
828
+ DeprecationWarning: use values instead
829
+ See https://github.com/sagemath/sage/issues/34488 for details.
830
+ """
831
+ deprecation(34488, "use values instead")
832
+ return self.values()
833
+
834
+ def values(self):
835
+ """
836
+ Iterate over the values of this dictionary.
837
+
838
+ .. WARNING::
839
+
840
+ Iteration is unsafe, if the length of the dictionary changes
841
+ during the iteration! This can also happen by garbage collection.
842
+
843
+ EXAMPLES::
844
+
845
+ sage: import sage.misc.weak_dict
846
+ sage: class Vals:
847
+ ....: def __init__(self, n):
848
+ ....: self.n = n
849
+ ....: def __repr__(self):
850
+ ....: return "<%s>" % self.n
851
+ ....: def __lt__(self, other):
852
+ ....: return self.n < other.n
853
+ ....: def __eq__(self, other):
854
+ ....: return self.n == other.n
855
+ ....: def __ne__(self, other):
856
+ ....: return self.val() != other.val()
857
+ sage: L = [Vals(n) for n in range(10)]
858
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
859
+
860
+ We delete one item from ``D`` and we delete one item from the list
861
+ ``L``. The latter implies that the corresponding item from ``D`` gets
862
+ deleted as well. Hence, there remain eight values::
863
+
864
+ sage: del D[2]
865
+ sage: del L[5]
866
+ sage: for v in sorted(D.values()):
867
+ ....: print(v)
868
+ <0>
869
+ <1>
870
+ <3>
871
+ <4>
872
+ <6>
873
+ <7>
874
+ <8>
875
+ <9>
876
+ """
877
+ cdef PyObject *key
878
+ cdef PyObject *wr
879
+ cdef Py_ssize_t pos = 0
880
+ try:
881
+ self._enter_iter()
882
+ while PyDict_Next(self, &pos, &key, &wr):
883
+ out = PyWeakref_GetObject(wr)
884
+ if out != Py_None:
885
+ yield <object>out
886
+ finally:
887
+ self._exit_iter()
888
+
889
+ def values_list(self):
890
+ """
891
+ Return the list of values.
892
+
893
+ EXAMPLES::
894
+
895
+ sage: import sage.misc.weak_dict
896
+ sage: class Vals:
897
+ ....: def __init__(self, n):
898
+ ....: self.n = n
899
+ ....: def __repr__(self):
900
+ ....: return "<%s>" % self.n
901
+ ....: def __lt__(self, other):
902
+ ....: return self.n < other.n
903
+ ....: def __eq__(self, other):
904
+ ....: return self.n == other.n
905
+ ....: def __ne__(self, other):
906
+ ....: return self.val() != other.val()
907
+ sage: L = [Vals(n) for n in range(10)]
908
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
909
+
910
+ We delete one item from ``D`` and we delete one item from the list
911
+ ``L``. The latter implies that the corresponding item from ``D`` gets
912
+ deleted as well. Hence, there remain eight values::
913
+
914
+ sage: del D[2]
915
+ sage: del L[5]
916
+ sage: sorted(D.values_list())
917
+ [<0>, <1>, <3>, <4>, <6>, <7>, <8>, <9>]
918
+ """
919
+ return list(self.values())
920
+
921
+ def iteritems(self):
922
+ """
923
+ EXAMPLES::
924
+
925
+ sage: import sage.misc.weak_dict
926
+ sage: class Vals(): pass
927
+ sage: L = [Vals() for _ in range(10)]
928
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(enumerate(L))
929
+ sage: T = list(D.iteritems())
930
+ doctest:warning...:
931
+ DeprecationWarning: use items instead
932
+ See https://github.com/sagemath/sage/issues/34488 for details.
933
+ """
934
+ deprecation(34488, "use items instead")
935
+ return self.items()
936
+
937
+ def items(self):
938
+ """
939
+ Iterate over the items of this dictionary.
940
+
941
+ .. WARNING::
942
+
943
+ Iteration is unsafe, if the length of the dictionary changes
944
+ during the iteration! This can also happen by garbage collection.
945
+
946
+ EXAMPLES::
947
+
948
+ sage: import sage.misc.weak_dict
949
+ sage: class Vals:
950
+ ....: def __init__(self, n):
951
+ ....: self.n = n
952
+ ....: def __repr__(self):
953
+ ....: return "<%s>" % self.n
954
+ ....: def __lt__(self, other):
955
+ ....: return self.n < other.n
956
+ ....: def __eq__(self, other):
957
+ ....: return self.n == other.n
958
+ ....: def __ne__(self, other):
959
+ ....: return self.val() != other.val()
960
+ sage: class Keys():
961
+ ....: def __init__(self, n):
962
+ ....: self.n = n
963
+ ....: def __hash__(self):
964
+ ....: if self.n % 2:
965
+ ....: return int(5)
966
+ ....: return int(3)
967
+ ....: def __repr__(self):
968
+ ....: return "[%s]" % self.n
969
+ ....: def __lt__(self, other):
970
+ ....: return self.n < other.n
971
+ ....: def __eq__(self, other):
972
+ ....: return self.n == other.n
973
+ ....: def __ne__(self, other):
974
+ ....: return self.val() != other.val()
975
+ sage: L = [(Keys(n), Vals(n)) for n in range(10)]
976
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(L)
977
+
978
+ We remove one dictionary item directly. Another item is removed by
979
+ means of garbage collection. By consequence, there remain eight
980
+ items in the dictionary::
981
+
982
+ sage: del D[Keys(2)]
983
+ sage: del L[5]
984
+ sage: for k,v in sorted(D.items()):
985
+ ....: print("{} {}".format(k, v))
986
+ [0] <0>
987
+ [1] <1>
988
+ [3] <3>
989
+ [4] <4>
990
+ [6] <6>
991
+ [7] <7>
992
+ [8] <8>
993
+ [9] <9>
994
+ """
995
+ cdef PyObject *key
996
+ cdef PyObject *wr
997
+ cdef Py_ssize_t pos = 0
998
+ try:
999
+ self._enter_iter()
1000
+ while PyDict_Next(self, &pos, &key, &wr):
1001
+ out = PyWeakref_GetObject(wr)
1002
+ if out != Py_None:
1003
+ yield <object>key, <object>out
1004
+ finally:
1005
+ self._exit_iter()
1006
+
1007
+ def items_list(self):
1008
+ """
1009
+ The key-value pairs of this dictionary.
1010
+
1011
+ EXAMPLES::
1012
+
1013
+ sage: import sage.misc.weak_dict
1014
+ sage: class Vals:
1015
+ ....: def __init__(self, n):
1016
+ ....: self.n = n
1017
+ ....: def __repr__(self):
1018
+ ....: return "<%s>" % self.n
1019
+ ....: def __lt__(self, other):
1020
+ ....: return self.n < other.n
1021
+ ....: def __eq__(self, other):
1022
+ ....: return self.n == other.n
1023
+ ....: def __ne__(self, other):
1024
+ ....: return self.val() != other.val()
1025
+ sage: class Keys():
1026
+ ....: def __init__(self, n):
1027
+ ....: self.n = n
1028
+ ....: def __hash__(self):
1029
+ ....: if self.n % 2:
1030
+ ....: return int(5)
1031
+ ....: return int(3)
1032
+ ....: def __repr__(self):
1033
+ ....: return "[%s]" % self.n
1034
+ ....: def __lt__(self, other):
1035
+ ....: return self.n < other.n
1036
+ ....: def __eq__(self, other):
1037
+ ....: return self.n == other.n
1038
+ ....: def __ne__(self, other):
1039
+ ....: return self.val() != other.val()
1040
+ sage: L = [(Keys(n), Vals(n)) for n in range(10)]
1041
+ sage: D = sage.misc.weak_dict.WeakValueDictionary(L)
1042
+
1043
+ We remove one dictionary item directly. Another item is removed by
1044
+ means of garbage collection. By consequence, there remain eight
1045
+ items in the dictionary::
1046
+
1047
+ sage: del D[Keys(2)]
1048
+ sage: del L[5]
1049
+ sage: sorted(D.items())
1050
+ [([0], <0>),
1051
+ ([1], <1>),
1052
+ ([3], <3>),
1053
+ ([4], <4>),
1054
+ ([6], <6>),
1055
+ ([7], <7>),
1056
+ ([8], <8>),
1057
+ ([9], <9>)]
1058
+ """
1059
+ return list(self.items())
1060
+
1061
+ cdef int _enter_iter(self) except -1:
1062
+ """
1063
+ Make sure that items of a weak value dictionary are not actually
1064
+ deleted, but only *marked* for deletion.
1065
+
1066
+ TESTS::
1067
+
1068
+ sage: from sage.misc.weak_dict import WeakValueDictionary
1069
+ sage: K = [frozenset([i]) for i in range(11)]
1070
+ sage: D = WeakValueDictionary((K[i],K[i+1]) for i in range(10))
1071
+ sage: k = K[10]
1072
+ sage: del K
1073
+ sage: i = iter(D); d = next(i); del d
1074
+ sage: len(D.keys())
1075
+ 10
1076
+ sage: del k
1077
+ sage: len(D.keys())
1078
+ 9
1079
+ sage: del i
1080
+ sage: len(D.keys())
1081
+ 0
1082
+ """
1083
+ self._guard_level += 1
1084
+ return 0
1085
+
1086
+ cdef int _exit_iter(self) except -1:
1087
+ """
1088
+ Make sure that all items of a weak value dictionary that are marked
1089
+ for deletion are actually deleted, as soon as there is no iteration
1090
+ over the dictionary.
1091
+
1092
+ TESTS::
1093
+
1094
+ sage: from sage.misc.weak_dict import WeakValueDictionary
1095
+ sage: K = [frozenset([i]) for i in range(11)]
1096
+ sage: D = WeakValueDictionary((K[i],K[i+1]) for i in range(10))
1097
+ sage: k = K[10]
1098
+ sage: del K
1099
+ sage: i = iter(D); d = next(i); del d
1100
+ sage: len(D.keys())
1101
+ 10
1102
+ sage: del k
1103
+ sage: len(D.keys())
1104
+ 9
1105
+ sage: del i
1106
+ sage: len(D.keys())
1107
+ 0
1108
+ """
1109
+ self._guard_level -= 1
1110
+ # when the guard_level drops to zero, we try to remove all the
1111
+ # pending removals. Note that this could trigger another iterator
1112
+ # to become active, in which case we should back off.
1113
+ while (not self._guard_level) and self._pending_removals:
1114
+ self.callback(self._pending_removals.pop())
1115
+ return 0
1116
+
1117
+
1118
+ cdef class CachedWeakValueDictionary(WeakValueDictionary):
1119
+ """
1120
+ This class extends :class:`WeakValueDictionary` with a strong cache
1121
+ to the most recently added values. It is meant to solve the case
1122
+ where significant performance losses can occur if a value is deleted
1123
+ too early, but where keeping a value alive too long does not hurt
1124
+ much. This is typically the case with cached functions.
1125
+
1126
+ EXAMPLES:
1127
+
1128
+ We illustrate the difference between :class:`WeakValueDictionary`
1129
+ and :class:`CachedWeakValueDictionary`. An item is removed from a
1130
+ :class:`WeakValueDictionary` as soon as there are no references to
1131
+ it::
1132
+
1133
+ sage: from sage.misc.weak_dict import WeakValueDictionary
1134
+ sage: D = WeakValueDictionary()
1135
+ sage: class Test(): pass
1136
+ sage: tmp = Test()
1137
+ sage: D[0] = tmp
1138
+ sage: 0 in D
1139
+ True
1140
+ sage: del tmp
1141
+ sage: 0 in D
1142
+ False
1143
+
1144
+ So, if you have a cached function repeatedly creating the same
1145
+ temporary object and deleting it (in a helper function called from
1146
+ a loop for example), this caching will not help at all. With
1147
+ :class:`CachedWeakValueDictionary`, the most recently added values
1148
+ are not deleted. After adding enough new values, the item is removed
1149
+ anyway::
1150
+
1151
+ sage: from sage.misc.weak_dict import CachedWeakValueDictionary
1152
+ sage: D = CachedWeakValueDictionary(cache=4)
1153
+ sage: class Test(): pass
1154
+ sage: tmp = Test()
1155
+ sage: D[0] = tmp
1156
+ sage: 0 in D
1157
+ True
1158
+ sage: del tmp
1159
+ sage: 0 in D
1160
+ True
1161
+ sage: for i in range(5):
1162
+ ....: D[1] = Test()
1163
+ ....: print(0 in D)
1164
+ True
1165
+ True
1166
+ True
1167
+ False
1168
+ False
1169
+ """
1170
+
1171
+ def __cinit__(self):
1172
+ """
1173
+ EXAMPLES::
1174
+
1175
+ sage: from sage.misc.weak_dict import CachedWeakValueDictionary
1176
+ sage: CachedWeakValueDictionary.__new__(CachedWeakValueDictionary)
1177
+ <CachedWeakValueDictionary at ...>
1178
+ """
1179
+ self.cache = ()
1180
+
1181
+ def __init__(self, data=(), cache=16):
1182
+ """
1183
+ Create a :class:`CachedWeakValueDictionary` with given initial
1184
+ data and strong cache size.
1185
+
1186
+ INPUT:
1187
+
1188
+ - ``data`` -- (optional) iterable of key-value pairs
1189
+
1190
+ - ``cache`` -- (default: 16) number of values with strong
1191
+ references
1192
+
1193
+ EXAMPLES::
1194
+
1195
+ sage: L = [(p, GF(p)) for p in prime_range(10)] # needs sage.libs.pari
1196
+ sage: from sage.misc.weak_dict import CachedWeakValueDictionary
1197
+ sage: D = CachedWeakValueDictionary()
1198
+ sage: len(D)
1199
+ 0
1200
+ sage: D = CachedWeakValueDictionary(L) # needs sage.libs.pari
1201
+ sage: len(D) == len(L) # needs sage.libs.pari
1202
+ True
1203
+
1204
+ A :class:`CachedWeakValueDictionary` with a cache size of zero
1205
+ works exactly the same as an ordinary
1206
+ :class:`WeakValueDictionary`::
1207
+
1208
+ sage: D = CachedWeakValueDictionary(cache=0)
1209
+ sage: class Test(): pass
1210
+ sage: tmp = Test()
1211
+ sage: D[0] = tmp
1212
+ sage: del tmp
1213
+ sage: 0 in D
1214
+ False
1215
+ """
1216
+ super().__init__(data)
1217
+ self.cache = PyTuple_New(cache)
1218
+
1219
+ cdef int _set_item(self, k, v) except -1:
1220
+ """
1221
+ Add the item to the dict with caching.
1222
+ """
1223
+ cdef Py_ssize_t N = PyTuple_GET_SIZE(self.cache)
1224
+ if N:
1225
+ if self.cache_index + 1 < N:
1226
+ self.cache_index += 1
1227
+ else:
1228
+ self.cache_index = 0
1229
+ PyTuple_SetItem(<PyObject*>self.cache, self.cache_index, <PyObject*>v)
1230
+ Py_INCREF(v)
1231
+ WeakValueDictionary._set_item(self, k, v)