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.
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.45.dist-info/METADATA +115 -0
- passagemath_objects-10.6.45.dist-info/RECORD +280 -0
- passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
- passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
- passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- sage/all__sagemath_objects.py +37 -0
- sage/arith/all__sagemath_objects.py +5 -0
- sage/arith/long.pxd +411 -0
- sage/arith/numerical_approx.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/action.pxd +29 -0
- sage/categories/action.pyx +641 -0
- sage/categories/algebra_functor.py +745 -0
- sage/categories/all__sagemath_objects.py +33 -0
- sage/categories/basic.py +62 -0
- sage/categories/cartesian_product.py +295 -0
- sage/categories/category.py +3401 -0
- sage/categories/category_cy_helper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_singleton.pxd +3 -0
- sage/categories/category_singleton.pyx +342 -0
- sage/categories/category_types.py +637 -0
- sage/categories/category_with_axiom.py +2876 -0
- sage/categories/covariant_functorial_construction.py +703 -0
- sage/categories/facade_sets.py +228 -0
- sage/categories/functor.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/functor.pxd +7 -0
- sage/categories/functor.pyx +691 -0
- sage/categories/homset.py +1338 -0
- sage/categories/homsets.py +364 -0
- sage/categories/isomorphic_objects.py +73 -0
- sage/categories/map.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2106 -0
- sage/categories/morphism.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/morphism.pxd +14 -0
- sage/categories/morphism.pyx +895 -0
- sage/categories/objects.py +167 -0
- sage/categories/primer.py +1696 -0
- sage/categories/pushout.py +4834 -0
- sage/categories/quotients.py +64 -0
- sage/categories/realizations.py +200 -0
- sage/categories/sets_cat.py +3290 -0
- sage/categories/sets_with_partial_maps.py +52 -0
- sage/categories/subobjects.py +64 -0
- sage/categories/subquotients.py +21 -0
- sage/categories/with_realizations.py +311 -0
- sage/cpython/__init__.py +19 -0
- sage/cpython/_py2_random.py +619 -0
- sage/cpython/all.py +3 -0
- sage/cpython/atexit.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/cython_metaclass.h +117 -0
- sage/cpython/cython_metaclass.pxd +3 -0
- sage/cpython/cython_metaclass.pyx +130 -0
- sage/cpython/debug.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/dict_del_by_value.pxd +9 -0
- sage/cpython/dict_del_by_value.pyx +191 -0
- sage/cpython/dict_internal.h +245 -0
- sage/cpython/getattr.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/getattr.pxd +9 -0
- sage/cpython/getattr.pyx +439 -0
- sage/cpython/pycore_long.h +97 -0
- sage/cpython/pycore_long.pxd +10 -0
- sage/cpython/python_debug.h +44 -0
- sage/cpython/python_debug.pxd +47 -0
- sage/cpython/pyx_visit.h +13 -0
- sage/cpython/string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/string.pxd +76 -0
- sage/cpython/string.pyx +34 -0
- sage/cpython/string_impl.h +60 -0
- sage/cpython/type.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/type.pxd +2 -0
- sage/cpython/type.pyx +40 -0
- sage/cpython/wrapperdescr.pxd +67 -0
- sage/ext/all__sagemath_objects.py +3 -0
- sage/ext/ccobject.h +64 -0
- sage/ext/cplusplus.pxd +17 -0
- sage/ext/mod_int.h +30 -0
- sage/ext/mod_int.pxd +24 -0
- sage/ext/stdsage.pxd +39 -0
- sage/groups/all__sagemath_objects.py +1 -0
- sage/groups/group.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/old.pxd +14 -0
- sage/groups/old.pyx +219 -0
- sage/libs/all__sagemath_objects.py +3 -0
- sage/libs/gmp/__init__.py +1 -0
- sage/libs/gmp/all.pxd +6 -0
- sage/libs/gmp/binop.pxd +23 -0
- sage/libs/gmp/misc.pxd +8 -0
- sage/libs/gmp/mpf.pxd +88 -0
- sage/libs/gmp/mpn.pxd +57 -0
- sage/libs/gmp/mpq.pxd +57 -0
- sage/libs/gmp/mpz.pxd +202 -0
- sage/libs/gmp/pylong.cpython-313-x86_64-linux-musl.so +0 -0
- sage/libs/gmp/pylong.pxd +12 -0
- sage/libs/gmp/pylong.pyx +150 -0
- sage/libs/gmp/random.pxd +25 -0
- sage/libs/gmp/randomize.pxd +59 -0
- sage/libs/gmp/types.pxd +53 -0
- sage/libs/gmpxx.pxd +19 -0
- sage/misc/abstract_method.py +276 -0
- sage/misc/all__sagemath_objects.py +43 -0
- sage/misc/bindable_class.py +253 -0
- sage/misc/c3_controlled.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/cachefunc.pxd +43 -0
- sage/misc/cachefunc.pyx +3781 -0
- sage/misc/call.py +188 -0
- sage/misc/classcall_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fast_methods.pxd +20 -0
- sage/misc/fast_methods.pyx +351 -0
- sage/misc/flatten.py +90 -0
- sage/misc/fpickle.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_string.pxd +7 -0
- sage/misc/lazy_string.pyx +546 -0
- sage/misc/misc.py +1066 -0
- sage/misc/misc_c.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/misc_c.pxd +3 -0
- sage/misc/misc_c.pyx +766 -0
- sage/misc/namespace_package.py +37 -0
- sage/misc/nested_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/randstate.pxd +30 -0
- sage/misc/randstate.pyx +1059 -0
- sage/misc/repr.py +203 -0
- sage/misc/reset.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +275 -0
- sage/misc/sage_timeit_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_timeit_class.pyx +120 -0
- sage/misc/sage_unittest.py +637 -0
- sage/misc/sageinspect.py +2768 -0
- sage/misc/session.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/session.pyx +392 -0
- sage/misc/superseded.py +557 -0
- sage/misc/test_nested_class.py +228 -0
- sage/misc/timing.py +264 -0
- sage/misc/unknown.py +222 -0
- sage/misc/verbose.py +253 -0
- sage/misc/weak_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/weak_dict.pxd +15 -0
- sage/misc/weak_dict.pyx +1231 -0
- sage/modules/all__sagemath_objects.py +1 -0
- sage/modules/module.cpython-313-x86_64-linux-musl.so +0 -0
- sage/modules/module.pxd +5 -0
- sage/modules/module.pyx +329 -0
- sage/rings/all__sagemath_objects.py +3 -0
- sage/rings/integer_fake.h +22 -0
- sage/rings/integer_fake.pxd +55 -0
- sage/sets/all__sagemath_objects.py +3 -0
- sage/sets/pythonclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/sets/pythonclass.pxd +9 -0
- sage/sets/pythonclass.pyx +247 -0
- sage/structure/__init__.py +4 -0
- sage/structure/all.py +30 -0
- sage/structure/category_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_dict.pxd +51 -0
- sage/structure/coerce_dict.pyx +1557 -0
- sage/structure/coerce_exceptions.py +23 -0
- sage/structure/coerce_maps.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/debug_options.pxd +6 -0
- sage/structure/debug_options.pyx +54 -0
- sage/structure/dynamic_class.py +541 -0
- sage/structure/element.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element_wrapper.pxd +12 -0
- sage/structure/element_wrapper.pyx +582 -0
- sage/structure/factorization.py +1422 -0
- sage/structure/factorization_integer.py +105 -0
- sage/structure/factory.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/factory.pyx +786 -0
- sage/structure/formal_sum.py +489 -0
- sage/structure/gens_py.py +73 -0
- sage/structure/global_options.py +1743 -0
- sage/structure/indexed_generators.py +863 -0
- sage/structure/list_clone.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_demo.pyx +248 -0
- sage/structure/list_clone_timings.py +179 -0
- sage/structure/list_clone_timings_cy.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/mutability.pxd +21 -0
- sage/structure/mutability.pyx +348 -0
- sage/structure/nonexact.py +69 -0
- sage/structure/parent.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_old.pxd +25 -0
- sage/structure/parent_old.pyx +294 -0
- sage/structure/proof/__init__.py +1 -0
- sage/structure/proof/all.py +243 -0
- sage/structure/proof/proof.py +300 -0
- sage/structure/richcmp.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/sage_object.pxd +3 -0
- sage/structure/sage_object.pyx +988 -0
- sage/structure/sage_object_test.py +19 -0
- sage/structure/sequence.py +937 -0
- sage/structure/set_factories.py +1178 -0
- sage/structure/set_factories_example.py +527 -0
- sage/structure/support_view.py +179 -0
- sage/structure/test_factory.py +56 -0
- sage/structure/unique_representation.py +1359 -0
sage/misc/weak_dict.pyx
ADDED
|
@@ -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)
|