passagemath-objects 10.6.41__cp314-cp314t-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-objects might be problematic. Click here for more details.
- passagemath_objects/.dylibs/libgmp.10.dylib +0 -0
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.41.dist-info/METADATA +115 -0
- passagemath_objects-10.6.41.dist-info/RECORD +280 -0
- passagemath_objects-10.6.41.dist-info/WHEEL +6 -0
- passagemath_objects-10.6.41.dist-info/top_level.txt +3 -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-314t-darwin.so +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cpython-314t-darwin.so +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cpython-314t-darwin.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-314t-darwin.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-314t-darwin.so +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2112 -0
- sage/categories/morphism.cpython-314t-darwin.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 +3228 -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-314t-darwin.so +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cpython-314t-darwin.so +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cpython-314t-darwin.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-314t-darwin.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-314t-darwin.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-314t-darwin.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-314t-darwin.so +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cpython-314t-darwin.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-314t-darwin.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-314t-darwin.so +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cpython-314t-darwin.so +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cpython-314t-darwin.so +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cpython-314t-darwin.so +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cpython-314t-darwin.so +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cpython-314t-darwin.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-314t-darwin.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-314t-darwin.so +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cpython-314t-darwin.so +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cpython-314t-darwin.so +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +276 -0
- sage/misc/sage_timeit_class.cpython-314t-darwin.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-314t-darwin.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-314t-darwin.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-314t-darwin.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-314t-darwin.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-314t-darwin.so +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cpython-314t-darwin.so +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cpython-314t-darwin.so +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cpython-314t-darwin.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-314t-darwin.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-314t-darwin.so +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cpython-314t-darwin.so +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cpython-314t-darwin.so +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cpython-314t-darwin.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-314t-darwin.so +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cpython-314t-darwin.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
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
r"""
|
|
3
|
+
Factory for cached representations
|
|
4
|
+
|
|
5
|
+
.. SEEALSO::
|
|
6
|
+
|
|
7
|
+
:mod:`sage.structure.unique_representation`
|
|
8
|
+
|
|
9
|
+
Using a :class:`UniqueFactory` is one way of implementing a *cached
|
|
10
|
+
representation behaviour*. In spite of its name, using a
|
|
11
|
+
:class:`UniqueFactory` is not enough to ensure the *unique representation
|
|
12
|
+
behaviour*. See :mod:`~sage.structure.unique_representation` for a
|
|
13
|
+
detailed explanation.
|
|
14
|
+
|
|
15
|
+
With a :class:`UniqueFactory`, one can preprocess the given arguments. There
|
|
16
|
+
is special support for specifying a subset of the arguments that serve as the
|
|
17
|
+
unique key, so that still *all* given arguments are used to create a new
|
|
18
|
+
instance, but only the specified subset is used to look up in the
|
|
19
|
+
cache. Typically, this is used to construct objects that accept an optional
|
|
20
|
+
``check=[True|False]`` argument, but whose result should be unique
|
|
21
|
+
regardless of said optional argument. (This use case should be handled with
|
|
22
|
+
care, though: Any checking which isn't done in the ``create_key`` or
|
|
23
|
+
``create_key_and_extra_args`` method will be done only when a new object is
|
|
24
|
+
generated, but not when a cached object is retrieved from cache.
|
|
25
|
+
Consequently, if the factory is once called with ``check=False``, a
|
|
26
|
+
subsequent call with ``check=True`` cannot be expected to perform all checks
|
|
27
|
+
unless these checks are all in the ``create_key`` or
|
|
28
|
+
``create_key_and_extra_args`` method.)
|
|
29
|
+
|
|
30
|
+
For a class derived from
|
|
31
|
+
:class:`~sage.structure.unique_representation.CachedRepresentation`, argument
|
|
32
|
+
preprocessing can be obtained by providing a custom static ``__classcall__``
|
|
33
|
+
or ``__classcall_private__`` method, but this seems less transparent. When
|
|
34
|
+
argument preprocessing is not needed or the preprocess is not very
|
|
35
|
+
sophisticated, then generally
|
|
36
|
+
:class:`~sage.structure.unique_representation.CachedRepresentation` is much
|
|
37
|
+
easier to use than a factory.
|
|
38
|
+
|
|
39
|
+
AUTHORS:
|
|
40
|
+
|
|
41
|
+
- Robert Bradshaw (2008): initial version.
|
|
42
|
+
- Simon King (2013): extended documentation.
|
|
43
|
+
- Julian Rueth (2014-05-09): use ``_cache_key`` if parameters are unhashable
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
#*****************************************************************************
|
|
47
|
+
# Copyright (C) 2008 Robert Bradshaw <robertwb@math.washington.edu>
|
|
48
|
+
# 2014 Julian Rueth <julian.rueth@fsfe.org>
|
|
49
|
+
#
|
|
50
|
+
# This program is free software: you can redistribute it and/or modify
|
|
51
|
+
# it under the terms of the GNU General Public License as published by
|
|
52
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
53
|
+
# (at your option) any later version.
|
|
54
|
+
# http://www.gnu.org/licenses/
|
|
55
|
+
#*****************************************************************************
|
|
56
|
+
|
|
57
|
+
import types
|
|
58
|
+
|
|
59
|
+
from sage.structure.sage_object cimport SageObject
|
|
60
|
+
|
|
61
|
+
cdef sage_version = None
|
|
62
|
+
from sage.cpython.string cimport bytes_to_str
|
|
63
|
+
|
|
64
|
+
cimport sage.misc.weak_dict
|
|
65
|
+
from sage.misc.cachefunc cimport cache_key as _cache_key
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
cdef class UniqueFactory(SageObject):
|
|
69
|
+
"""
|
|
70
|
+
This class is intended to make it easy to cache objects.
|
|
71
|
+
|
|
72
|
+
It is based on the idea that the object is uniquely defined by a set of
|
|
73
|
+
defining data (the key). There is also the possibility of some
|
|
74
|
+
non-defining data (extra args) which will be used in initial creation,
|
|
75
|
+
but not affect the caching.
|
|
76
|
+
|
|
77
|
+
.. WARNING::
|
|
78
|
+
|
|
79
|
+
This class only provides *cached representation behaviour*. Hence,
|
|
80
|
+
using :class:`UniqueFactory`, it is still possible to create distinct
|
|
81
|
+
objects that evaluate equal. Unique representation behaviour can be
|
|
82
|
+
added, for example, by additionally inheriting from
|
|
83
|
+
:class:`sage.misc.fast_methods.WithEqualityById`.
|
|
84
|
+
|
|
85
|
+
The objects created are cached (using weakrefs) based on their key and
|
|
86
|
+
returned directly rather than re-created if requested again. Pickling is
|
|
87
|
+
taken care of by the factory, and will return the same object for the same
|
|
88
|
+
version of Sage, and distinct (but hopefully equal) objects for different
|
|
89
|
+
versions of Sage.
|
|
90
|
+
|
|
91
|
+
.. WARNING::
|
|
92
|
+
|
|
93
|
+
The objects returned by a :class:`UniqueFactory` must be instances of
|
|
94
|
+
new style classes (hence, they must be instances of :class:`object`)
|
|
95
|
+
that must not only allow a weak reference, but must accept general
|
|
96
|
+
attribute assignment. Otherwise, pickling won't work.
|
|
97
|
+
|
|
98
|
+
USAGE:
|
|
99
|
+
|
|
100
|
+
A *unique factory* provides a way to create objects from parameters
|
|
101
|
+
(the type of these objects can depend on the parameters, and is often
|
|
102
|
+
determined only at runtime) and to cache them by a certain key
|
|
103
|
+
derived from these parameters, so that when the factory is being
|
|
104
|
+
called again with the same parameters (or just with parameters which
|
|
105
|
+
yield the same key), the object is being returned from cache rather
|
|
106
|
+
than constructed anew.
|
|
107
|
+
|
|
108
|
+
An implementation of a unique factory consists of a factory class and
|
|
109
|
+
an instance of this factory class.
|
|
110
|
+
|
|
111
|
+
The factory class has to be a class inheriting from ``UniqueFactory``.
|
|
112
|
+
Typically it only needs to implement :meth:`create_key` (a method that
|
|
113
|
+
creates a key from the given parameters, under which key the object
|
|
114
|
+
will be stored in the cache) and :meth:`create_object` (a method that
|
|
115
|
+
returns the actual object from the key). Sometimes, one would also
|
|
116
|
+
implement :meth:`create_key_and_extra_args` (this differs from
|
|
117
|
+
:meth:`create_key` in allowing to also create some additional
|
|
118
|
+
arguments from the given parameters, which arguments then get passed
|
|
119
|
+
to :meth:`create_object` and thus can have an effect on the initial
|
|
120
|
+
creation of the object, but do *not* affect the key) or
|
|
121
|
+
:meth:`other_keys`. Other methods are not supposed to be overloaded.
|
|
122
|
+
|
|
123
|
+
The factory class itself cannot be called to create objects. Instead,
|
|
124
|
+
an instance of the factory class has to be created first. For
|
|
125
|
+
technical reasons, this instance has to be provided with a name that
|
|
126
|
+
allows Sage to find its definition. Specifically, the name of the
|
|
127
|
+
factory instance (or the full path to it, if it is not in the global
|
|
128
|
+
namespace) has to be passed to the factory class as a string variable.
|
|
129
|
+
So, if our factory class has been called ``A`` and is located in
|
|
130
|
+
``sage/spam/battletoads.py``, then we need to define an instance (say,
|
|
131
|
+
``B``) of ``A`` by writing ``B = A("sage.spam.battletoads.B")``
|
|
132
|
+
(or ``B = A("B")`` if this ``B`` will be imported into global
|
|
133
|
+
namespace). This instance can then be used to create objects (by
|
|
134
|
+
calling ``B(*parameters)``).
|
|
135
|
+
|
|
136
|
+
Notice that the objects created by the factory don't inherit from the
|
|
137
|
+
factory class. They *do* know about the factory that created them (this
|
|
138
|
+
information, along with the keys under which this factory caches them,
|
|
139
|
+
is stored in the ``_factory_data`` attributes of the objects), but not
|
|
140
|
+
via inheritance.
|
|
141
|
+
|
|
142
|
+
EXAMPLES:
|
|
143
|
+
|
|
144
|
+
The below examples are rather artificial and illustrate particular
|
|
145
|
+
aspects. For a "real-life" usage case of ``UniqueFactory``, see
|
|
146
|
+
the finite field factory in :mod:`sage.rings.finite_rings.finite_field_constructor`.
|
|
147
|
+
|
|
148
|
+
In many cases, a factory class is implemented by providing the two
|
|
149
|
+
methods :meth:`create_key` and :meth:`create_object`. In our example,
|
|
150
|
+
we want to demonstrate how to use "extra arguments" to choose a specific
|
|
151
|
+
implementation, with preference given to an instance found in the cache,
|
|
152
|
+
even if its implementation is different. Hence, we implement
|
|
153
|
+
:meth:`create_key_and_extra_args` rather than :meth:`create_key`, putting
|
|
154
|
+
the chosen implementation into the extra arguments. Then, in the
|
|
155
|
+
:meth:`create_object` method, we create and return instances of the
|
|
156
|
+
specified implementation.
|
|
157
|
+
::
|
|
158
|
+
|
|
159
|
+
sage: from sage.structure.factory import UniqueFactory
|
|
160
|
+
sage: class MyFactory(UniqueFactory):
|
|
161
|
+
....: def create_key_and_extra_args(self, *args, **kwds):
|
|
162
|
+
....: return args, {'impl':kwds.get('impl', None)}
|
|
163
|
+
....: def create_object(self, version, key, **extra_args):
|
|
164
|
+
....: impl = extra_args['impl']
|
|
165
|
+
....: if impl == 'C':
|
|
166
|
+
....: return C(*key)
|
|
167
|
+
....: if impl == 'D':
|
|
168
|
+
....: return D(*key)
|
|
169
|
+
....: return E(*key)
|
|
170
|
+
....:
|
|
171
|
+
|
|
172
|
+
Now we can create a factory instance. It is supposed to be found under the
|
|
173
|
+
name ``'F'`` in the ``__main__`` module. Note that in an interactive
|
|
174
|
+
session, ``F`` would automatically be in the ``__main__`` module. Hence,
|
|
175
|
+
the second and third of the following four lines are only needed in
|
|
176
|
+
doctests. ::
|
|
177
|
+
|
|
178
|
+
sage: F = MyFactory("__main__.F")
|
|
179
|
+
sage: import __main__
|
|
180
|
+
sage: __main__.F = F
|
|
181
|
+
sage: loads(dumps(F)) is F
|
|
182
|
+
True
|
|
183
|
+
|
|
184
|
+
Now we create three classes ``C``, ``D`` and ``E``. The first is a Cython
|
|
185
|
+
extension-type class that does not allow weak references nor attribute
|
|
186
|
+
assignment. The second is a Python class that is not derived from
|
|
187
|
+
:class:`object`. The third allows attribute assignment and is derived
|
|
188
|
+
from :class:`object`. ::
|
|
189
|
+
|
|
190
|
+
sage: cython("cdef class C: pass") # needs sage.misc.cython
|
|
191
|
+
sage: class D:
|
|
192
|
+
....: def __init__(self, *args):
|
|
193
|
+
....: self.t = args
|
|
194
|
+
....: def __repr__(self):
|
|
195
|
+
....: return "D%s"%repr(self.t)
|
|
196
|
+
....:
|
|
197
|
+
sage: class E(D, object): pass
|
|
198
|
+
|
|
199
|
+
Again, being in a doctest, we need to put the class ``D`` into the
|
|
200
|
+
``__main__`` module, so that Python can find it::
|
|
201
|
+
|
|
202
|
+
sage: import __main__
|
|
203
|
+
sage: __main__.D = D
|
|
204
|
+
|
|
205
|
+
It is impossible to create an instance of ``C`` with our factory, since it
|
|
206
|
+
does not allow weak references::
|
|
207
|
+
|
|
208
|
+
sage: F(1, impl='C') # needs sage.misc.cython
|
|
209
|
+
Traceback (most recent call last):
|
|
210
|
+
...
|
|
211
|
+
TypeError: cannot create weak reference to '....C' object
|
|
212
|
+
|
|
213
|
+
Let us try again, with a Cython class that does allow weak
|
|
214
|
+
references. Now, creation of an instance using the factory works::
|
|
215
|
+
|
|
216
|
+
sage: cython( # needs sage.misc.cython
|
|
217
|
+
....: '''
|
|
218
|
+
....: cdef class C:
|
|
219
|
+
....: cdef __weakref__
|
|
220
|
+
....: ''')
|
|
221
|
+
....:
|
|
222
|
+
sage: c = F(1, impl='C') # needs sage.misc.cython
|
|
223
|
+
sage: isinstance(c, C) # needs sage.misc.cython
|
|
224
|
+
True
|
|
225
|
+
|
|
226
|
+
The cache is used when calling the factory again---even if it is suggested
|
|
227
|
+
to use a different implementation. This is because the implementation is
|
|
228
|
+
only considered an "extra argument" that does not count for the key.
|
|
229
|
+
::
|
|
230
|
+
|
|
231
|
+
sage: c is F(1, impl='C') is F(1, impl='D') is F(1) # needs sage.misc.cython
|
|
232
|
+
True
|
|
233
|
+
|
|
234
|
+
However, pickling and unpickling does not use the cache. This is because
|
|
235
|
+
the factory has tried to assign an attribute to the instance that provides
|
|
236
|
+
information on the key used to create the instance, but failed::
|
|
237
|
+
|
|
238
|
+
sage: loads(dumps(c)) is c # needs sage.misc.cython
|
|
239
|
+
False
|
|
240
|
+
sage: hasattr(c, '_factory_data') # needs sage.misc.cython
|
|
241
|
+
False
|
|
242
|
+
|
|
243
|
+
We have already seen that our factory will only take the requested
|
|
244
|
+
implementation into account if the arguments used as key have not been
|
|
245
|
+
used yet. So, we use other arguments to create an instance of class
|
|
246
|
+
``D``::
|
|
247
|
+
|
|
248
|
+
sage: d = F(2, impl='D')
|
|
249
|
+
sage: isinstance(d, D)
|
|
250
|
+
True
|
|
251
|
+
|
|
252
|
+
The factory only knows about the pickling protocol used by new style
|
|
253
|
+
classes. Hence, again, pickling and unpickling fails to use the cache,
|
|
254
|
+
even though the "factory data" are now available (this is not the case
|
|
255
|
+
on Python 3 which *only* has new style classes)::
|
|
256
|
+
|
|
257
|
+
sage: loads(dumps(d)) is d
|
|
258
|
+
True
|
|
259
|
+
sage: d._factory_data
|
|
260
|
+
(<__main__.MyFactory object at ...>,
|
|
261
|
+
(...),
|
|
262
|
+
(2,),
|
|
263
|
+
{'impl': 'D'})
|
|
264
|
+
|
|
265
|
+
Only when we have a new style class that can be weak referenced and allows
|
|
266
|
+
for attribute assignment, everything works::
|
|
267
|
+
|
|
268
|
+
sage: e = F(3)
|
|
269
|
+
sage: isinstance(e, E)
|
|
270
|
+
True
|
|
271
|
+
sage: loads(dumps(e)) is e
|
|
272
|
+
True
|
|
273
|
+
sage: e._factory_data
|
|
274
|
+
(<__main__.MyFactory object at ...>,
|
|
275
|
+
(...),
|
|
276
|
+
(3,),
|
|
277
|
+
{'impl': None})
|
|
278
|
+
"""
|
|
279
|
+
cdef readonly _name
|
|
280
|
+
cdef readonly _cache
|
|
281
|
+
|
|
282
|
+
def __init__(self, name):
|
|
283
|
+
"""
|
|
284
|
+
INPUT:
|
|
285
|
+
|
|
286
|
+
- ``name`` -- string; a name in the global namespace referring to
|
|
287
|
+
``self`` or a fully qualified path name to ``self``, which is used to
|
|
288
|
+
locate the factory on unpickling
|
|
289
|
+
|
|
290
|
+
EXAMPLES::
|
|
291
|
+
|
|
292
|
+
sage: from sage.structure.factory import UniqueFactory
|
|
293
|
+
sage: fake_factory = UniqueFactory('ZZ')
|
|
294
|
+
sage: loads(dumps(fake_factory))
|
|
295
|
+
Integer Ring
|
|
296
|
+
sage: fake_factory = UniqueFactory('sage.rings.rational_field.QQ')
|
|
297
|
+
sage: loads(dumps(fake_factory))
|
|
298
|
+
Rational Field
|
|
299
|
+
"""
|
|
300
|
+
self._name = name
|
|
301
|
+
self._cache = sage.misc.weak_dict.WeakValueDictionary()
|
|
302
|
+
|
|
303
|
+
def __reduce__(self):
|
|
304
|
+
"""
|
|
305
|
+
EXAMPLES::
|
|
306
|
+
|
|
307
|
+
sage: A = FiniteField(127)
|
|
308
|
+
sage: A is loads(dumps(A)) # indirect doctest
|
|
309
|
+
True
|
|
310
|
+
|
|
311
|
+
sage: # needs sage.rings.finite_rings
|
|
312
|
+
sage: B = FiniteField(3^3,'b')
|
|
313
|
+
sage: B is loads(dumps(B))
|
|
314
|
+
True
|
|
315
|
+
sage: C = FiniteField(2^16,'c')
|
|
316
|
+
sage: C is loads(dumps(C))
|
|
317
|
+
True
|
|
318
|
+
sage: D = FiniteField(3^20,'d')
|
|
319
|
+
sage: D is loads(dumps(D))
|
|
320
|
+
True
|
|
321
|
+
|
|
322
|
+
TESTS::
|
|
323
|
+
|
|
324
|
+
sage: loads(dumps(FiniteField)) is FiniteField
|
|
325
|
+
True
|
|
326
|
+
sage: from sage.structure.test_factory import test_factory
|
|
327
|
+
sage: loads(dumps(test_factory)) is test_factory
|
|
328
|
+
True
|
|
329
|
+
"""
|
|
330
|
+
return lookup_global, (self._name,)
|
|
331
|
+
|
|
332
|
+
def __call__(self, *args, **kwds):
|
|
333
|
+
"""
|
|
334
|
+
This is the method invoked to create objects. It first creates a key
|
|
335
|
+
from the given parameters, then if an object with that key already
|
|
336
|
+
exists returns it, and otherwise creates one and stores a weak reference
|
|
337
|
+
to it in its dictionary.
|
|
338
|
+
|
|
339
|
+
Do not override this method. Instead, override ``create_key`` and
|
|
340
|
+
``create_object`` and put the docstring in the body of the class.
|
|
341
|
+
|
|
342
|
+
EXAMPLES::
|
|
343
|
+
|
|
344
|
+
sage: from sage.structure.test_factory import test_factory
|
|
345
|
+
sage: _ = test_factory(1,2,3); _
|
|
346
|
+
Making object (1, 2, 3)
|
|
347
|
+
<sage.structure.test_factory.A object at ...>
|
|
348
|
+
|
|
349
|
+
It already created one, so don't re-create::
|
|
350
|
+
|
|
351
|
+
sage: test_factory(1,2,3)
|
|
352
|
+
<sage.structure.test_factory.A object at ...>
|
|
353
|
+
sage: test_factory(1,2,3) is test_factory(1,2,3)
|
|
354
|
+
True
|
|
355
|
+
|
|
356
|
+
Of course, with a different key, a new object will be created::
|
|
357
|
+
|
|
358
|
+
sage: test_factory(1,2,3) is test_factory(1,2,4)
|
|
359
|
+
Making object (1, 2, 4)
|
|
360
|
+
False
|
|
361
|
+
"""
|
|
362
|
+
key, kwds = self.create_key_and_extra_args(*args, **kwds)
|
|
363
|
+
version = self.get_version(sage_version)
|
|
364
|
+
return self.get_object(version, key, kwds)
|
|
365
|
+
|
|
366
|
+
cpdef get_object(self, version, key, extra_args):
|
|
367
|
+
"""
|
|
368
|
+
Return the object corresponding to ``key``, creating it with
|
|
369
|
+
``extra_args`` if necessary (for example, it isn't in the cache
|
|
370
|
+
or it is unpickling from an older version of Sage).
|
|
371
|
+
|
|
372
|
+
EXAMPLES::
|
|
373
|
+
|
|
374
|
+
sage: from sage.structure.test_factory import test_factory
|
|
375
|
+
sage: a = test_factory.get_object(3.0, 'a', {}); a
|
|
376
|
+
Making object a
|
|
377
|
+
<sage.structure.test_factory.A object at ...>
|
|
378
|
+
sage: test_factory.get_object(3.0, 'a', {}) is test_factory.get_object(3.0, 'a', {})
|
|
379
|
+
True
|
|
380
|
+
sage: test_factory.get_object(3.0, 'a', {}) is test_factory.get_object(3.1, 'a', {})
|
|
381
|
+
Making object a
|
|
382
|
+
False
|
|
383
|
+
sage: test_factory.get_object(3.0, 'a', {}) is test_factory.get_object(3.0, 'b', {})
|
|
384
|
+
Making object b
|
|
385
|
+
False
|
|
386
|
+
|
|
387
|
+
TESTS:
|
|
388
|
+
|
|
389
|
+
Check that :issue:`16317` has been fixed, i.e., caching works for
|
|
390
|
+
unhashable objects::
|
|
391
|
+
|
|
392
|
+
sage: K.<u> = Qq(4) # needs sage.rings.padics
|
|
393
|
+
sage: d = test_factory.get_object(3.0, (K(1), 'c'), {}) # needs sage.rings.padics
|
|
394
|
+
Making object (1 + O(2^20), 'c')
|
|
395
|
+
sage: d is test_factory.get_object(3.0, (K(1), 'c'), {}) # needs sage.rings.padics
|
|
396
|
+
True
|
|
397
|
+
"""
|
|
398
|
+
cache_key = key
|
|
399
|
+
try:
|
|
400
|
+
try:
|
|
401
|
+
return self._cache[version, cache_key]
|
|
402
|
+
except TypeError: # key is unhashable
|
|
403
|
+
cache_key = _cache_key(cache_key)
|
|
404
|
+
return self._cache[version, cache_key]
|
|
405
|
+
except KeyError:
|
|
406
|
+
pass
|
|
407
|
+
obj = self.create_object(version, key, **extra_args)
|
|
408
|
+
self._cache[version, cache_key] = obj
|
|
409
|
+
try:
|
|
410
|
+
for key in self.other_keys(key, obj):
|
|
411
|
+
try:
|
|
412
|
+
self._cache[version, key] = obj
|
|
413
|
+
except TypeError: # key is unhashable
|
|
414
|
+
self._cache[version, _cache_key(key)] = obj
|
|
415
|
+
obj._factory_data = self, version, key, extra_args
|
|
416
|
+
|
|
417
|
+
# Install a custom __reduce__ method on the instance "obj"
|
|
418
|
+
# that we just created. We only do this if the class of
|
|
419
|
+
# "obj" has a generic __reduce__ method, which is either
|
|
420
|
+
# object.__reduce__ or __reduce_cython__, the
|
|
421
|
+
# auto-generated pickling function for Cython.
|
|
422
|
+
f = obj.__class__.__reduce__
|
|
423
|
+
if f.__objclass__ is object or f.__name__ == "__reduce_cython__":
|
|
424
|
+
obj.__reduce_ex__ = types.MethodType(generic_factory_reduce, obj)
|
|
425
|
+
except AttributeError:
|
|
426
|
+
pass
|
|
427
|
+
return obj
|
|
428
|
+
|
|
429
|
+
cpdef get_version(self, sage_version):
|
|
430
|
+
"""
|
|
431
|
+
This is provided to allow more or less granular control over
|
|
432
|
+
pickle versioning. Objects pickled in the same version of Sage
|
|
433
|
+
will unpickle to the same rather than simply equal objects. This
|
|
434
|
+
can provide significant gains as arithmetic must be performed on
|
|
435
|
+
objects with identical parents. However, if there has been an
|
|
436
|
+
incompatible change (e.g. in element representation) we want the
|
|
437
|
+
version number to change so coercion is forced between the two
|
|
438
|
+
parents.
|
|
439
|
+
|
|
440
|
+
Defaults to the Sage version that is passed in, but coarser
|
|
441
|
+
granularity can be provided.
|
|
442
|
+
|
|
443
|
+
EXAMPLES::
|
|
444
|
+
|
|
445
|
+
sage: from sage.structure.test_factory import test_factory
|
|
446
|
+
sage: test_factory.get_version((3,1,0))
|
|
447
|
+
(3, 1, 0)
|
|
448
|
+
"""
|
|
449
|
+
global sage_version
|
|
450
|
+
if sage_version is None:
|
|
451
|
+
from sage.version import version as sage_version
|
|
452
|
+
sage_version = sage_version.split('.')
|
|
453
|
+
for i in range(len(sage_version)):
|
|
454
|
+
try:
|
|
455
|
+
sage_version[i] = int(sage_version[i])
|
|
456
|
+
except ValueError:
|
|
457
|
+
pass
|
|
458
|
+
sage_version = tuple(sage_version)
|
|
459
|
+
return sage_version
|
|
460
|
+
|
|
461
|
+
def create_key_and_extra_args(self, *args, **kwds):
|
|
462
|
+
r"""
|
|
463
|
+
Return a tuple containing the key (uniquely defining data)
|
|
464
|
+
and any extra arguments (empty by default).
|
|
465
|
+
|
|
466
|
+
Defaults to :meth:`create_key`.
|
|
467
|
+
|
|
468
|
+
EXAMPLES::
|
|
469
|
+
|
|
470
|
+
sage: from sage.structure.test_factory import test_factory
|
|
471
|
+
sage: test_factory.create_key_and_extra_args(1, 2, key=5)
|
|
472
|
+
((1, 2), {})
|
|
473
|
+
sage: GF.create_key_and_extra_args(3)
|
|
474
|
+
((3, ('x',), None, 'modn', 3, 1, True, None, None, None, True, False), {})
|
|
475
|
+
"""
|
|
476
|
+
return self.create_key(*args, **kwds), {}
|
|
477
|
+
|
|
478
|
+
def create_key(self, *args, **kwds):
|
|
479
|
+
"""
|
|
480
|
+
Given the parameters (arguments and keywords), create a key
|
|
481
|
+
that uniquely determines this object.
|
|
482
|
+
|
|
483
|
+
EXAMPLES::
|
|
484
|
+
|
|
485
|
+
sage: from sage.structure.test_factory import test_factory
|
|
486
|
+
sage: test_factory.create_key(1, 2, key=5)
|
|
487
|
+
(1, 2)
|
|
488
|
+
"""
|
|
489
|
+
raise NotImplementedError
|
|
490
|
+
|
|
491
|
+
def create_object(self, version, key, **extra_args):
|
|
492
|
+
"""
|
|
493
|
+
Create the object from the key and extra arguments. This is only
|
|
494
|
+
called if the object was not found in the cache.
|
|
495
|
+
|
|
496
|
+
EXAMPLES::
|
|
497
|
+
|
|
498
|
+
sage: from sage.structure.test_factory import test_factory
|
|
499
|
+
sage: test_factory.create_object(0, (1,2,3))
|
|
500
|
+
Making object (1, 2, 3)
|
|
501
|
+
<sage.structure.test_factory.A object at ...>
|
|
502
|
+
sage: test_factory('a')
|
|
503
|
+
Making object ('a',)
|
|
504
|
+
<sage.structure.test_factory.A object at ...>
|
|
505
|
+
sage: test_factory('a') # NOT called again
|
|
506
|
+
<sage.structure.test_factory.A object at ...>
|
|
507
|
+
"""
|
|
508
|
+
raise NotImplementedError
|
|
509
|
+
|
|
510
|
+
cpdef other_keys(self, key, obj):
|
|
511
|
+
"""
|
|
512
|
+
Sometimes during object creation, certain defaults are chosen which
|
|
513
|
+
may result in a new (more specific) key. This allows the more specific
|
|
514
|
+
key to be regarded as equivalent to the original key returned by
|
|
515
|
+
:meth:`create_key` for the purpose of lookup in the cache, and is used
|
|
516
|
+
for pickling.
|
|
517
|
+
|
|
518
|
+
EXAMPLES:
|
|
519
|
+
|
|
520
|
+
The ``GF`` factory used to have a custom :meth:`other_keys`
|
|
521
|
+
method, but this was removed in :issue:`16934`::
|
|
522
|
+
|
|
523
|
+
sage: # needs sage.libs.linbox sage.rings.finite_rings
|
|
524
|
+
sage: key, _ = GF.create_key_and_extra_args(27, 'k'); key
|
|
525
|
+
(27, ('k',), x^3 + 2*x + 1, 'givaro', 3, 3, True, None, 'poly', True, True, True)
|
|
526
|
+
sage: K = GF.create_object(0, key); K
|
|
527
|
+
Finite Field in k of size 3^3
|
|
528
|
+
sage: GF.other_keys(key, K)
|
|
529
|
+
[]
|
|
530
|
+
|
|
531
|
+
sage: K = GF(7^40, 'a') # needs sage.rings.finite_rings
|
|
532
|
+
sage: loads(dumps(K)) is K # needs sage.rings.finite_rings
|
|
533
|
+
True
|
|
534
|
+
"""
|
|
535
|
+
return []
|
|
536
|
+
|
|
537
|
+
cpdef reduce_data(self, obj):
|
|
538
|
+
"""
|
|
539
|
+
The results of this function can be returned from
|
|
540
|
+
:meth:`__reduce__`. This is here so the factory internals can
|
|
541
|
+
change without having to re-write :meth:`__reduce__` methods
|
|
542
|
+
that use it.
|
|
543
|
+
|
|
544
|
+
EXAMPLES::
|
|
545
|
+
|
|
546
|
+
sage: # needs sage.modules
|
|
547
|
+
sage: from sage.modules.free_module import FreeModuleFactory_with_standard_basis as F
|
|
548
|
+
sage: V = F(ZZ, 5)
|
|
549
|
+
sage: factory, data = F.reduce_data(V)
|
|
550
|
+
sage: factory(*data)
|
|
551
|
+
Ambient free module of rank 5 over the principal ideal domain Integer Ring
|
|
552
|
+
sage: factory(*data) is V
|
|
553
|
+
True
|
|
554
|
+
|
|
555
|
+
sage: from sage.structure.test_factory import test_factory
|
|
556
|
+
sage: a = test_factory(1, 2)
|
|
557
|
+
Making object (1, 2)
|
|
558
|
+
sage: test_factory.reduce_data(a)
|
|
559
|
+
(<built-in function generic_factory_unpickle>,
|
|
560
|
+
(<sage.structure.test_factory.UniqueFactoryTester object at ...>,
|
|
561
|
+
(...),
|
|
562
|
+
(1, 2),
|
|
563
|
+
{}))
|
|
564
|
+
|
|
565
|
+
Note that the ellipsis ``(...)`` here stands for the Sage
|
|
566
|
+
version.
|
|
567
|
+
"""
|
|
568
|
+
return generic_factory_unpickle, obj._factory_data
|
|
569
|
+
|
|
570
|
+
# This is used to handle old UniqueFactory pickles
|
|
571
|
+
factory_unpickles = {}
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def register_factory_unpickle(name, callable):
|
|
575
|
+
"""
|
|
576
|
+
Register a callable to handle the unpickling from an old
|
|
577
|
+
:class:`UniqueFactory` object.
|
|
578
|
+
|
|
579
|
+
:class:`UniqueFactory` pickles use a global name through
|
|
580
|
+
:func:`generic_factory_unpickle()`, so the usual
|
|
581
|
+
:func:`~sage.misc.persist.register_unpickle_override()`
|
|
582
|
+
cannot be used here.
|
|
583
|
+
|
|
584
|
+
.. SEEALSO::
|
|
585
|
+
|
|
586
|
+
:func:`generic_factory_unpickle()`
|
|
587
|
+
|
|
588
|
+
TESTS:
|
|
589
|
+
|
|
590
|
+
This is similar to the example given in
|
|
591
|
+
:func:`generic_factory_unpickle()`, but here we will use a function to
|
|
592
|
+
explicitly return a polynomial ring.
|
|
593
|
+
|
|
594
|
+
First, we create the factory. In a doctest, it is needed to explicitly put
|
|
595
|
+
it into ``__main__``, so that it can be located when pickling. Also, it is
|
|
596
|
+
needed that we work with a new-style class::
|
|
597
|
+
|
|
598
|
+
sage: from sage.structure.factory import UniqueFactory, register_factory_unpickle
|
|
599
|
+
sage: import __main__
|
|
600
|
+
sage: class OldStuff():
|
|
601
|
+
....: def __init__(self, n, **extras):
|
|
602
|
+
....: self.n = n
|
|
603
|
+
....: def __repr__(self):
|
|
604
|
+
....: return "Rotten old thing of level {}".format(self.n)
|
|
605
|
+
sage: __main__.OldStuff = OldStuff
|
|
606
|
+
sage: class MyFactory(UniqueFactory):
|
|
607
|
+
....: def create_object(self, version, key, **extras):
|
|
608
|
+
....: return OldStuff(key[0])
|
|
609
|
+
....: def create_key(self, *args):
|
|
610
|
+
....: return args
|
|
611
|
+
sage: G = MyFactory('__main__.G')
|
|
612
|
+
sage: __main__.G = G
|
|
613
|
+
sage: a = G(3); a
|
|
614
|
+
Rotten old thing of level 3
|
|
615
|
+
sage: loads(dumps(a)) is a
|
|
616
|
+
True
|
|
617
|
+
|
|
618
|
+
Now, we create a pickle (the string returned by ``dumps(a)``)::
|
|
619
|
+
|
|
620
|
+
sage: s = dumps(a)
|
|
621
|
+
|
|
622
|
+
We create the function which will handle the unpickling::
|
|
623
|
+
|
|
624
|
+
sage: def foo(n, **kwds):
|
|
625
|
+
....: return PolynomialRing(QQ, n, 'x')
|
|
626
|
+
sage: register_factory_unpickle('__main__.G', foo)
|
|
627
|
+
|
|
628
|
+
The old pickle correctly unpickles as an explicit polynomial ring::
|
|
629
|
+
|
|
630
|
+
sage: loads(s)
|
|
631
|
+
Multivariate Polynomial Ring in x0, x1, x2 over Rational Field
|
|
632
|
+
"""
|
|
633
|
+
#global factory_unpickles
|
|
634
|
+
factory_unpickles[name] = callable
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def generic_factory_unpickle(factory, *args):
|
|
638
|
+
"""
|
|
639
|
+
Method used for unpickling the object.
|
|
640
|
+
|
|
641
|
+
The unpickling mechanism needs a plain Python function to call.
|
|
642
|
+
It takes a factory as the first argument, passes the rest of the
|
|
643
|
+
arguments onto the factory's :meth:`UniqueFactory.get_object` method.
|
|
644
|
+
|
|
645
|
+
EXAMPLES::
|
|
646
|
+
|
|
647
|
+
sage: # needs sage.modules
|
|
648
|
+
sage: from sage.modules.free_module import FreeModuleFactory_with_standard_basis as F
|
|
649
|
+
sage: V = F(ZZ, 5)
|
|
650
|
+
sage: func, data = F.reduce_data(V)
|
|
651
|
+
sage: func is sage.structure.factory.generic_factory_unpickle
|
|
652
|
+
True
|
|
653
|
+
sage: sage.structure.factory.generic_factory_unpickle(*data) is V
|
|
654
|
+
True
|
|
655
|
+
|
|
656
|
+
TESTS:
|
|
657
|
+
|
|
658
|
+
The following was enabled in :issue:`16349`. Suppose we have defined
|
|
659
|
+
(somewhere in the library of an old Sage version) a unique factory; in our
|
|
660
|
+
example below, it returns polynomial rings. Now suppose that we want to
|
|
661
|
+
replace the factory by something else, say, a class that provides the
|
|
662
|
+
unique parent behaviour using
|
|
663
|
+
:class:`~sage.structure.unique_representation.UniqueRepresentation`. We
|
|
664
|
+
show here how to make it possible to unpickle a pickle created with the
|
|
665
|
+
factory, automatically turning it into an instance of the new class.
|
|
666
|
+
|
|
667
|
+
First, we create the factory. In a doctest, it is needed to explicitly put
|
|
668
|
+
it into ``__main__``, so that it can be located when pickling. Also, it is
|
|
669
|
+
needed that we work with a new-style class::
|
|
670
|
+
|
|
671
|
+
sage: from sage.structure.factory import UniqueFactory
|
|
672
|
+
sage: import __main__
|
|
673
|
+
sage: class OldStuff():
|
|
674
|
+
....: def __init__(self, n, **extras):
|
|
675
|
+
....: self.n = n
|
|
676
|
+
....: def __repr__(self):
|
|
677
|
+
....: return "Rotten old thing of level {}".format(self.n)
|
|
678
|
+
sage: __main__.OldStuff = OldStuff
|
|
679
|
+
sage: class MyFactory(UniqueFactory):
|
|
680
|
+
....: def create_object(self, version, key, **extras):
|
|
681
|
+
....: return OldStuff(key[0])
|
|
682
|
+
....: def create_key(self, *args):
|
|
683
|
+
....: return args
|
|
684
|
+
sage: F = MyFactory('__main__.F')
|
|
685
|
+
sage: __main__.F = F
|
|
686
|
+
sage: a = F(3); a
|
|
687
|
+
Rotten old thing of level 3
|
|
688
|
+
sage: loads(dumps(a)) is a
|
|
689
|
+
True
|
|
690
|
+
|
|
691
|
+
Now, we create a pickle (the string returned by ``dumps(a)``)::
|
|
692
|
+
|
|
693
|
+
sage: s = dumps(a)
|
|
694
|
+
|
|
695
|
+
We create a new class, derived from
|
|
696
|
+
:class:`~sage.structure.unique_representation.UniqueRepresentation`, that
|
|
697
|
+
shall replace the old factory. In particular, the class has to have the
|
|
698
|
+
same name as the old factory, and has to be put into the same module
|
|
699
|
+
(here: ``__main__``). We turn it into a sub-class of the old class, but
|
|
700
|
+
this is just to save the effort of writing a new init method::
|
|
701
|
+
|
|
702
|
+
sage: from sage.structure.unique_representation import UniqueRepresentation
|
|
703
|
+
sage: class F(UniqueRepresentation, OldStuff):
|
|
704
|
+
....: def __repr__(self):
|
|
705
|
+
....: return "Shiny new thing of level {}".format(self.n)
|
|
706
|
+
sage: __main__.F = F
|
|
707
|
+
|
|
708
|
+
The old pickle correctly unpickles as an instance of the new class, which
|
|
709
|
+
is of course different from the instance of the old class, but exhibits
|
|
710
|
+
unique object behaviour as well::
|
|
711
|
+
|
|
712
|
+
sage: b = loads(s); b
|
|
713
|
+
Shiny new thing of level 3
|
|
714
|
+
sage: a is b
|
|
715
|
+
False
|
|
716
|
+
sage: loads(dumps(b)) is b
|
|
717
|
+
True
|
|
718
|
+
"""
|
|
719
|
+
cdef UniqueFactory F
|
|
720
|
+
if factory is not None:
|
|
721
|
+
try:
|
|
722
|
+
F = factory
|
|
723
|
+
return F.get_object(*args)
|
|
724
|
+
except TypeError:
|
|
725
|
+
pass
|
|
726
|
+
# See Issue #16349: When replacing a UniqueFactory by something else (e.g.,
|
|
727
|
+
# a UniqueRepresentation), then we get the object by calling.
|
|
728
|
+
#
|
|
729
|
+
# The first argument of a UniqueFactory pickle is a version number. We
|
|
730
|
+
# strip this.
|
|
731
|
+
return factory(*args[1], **args[2])
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def generic_factory_reduce(self, proto):
|
|
735
|
+
"""
|
|
736
|
+
Used to provide a ``__reduce__`` method if one does not already exist.
|
|
737
|
+
|
|
738
|
+
EXAMPLES::
|
|
739
|
+
|
|
740
|
+
sage: V = QQ^6 # needs sage.modules
|
|
741
|
+
sage: sage.structure.factory.generic_factory_reduce(V, 1) == V.__reduce_ex__(1) # needs sage.modules
|
|
742
|
+
True
|
|
743
|
+
"""
|
|
744
|
+
if self._factory_data is None:
|
|
745
|
+
raise NotImplementedError("__reduce__ not implemented for %s" % type(self))
|
|
746
|
+
else:
|
|
747
|
+
return self._factory_data[0].reduce_data(self)
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def lookup_global(name):
|
|
751
|
+
"""
|
|
752
|
+
Used in unpickling the factory itself.
|
|
753
|
+
|
|
754
|
+
EXAMPLES::
|
|
755
|
+
|
|
756
|
+
sage: from sage.structure.factory import lookup_global
|
|
757
|
+
sage: lookup_global('ZZ')
|
|
758
|
+
Integer Ring
|
|
759
|
+
sage: lookup_global('sage.rings.integer_ring.ZZ')
|
|
760
|
+
Integer Ring
|
|
761
|
+
"""
|
|
762
|
+
name = bytes_to_str(name, encoding='ASCII')
|
|
763
|
+
try:
|
|
764
|
+
return factory_unpickles[name]
|
|
765
|
+
except KeyError:
|
|
766
|
+
pass
|
|
767
|
+
|
|
768
|
+
if '.' in name:
|
|
769
|
+
module, name = name.rsplit('.', 1)
|
|
770
|
+
all = __import__(module, fromlist=[name])
|
|
771
|
+
else:
|
|
772
|
+
try:
|
|
773
|
+
import sage.all as all
|
|
774
|
+
except ImportError:
|
|
775
|
+
try:
|
|
776
|
+
import sage.all__sagemath_modules as all
|
|
777
|
+
except ImportError:
|
|
778
|
+
try:
|
|
779
|
+
import sage.all__sagemath_pari as all
|
|
780
|
+
except ImportError:
|
|
781
|
+
import sage.all__sagemath_categories as all
|
|
782
|
+
return getattr(all, name)
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
# Old imports required for unpickling old pickles
|
|
786
|
+
from sage.structure.test_factory import test_factory
|