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
|
@@ -0,0 +1,1743 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
r"""
|
|
3
|
+
Global options
|
|
4
|
+
|
|
5
|
+
The :class:`GlobalOptions` class provides a generic mechanism for
|
|
6
|
+
setting and accessing **global** options for parents in one or several
|
|
7
|
+
related classes, typically for customizing the representation of their
|
|
8
|
+
elements. This class will eventually also support setting options on a
|
|
9
|
+
parent by parent basis.
|
|
10
|
+
|
|
11
|
+
These options should be "attached" to one or more classes as an options method.
|
|
12
|
+
|
|
13
|
+
.. SEEALSO::
|
|
14
|
+
|
|
15
|
+
For good examples of :class:`GlobalOptions` in action see
|
|
16
|
+
:obj:`sage.combinat.partition.Partitions.options` and
|
|
17
|
+
:obj:`sage.combinat.tableau.Tableaux.options`.
|
|
18
|
+
|
|
19
|
+
.. _construction_section:
|
|
20
|
+
|
|
21
|
+
Construction of options classes
|
|
22
|
+
-------------------------------
|
|
23
|
+
|
|
24
|
+
The general setup for creating a set of global options is::
|
|
25
|
+
|
|
26
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
27
|
+
sage: class MyOptions(GlobalOptions):
|
|
28
|
+
....: '''
|
|
29
|
+
....: Nice options
|
|
30
|
+
....:
|
|
31
|
+
....: @OPTIONS@
|
|
32
|
+
....: '''
|
|
33
|
+
....: NAME = 'option name'
|
|
34
|
+
....: module = 'sage.some_module.some_file'
|
|
35
|
+
....: option_class = 'name_of_class_controlled_by_options'
|
|
36
|
+
....: first_option = dict(default='with_bells',
|
|
37
|
+
....: description='Changes the functionality of _repr_',
|
|
38
|
+
....: values=dict(with_bells='causes _repr_ to print with bells',
|
|
39
|
+
....: with_whistles='causes _repr_ to print with whistles'),
|
|
40
|
+
....: alias=dict(bells='option1', whistles='option2'))
|
|
41
|
+
....: # second_option = dict(...)
|
|
42
|
+
....: # third_option = dict(...)
|
|
43
|
+
|
|
44
|
+
Note the syntax using the ``class`` keyword. However, because of some
|
|
45
|
+
metaclass magic, the resulting ``MyOptions`` object becomes an instance
|
|
46
|
+
of ``GlobalOptions`` instead of a subclass. So, despite the ``class``
|
|
47
|
+
syntax, ``MyOptions`` is not a class.
|
|
48
|
+
|
|
49
|
+
The options constructed by :class:`GlobalOptions` have to be explicitly
|
|
50
|
+
associated to the class that they control using the following arguments:
|
|
51
|
+
|
|
52
|
+
- ``NAME`` -- a descriptive name for the options class; this is
|
|
53
|
+
optional. The default is the name of the constructed class.
|
|
54
|
+
|
|
55
|
+
- ``module`` -- the sage module containing the options class (optional)
|
|
56
|
+
|
|
57
|
+
- ``option_class`` -- the name of the options class; this is optional and
|
|
58
|
+
defaults to ``NAME`` if not explicitly set
|
|
59
|
+
|
|
60
|
+
It is only possible to pickle a :class:`GlobalOptions` class if the
|
|
61
|
+
corresponding module is specified *and* if the options are explicitly
|
|
62
|
+
attached to the corresponding class as a *options* method.
|
|
63
|
+
|
|
64
|
+
Each option is specified as a dictionary which describes the possible
|
|
65
|
+
values for the option and its documentation. The possible entries in this
|
|
66
|
+
dictionary are:
|
|
67
|
+
|
|
68
|
+
- ``alias`` -- allows for several option values to do the same thing
|
|
69
|
+
|
|
70
|
+
- ``alt_name`` -- an alternative name for this option
|
|
71
|
+
|
|
72
|
+
- ``checker`` -- a validation function which returns whether a user
|
|
73
|
+
supplied value is valid or not. This is typically useful for large
|
|
74
|
+
lists of legal values such as :class:`~sage.rings.semirings.non_negative_integer_semiring.NN`.
|
|
75
|
+
|
|
76
|
+
- ``default`` -- gives the default value for the option
|
|
77
|
+
|
|
78
|
+
- ``description`` -- a one line description of the option
|
|
79
|
+
|
|
80
|
+
- ``link_to`` -- links this option to another one in another set of
|
|
81
|
+
global options. This is used for example to allow
|
|
82
|
+
:class:`Partitions` and :class:`Tableaux` to share the same
|
|
83
|
+
``convention`` option.
|
|
84
|
+
|
|
85
|
+
- ``setter`` -- a function which is called **after** the value of the
|
|
86
|
+
option is changed
|
|
87
|
+
|
|
88
|
+
- ``values`` -- dictionary assigning each valid value for the option
|
|
89
|
+
to a short description of what it does
|
|
90
|
+
|
|
91
|
+
- ``case_sensitive`` -- boolean (default: ``True``); depending on
|
|
92
|
+
whether the values of the option are case sensitive
|
|
93
|
+
|
|
94
|
+
For each option, either a complete list of possible values, via ``values``, or a
|
|
95
|
+
validation function, via ``checker``, must be given. The values can be quite
|
|
96
|
+
arbitrary, including user-defined functions which customize the default
|
|
97
|
+
behaviour of the classes such as the output of ``_repr_`` or :func:`latex`. See
|
|
98
|
+
:ref:`dispatcher` below, and :meth:`~GlobalOptions._dispatcher`, for more
|
|
99
|
+
information.
|
|
100
|
+
|
|
101
|
+
The documentation for the options is automatically constructed from
|
|
102
|
+
the docstring of the class by replacing the magic word ``@OPTIONS@``
|
|
103
|
+
with a description of each option.
|
|
104
|
+
|
|
105
|
+
The basic structure for defining a :class:`GlobalOptions` class is best
|
|
106
|
+
illustrated by an example::
|
|
107
|
+
|
|
108
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
109
|
+
sage: class Menu():
|
|
110
|
+
....: class options(GlobalOptions):
|
|
111
|
+
....: '''
|
|
112
|
+
....: Fancy documentation
|
|
113
|
+
....: -------------------
|
|
114
|
+
....:
|
|
115
|
+
....: @OPTIONS@
|
|
116
|
+
....:
|
|
117
|
+
....: The END!
|
|
118
|
+
....: '''
|
|
119
|
+
....: NAME = 'menu'
|
|
120
|
+
....: entree = dict(default='soup',
|
|
121
|
+
....: description='The first course of a meal',
|
|
122
|
+
....: values=dict(soup='soup of the day', bread='oven baked'),
|
|
123
|
+
....: alias=dict(rye='bread'))
|
|
124
|
+
....: appetizer = dict(alt_name='entree')
|
|
125
|
+
....: main = dict(default='pizza', description='Main meal',
|
|
126
|
+
....: values=dict(pizza='thick crust', pasta='penne arrabiata'),
|
|
127
|
+
....: case_sensitive=False)
|
|
128
|
+
....: dessert = dict(default='espresso', description='Dessert',
|
|
129
|
+
....: values=dict(espresso='life begins again',
|
|
130
|
+
....: cake='waist begins again',
|
|
131
|
+
....: cream='fluffy, white stuff'))
|
|
132
|
+
....: tip = dict(default=10, description='Reward for good service',
|
|
133
|
+
....: checker = lambda tip: tip in range(20))
|
|
134
|
+
sage: Menu.options
|
|
135
|
+
Current options for menu
|
|
136
|
+
- dessert: espresso
|
|
137
|
+
- entree: soup
|
|
138
|
+
- main: pizza
|
|
139
|
+
- tip: 10
|
|
140
|
+
|
|
141
|
+
In the examples above, the options are constructed when the ``options``
|
|
142
|
+
object is created. However, it is also possible to construct the options
|
|
143
|
+
dynamically using the :meth:`GlobalOptions._add_to_options` methods.
|
|
144
|
+
|
|
145
|
+
For more details see :class:`GlobalOptions`.
|
|
146
|
+
|
|
147
|
+
Accessing and setting option values
|
|
148
|
+
-----------------------------------
|
|
149
|
+
|
|
150
|
+
All options and their values, when they are strings, are forced to be lower
|
|
151
|
+
case. The values of an options class can be set and accessed by calling the
|
|
152
|
+
class or by treating the class as an array.
|
|
153
|
+
|
|
154
|
+
Continuing the example from :ref:`construction_section`::
|
|
155
|
+
|
|
156
|
+
sage: Menu.options
|
|
157
|
+
Current options for menu
|
|
158
|
+
- dessert: espresso
|
|
159
|
+
- entree: soup
|
|
160
|
+
- main: pizza
|
|
161
|
+
- tip: 10
|
|
162
|
+
sage: Menu.options.dessert
|
|
163
|
+
espresso
|
|
164
|
+
sage: Menu.options.dessert = 'cake'
|
|
165
|
+
sage: Menu.options.dessert
|
|
166
|
+
cake
|
|
167
|
+
|
|
168
|
+
Note that, provided there is no ambiguity, options and their values can be
|
|
169
|
+
abbreviated::
|
|
170
|
+
|
|
171
|
+
sage: Menu.options('d')
|
|
172
|
+
'cake'
|
|
173
|
+
sage: Menu.options('m','t',des='esp', ent='sou') # get and set several values at once
|
|
174
|
+
['pizza', 10]
|
|
175
|
+
sage: Menu.options(t=15)
|
|
176
|
+
sage: Menu.options('tip')
|
|
177
|
+
15
|
|
178
|
+
sage: Menu.options.tip
|
|
179
|
+
15
|
|
180
|
+
sage: Menu.options(e='s', m='Pi'); Menu.options()
|
|
181
|
+
Current options for menu
|
|
182
|
+
- dessert: cake
|
|
183
|
+
- entree: soup
|
|
184
|
+
- main: pizza
|
|
185
|
+
- tip: 15
|
|
186
|
+
sage: Menu.options(m='P')
|
|
187
|
+
Traceback (most recent call last):
|
|
188
|
+
...
|
|
189
|
+
ValueError: P is not a valid value for main in the options for menu
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
Setter functions
|
|
193
|
+
----------------
|
|
194
|
+
|
|
195
|
+
Each option of a :class:`GlobalOptions` can be equipped with an optional setter
|
|
196
|
+
function which is called **after** the value of the option is changed. In the
|
|
197
|
+
following example, setting the option 'add' changes the state of the class by
|
|
198
|
+
setting an attribute in this class using a :func:`classmethod`. Note that the
|
|
199
|
+
options object is inserted after the creation of the class in order to access
|
|
200
|
+
the :func:`classmethod` as ``A.setter``::
|
|
201
|
+
|
|
202
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
203
|
+
sage: class A(SageObject):
|
|
204
|
+
....: state = 0
|
|
205
|
+
....: @classmethod
|
|
206
|
+
....: def setter(cls, option, val):
|
|
207
|
+
....: cls.state += int(val)
|
|
208
|
+
sage: class options(GlobalOptions):
|
|
209
|
+
....: NAME = "A"
|
|
210
|
+
....: add = dict(default=1,
|
|
211
|
+
....: checker=lambda v: int(v)>0,
|
|
212
|
+
....: description='An option with a setter',
|
|
213
|
+
....: setter=A.setter)
|
|
214
|
+
sage: A.options = options
|
|
215
|
+
sage: A.options
|
|
216
|
+
Current options for A
|
|
217
|
+
- add: 1
|
|
218
|
+
sage: a = A(); a.state
|
|
219
|
+
1
|
|
220
|
+
sage: a.options()
|
|
221
|
+
Current options for A
|
|
222
|
+
- add: 1
|
|
223
|
+
sage: a.options(add=4)
|
|
224
|
+
sage: a.state
|
|
225
|
+
5
|
|
226
|
+
sage: a.options()
|
|
227
|
+
Current options for A
|
|
228
|
+
- add: 4
|
|
229
|
+
|
|
230
|
+
Documentation for options
|
|
231
|
+
-------------------------
|
|
232
|
+
|
|
233
|
+
The documentation for a :class:`GlobalOptions` is automatically generated from
|
|
234
|
+
the supplied options. For example, the generated documentation for the options
|
|
235
|
+
``menu`` defined in :ref:`construction_section` is the following:
|
|
236
|
+
|
|
237
|
+
.. CODE-BLOCK:: text
|
|
238
|
+
|
|
239
|
+
Fancy documentation
|
|
240
|
+
-------------------
|
|
241
|
+
|
|
242
|
+
OPTIONS:
|
|
243
|
+
|
|
244
|
+
- ``appetizer`` -- alternative name for ``entree``
|
|
245
|
+
- ``dessert`` -- (default: ``espresso``)
|
|
246
|
+
Dessert
|
|
247
|
+
|
|
248
|
+
- ``cake`` -- waist begins again
|
|
249
|
+
- ``cream`` -- fluffy, white stuff
|
|
250
|
+
- ``espresso`` -- life begins again
|
|
251
|
+
|
|
252
|
+
- ``entree`` -- (default: ``soup``)
|
|
253
|
+
The first course of a meal
|
|
254
|
+
|
|
255
|
+
- ``bread`` -- oven baked
|
|
256
|
+
- ``rye`` -- alias for ``bread``
|
|
257
|
+
- ``soup`` -- soup of the day
|
|
258
|
+
|
|
259
|
+
- ``main`` -- (default: ``pizza``)
|
|
260
|
+
Main meal
|
|
261
|
+
|
|
262
|
+
- ``pasta`` -- penne arrabiata
|
|
263
|
+
- ``pizza`` -- thick crust
|
|
264
|
+
|
|
265
|
+
- ``tip`` -- (default: ``10``)
|
|
266
|
+
Reward for good service
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
The END!
|
|
270
|
+
|
|
271
|
+
See :class:`~sage.structure.global_options.GlobalOptions` for more features of these options.
|
|
272
|
+
|
|
273
|
+
In addition, help on each option, and its list of possible values, can be
|
|
274
|
+
obtained by (trying to) set the option equal to '?'::
|
|
275
|
+
|
|
276
|
+
sage: Menu.options.dessert? # not tested
|
|
277
|
+
- ``dessert`` -- (default: ``espresso``)
|
|
278
|
+
Dessert
|
|
279
|
+
|
|
280
|
+
- ``cake`` -- waist begins again
|
|
281
|
+
- ``cream`` -- fluffy, white stuff
|
|
282
|
+
- ``espresso`` -- life begins again
|
|
283
|
+
|
|
284
|
+
.. _dispatcher:
|
|
285
|
+
|
|
286
|
+
Dispatchers
|
|
287
|
+
-----------
|
|
288
|
+
|
|
289
|
+
The whole idea of a :class:`GlobalOptions` class is that the options change the
|
|
290
|
+
default behaviour of the associated classes. This can be done either by simply
|
|
291
|
+
checking what the current value of the relevant option is. Another possibility
|
|
292
|
+
is to use the options class as a dispatcher to associated methods. To use the
|
|
293
|
+
dispatcher feature of a :class:`GlobalOptions` class it is necessary to implement
|
|
294
|
+
separate methods for each value of the option where the naming convention for
|
|
295
|
+
these methods is that they start with a common prefix and finish with the value
|
|
296
|
+
of the option.
|
|
297
|
+
|
|
298
|
+
If the value of a dispatchable option is set equal to a (user defined) function
|
|
299
|
+
then this function is called instead of a class method.
|
|
300
|
+
|
|
301
|
+
For example, the options ``MyOptions`` can be used to dispatch the ``_repr_``
|
|
302
|
+
method of the associated class ``MyClass`` as follows:
|
|
303
|
+
|
|
304
|
+
.. CODE-BLOCK:: python
|
|
305
|
+
|
|
306
|
+
class MyClass(...):
|
|
307
|
+
def _repr_(self):
|
|
308
|
+
return self.options._dispatch(self,'_repr_','first_option')
|
|
309
|
+
def _repr_with_bells(self):
|
|
310
|
+
print('Bell!')
|
|
311
|
+
def _repr_with_whistles(self):
|
|
312
|
+
print('Whistles!')
|
|
313
|
+
class MyOptions(GlobalOptions):
|
|
314
|
+
...
|
|
315
|
+
|
|
316
|
+
In this example, ``first_option`` is an option of ``MyOptions`` which takes
|
|
317
|
+
values ``bells``, ``whistles``, and so on. Note that it is necessary to make
|
|
318
|
+
``self``, which is an instance of ``MyClass``, an argument of the dispatcher
|
|
319
|
+
because :meth:`~GlobalOptions._dispatch()` is a method of :class:`GlobalOptions`
|
|
320
|
+
and not a method of ``MyClass``. Apart from ``MyOptions``, as it is a method of
|
|
321
|
+
this class, the arguments are the attached class (here ``MyClass``), the prefix
|
|
322
|
+
of the method of ``MyClass`` being dispatched, the option of ``MyOptions``
|
|
323
|
+
which controls the dispatching. All other arguments are passed through to the
|
|
324
|
+
corresponding methods of ``MyClass``. In general, a dispatcher is invoked as:
|
|
325
|
+
|
|
326
|
+
.. CODE-BLOCK:: python
|
|
327
|
+
|
|
328
|
+
self.options._dispatch(self, dispatch_to, option, *args, **kargs)
|
|
329
|
+
|
|
330
|
+
Usually this will result in the method
|
|
331
|
+
``dispatch_to + '_' + MyOptions(options)`` of ``self`` being called with
|
|
332
|
+
arguments ``*args`` and ``**kargs`` (if ``dispatch_to[-1] == '_'`` then the
|
|
333
|
+
method ``dispatch_to + MyOptions(options)`` is called).
|
|
334
|
+
|
|
335
|
+
If ``MyOptions(options)`` is itself a function then the dispatcher will call
|
|
336
|
+
this function instead. In this way, it is possible to allow the user to
|
|
337
|
+
customise the default behaviour of this method. See
|
|
338
|
+
:meth:`~GlobalOptions._dispatch` for an example of how this can be achieved.
|
|
339
|
+
|
|
340
|
+
The dispatching capabilities of :class:`GlobalOptions` allows options to be
|
|
341
|
+
applied automatically without needing to parse different values of the option
|
|
342
|
+
(the cost is that there must be a method for each value). The dispatching
|
|
343
|
+
capabilities can also be used to make one option control several methods:
|
|
344
|
+
|
|
345
|
+
.. CODE-BLOCK:: python
|
|
346
|
+
|
|
347
|
+
def __le__(self, other):
|
|
348
|
+
return self.options._dispatch(self, '_le_','cmp', other)
|
|
349
|
+
def __ge__(self, other):
|
|
350
|
+
return self.options._dispatch(self, '_ge_','cmp', other)
|
|
351
|
+
def _le_option_a(self, other):
|
|
352
|
+
return ...
|
|
353
|
+
def _ge_option_a(self, other):
|
|
354
|
+
return ...
|
|
355
|
+
def _le_option_b(self, other):
|
|
356
|
+
return ...
|
|
357
|
+
def _ge_option_b(self, other):
|
|
358
|
+
return ...
|
|
359
|
+
|
|
360
|
+
See :meth:`~GlobalOptions._dispatch` for more details.
|
|
361
|
+
|
|
362
|
+
Doc testing
|
|
363
|
+
-----------
|
|
364
|
+
|
|
365
|
+
All of the options and their effects should be doc-tested. However, in order
|
|
366
|
+
not to break other tests, all options should be returned to their default state
|
|
367
|
+
at the end of each test. To make this easier, every :class:`GlobalOptions` class has
|
|
368
|
+
a :meth:`~GlobalOptions._reset()` method for doing exactly this.
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
Pickling
|
|
372
|
+
--------
|
|
373
|
+
|
|
374
|
+
Options classes can only be pickled if they are the options for some standard
|
|
375
|
+
sage class. In this case the class is specified using the arguments to
|
|
376
|
+
:class:`GlobalOptions`. For example
|
|
377
|
+
:meth:`~sage.combinat.partition.Partitions.options` is defined as:
|
|
378
|
+
|
|
379
|
+
.. CODE-BLOCK:: python
|
|
380
|
+
|
|
381
|
+
class Partitions(UniqueRepresentation, Parent):
|
|
382
|
+
...
|
|
383
|
+
class options(GlobalOptions):
|
|
384
|
+
NAME = 'Partitions'
|
|
385
|
+
module = 'sage.combinat.partition'
|
|
386
|
+
...
|
|
387
|
+
|
|
388
|
+
Here is an example to test the pickling of a :class:`GlobalOptions` instance::
|
|
389
|
+
|
|
390
|
+
sage: TestSuite(Partitions.options).run()
|
|
391
|
+
|
|
392
|
+
TESTS:
|
|
393
|
+
|
|
394
|
+
Check that the old call syntax still works::
|
|
395
|
+
|
|
396
|
+
sage: class Menu():
|
|
397
|
+
....: options = GlobalOptions('menu',
|
|
398
|
+
....: doc='Fancy documentation\n'+'-'*19, end_doc='The END!',
|
|
399
|
+
....: entree=dict(default='soup',
|
|
400
|
+
....: description='The first course of a meal',
|
|
401
|
+
....: values=dict(soup='soup of the day', bread='oven baked'),
|
|
402
|
+
....: alias=dict(rye='bread')),
|
|
403
|
+
....: appetizer=dict(alt_name='entree'),
|
|
404
|
+
....: main=dict(default='pizza', description='Main meal',
|
|
405
|
+
....: values=dict(pizza='thick crust', pasta='penne arrabiata'),
|
|
406
|
+
....: case_sensitive=False),
|
|
407
|
+
....: dessert=dict(default='espresso', description='Dessert',
|
|
408
|
+
....: values=dict(espresso='life begins again',
|
|
409
|
+
....: cake='waist begins again',
|
|
410
|
+
....: cream='fluffy, white stuff')),
|
|
411
|
+
....: tip=dict(default=10, description='Reward for good service',
|
|
412
|
+
....: checker=lambda tip: tip in range(20))
|
|
413
|
+
....: )
|
|
414
|
+
sage: Menu.options
|
|
415
|
+
Current options for menu
|
|
416
|
+
- dessert: espresso
|
|
417
|
+
- entree: soup
|
|
418
|
+
- main: pizza
|
|
419
|
+
- tip: 10
|
|
420
|
+
|
|
421
|
+
We can have a ``name`` option::
|
|
422
|
+
|
|
423
|
+
sage: class MyOptions(GlobalOptions):
|
|
424
|
+
....: name = dict(default='alice', values={'alice': "A", 'bob': "B"})
|
|
425
|
+
sage: MyOptions
|
|
426
|
+
Current options for MyOptions
|
|
427
|
+
- name: alice
|
|
428
|
+
|
|
429
|
+
Check that the ``name`` and ``NAME`` keywords are both supported with
|
|
430
|
+
this syntax::
|
|
431
|
+
|
|
432
|
+
sage: GlobalOptions(name='menu')
|
|
433
|
+
Current options for menu
|
|
434
|
+
sage: GlobalOptions(NAME='menu')
|
|
435
|
+
Current options for menu
|
|
436
|
+
sage: GlobalOptions()
|
|
437
|
+
Traceback (most recent call last):
|
|
438
|
+
...
|
|
439
|
+
TypeError: GlobalOptions() is missing keyword argument 'name'
|
|
440
|
+
|
|
441
|
+
Test the documentation examples above::
|
|
442
|
+
|
|
443
|
+
sage: print(Menu.options.__doc__)
|
|
444
|
+
Fancy documentation
|
|
445
|
+
-------------------
|
|
446
|
+
<BLANKLINE>
|
|
447
|
+
OPTIONS:
|
|
448
|
+
<BLANKLINE>
|
|
449
|
+
- ``appetizer`` -- alternative name for ``entree``
|
|
450
|
+
- ``dessert`` -- (default: ``espresso``)
|
|
451
|
+
Dessert
|
|
452
|
+
<BLANKLINE>
|
|
453
|
+
- ``cake`` -- waist begins again
|
|
454
|
+
- ``cream`` -- fluffy, white stuff
|
|
455
|
+
- ``espresso`` -- life begins again
|
|
456
|
+
<BLANKLINE>
|
|
457
|
+
- ``entree`` -- (default: ``soup``)
|
|
458
|
+
The first course of a meal
|
|
459
|
+
<BLANKLINE>
|
|
460
|
+
- ``bread`` -- oven baked
|
|
461
|
+
- ``rye`` -- alias for ``bread``
|
|
462
|
+
- ``soup`` -- soup of the day
|
|
463
|
+
<BLANKLINE>
|
|
464
|
+
- ``main`` -- (default: ``pizza``)
|
|
465
|
+
Main meal
|
|
466
|
+
<BLANKLINE>
|
|
467
|
+
- ``pasta`` -- penne arrabiata
|
|
468
|
+
- ``pizza`` -- thick crust
|
|
469
|
+
<BLANKLINE>
|
|
470
|
+
- ``tip`` -- (default: ``10``)
|
|
471
|
+
Reward for good service
|
|
472
|
+
<BLANKLINE>
|
|
473
|
+
<BLANKLINE>
|
|
474
|
+
<BLANKLINE>
|
|
475
|
+
<BLANKLINE>
|
|
476
|
+
The END!
|
|
477
|
+
See :class:`~sage.structure.global_options.GlobalOptions` for more features of these options.
|
|
478
|
+
|
|
479
|
+
::
|
|
480
|
+
|
|
481
|
+
sage: print(Menu.options.dessert.__doc__)
|
|
482
|
+
- ``dessert`` -- (default: ``espresso``)
|
|
483
|
+
Dessert
|
|
484
|
+
<BLANKLINE>
|
|
485
|
+
- ``cake`` -- waist begins again
|
|
486
|
+
- ``cream`` -- fluffy, white stuff
|
|
487
|
+
- ``espresso`` -- life begins again
|
|
488
|
+
<BLANKLINE>
|
|
489
|
+
|
|
490
|
+
AUTHORS:
|
|
491
|
+
|
|
492
|
+
- Andrew Mathas (2013): initial version
|
|
493
|
+
- Andrew Mathas (2016): overhaul making the options attributes, enabling
|
|
494
|
+
pickling and attaching the options to a class.
|
|
495
|
+
- Jeroen Demeyer (2017): use subclassing to create instances
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
# ****************************************************************************
|
|
499
|
+
# Copyright (C) 2013,2016 Andrew Mathas <andrew dot mathas at sydney dot edu dot au>
|
|
500
|
+
# Copyright (C) 2017 Jeroen Demeyer <J.Demeyer@UGent.be>
|
|
501
|
+
#
|
|
502
|
+
# This program is free software: you can redistribute it and/or modify
|
|
503
|
+
# it under the terms of the GNU General Public License as published by
|
|
504
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
505
|
+
# (at your option) any later version.
|
|
506
|
+
# https://www.gnu.org/licenses/
|
|
507
|
+
# ****************************************************************************
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
from importlib import import_module
|
|
511
|
+
from pickle import PicklingError
|
|
512
|
+
from textwrap import dedent
|
|
513
|
+
|
|
514
|
+
from sage.misc.instancedoc import instancedoc
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
class Option:
|
|
518
|
+
r"""
|
|
519
|
+
An option.
|
|
520
|
+
|
|
521
|
+
Each option for an options class is an instance of this class which
|
|
522
|
+
implements the magic that allows the options to the attributes of the
|
|
523
|
+
options class that can be looked up, set and called.
|
|
524
|
+
|
|
525
|
+
By way of example, this class implements the following functionality.
|
|
526
|
+
|
|
527
|
+
EXAMPLES::
|
|
528
|
+
|
|
529
|
+
sage: # needs sage.combinat
|
|
530
|
+
sage: Partitions.options.display
|
|
531
|
+
list
|
|
532
|
+
sage: Partitions.options.display = 'compact'
|
|
533
|
+
sage: Partitions.options.display('list')
|
|
534
|
+
sage: Partitions.options._reset()
|
|
535
|
+
|
|
536
|
+
TESTS::
|
|
537
|
+
|
|
538
|
+
sage: TestSuite(Partitions.options.display).run()
|
|
539
|
+
"""
|
|
540
|
+
__name__ = 'Option class'
|
|
541
|
+
|
|
542
|
+
def __init__(self, options, name):
|
|
543
|
+
r"""
|
|
544
|
+
Initialise an option by settings its ``name``, "parent" option class
|
|
545
|
+
``options`` and doc-string.
|
|
546
|
+
|
|
547
|
+
EXAMPLES::
|
|
548
|
+
|
|
549
|
+
sage: type(Partitions.options.display) # indirect doctest
|
|
550
|
+
<class 'sage.structure.global_options.Option'>
|
|
551
|
+
"""
|
|
552
|
+
self._name = name
|
|
553
|
+
self._options = options
|
|
554
|
+
self.__doc__ = options._doc[name]
|
|
555
|
+
super().__init__()
|
|
556
|
+
|
|
557
|
+
def __repr__(self):
|
|
558
|
+
r"""
|
|
559
|
+
Return a string representation for this collection of options.
|
|
560
|
+
|
|
561
|
+
EXAMPLES::
|
|
562
|
+
|
|
563
|
+
sage: Partitions.options.display # indirect doctest
|
|
564
|
+
list
|
|
565
|
+
"""
|
|
566
|
+
# NOTE: we intentionally use str() instead of repr()
|
|
567
|
+
return str(self._options[self._name])
|
|
568
|
+
|
|
569
|
+
def __add__(self, other):
|
|
570
|
+
r"""
|
|
571
|
+
Return the object obtained by adding ``self`` and ``other``, where
|
|
572
|
+
``self`` behaves like its value.
|
|
573
|
+
|
|
574
|
+
EXAMPLES::
|
|
575
|
+
|
|
576
|
+
sage: Tableaux.options.convention + ' is good' # needs sage.combinat
|
|
577
|
+
'English is good'
|
|
578
|
+
"""
|
|
579
|
+
return self._options[self._name] + other
|
|
580
|
+
|
|
581
|
+
def __radd__(self, other):
|
|
582
|
+
r"""
|
|
583
|
+
Return the object obtained by adding ``other`` and ``self``, where
|
|
584
|
+
``self`` behaves like its value.
|
|
585
|
+
|
|
586
|
+
EXAMPLES::
|
|
587
|
+
|
|
588
|
+
sage: 'Good ' + Tableaux.options.convention # needs sage.combinat
|
|
589
|
+
'Good English'
|
|
590
|
+
"""
|
|
591
|
+
return other + self._options[self._name]
|
|
592
|
+
|
|
593
|
+
def __mul__(self, other):
|
|
594
|
+
r"""
|
|
595
|
+
Return the object obtained by adding ``self`` and ``other``, where
|
|
596
|
+
``self`` behaves like its value.
|
|
597
|
+
|
|
598
|
+
EXAMPLES::
|
|
599
|
+
|
|
600
|
+
sage: Tableaux.options.convention + ' is good' # needs sage.combinat
|
|
601
|
+
'English is good'
|
|
602
|
+
"""
|
|
603
|
+
return self._options[self._name] * other
|
|
604
|
+
|
|
605
|
+
def __rmul__(self, other):
|
|
606
|
+
r"""
|
|
607
|
+
Return the object obtained by r-adding ``other`` and ``self``, where
|
|
608
|
+
``self`` behaves like its value.
|
|
609
|
+
|
|
610
|
+
EXAMPLES::
|
|
611
|
+
|
|
612
|
+
sage: 'Good ' + Tableaux.options.convention # needs sage.combinat
|
|
613
|
+
'Good English'
|
|
614
|
+
"""
|
|
615
|
+
return other * self._options[self._name]
|
|
616
|
+
|
|
617
|
+
def __bool__(self) -> bool:
|
|
618
|
+
r"""
|
|
619
|
+
Return the value of this option interpreted as a boolean.
|
|
620
|
+
|
|
621
|
+
EXAMPLES::
|
|
622
|
+
|
|
623
|
+
sage: # needs sage.combinat sage.graphs sage.modules
|
|
624
|
+
sage: RiggedConfigurations.options.half_width_boxes_type_B
|
|
625
|
+
True
|
|
626
|
+
sage: 'yes' if RiggedConfigurations.options.half_width_boxes_type_B else 'no'
|
|
627
|
+
'yes'
|
|
628
|
+
sage: RiggedConfigurations.options.half_width_boxes_type_B = False
|
|
629
|
+
sage: 'yes' if RiggedConfigurations.options.half_width_boxes_type_B else 'no'
|
|
630
|
+
'no'
|
|
631
|
+
sage: RiggedConfigurations.options._reset()
|
|
632
|
+
"""
|
|
633
|
+
return bool(self._options[self._name])
|
|
634
|
+
|
|
635
|
+
def __call__(self, *args, **kwds):
|
|
636
|
+
r"""
|
|
637
|
+
Get or set value of the option ``self``.
|
|
638
|
+
|
|
639
|
+
EXAMPLES::
|
|
640
|
+
|
|
641
|
+
sage: # needs sage.combinat
|
|
642
|
+
sage: Partitions.options.display() # indirect doctest
|
|
643
|
+
'list'
|
|
644
|
+
sage: Partitions.options.display('exp') # indirect doctest
|
|
645
|
+
sage: Partitions.options.display() # indirect doctest
|
|
646
|
+
'exp_low'
|
|
647
|
+
sage: Partitions.options._reset()
|
|
648
|
+
|
|
649
|
+
TESTS:
|
|
650
|
+
|
|
651
|
+
Check that values can be set to ``None`` (:issue:`30763`)::
|
|
652
|
+
|
|
653
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
654
|
+
sage: class config(GlobalOptions):
|
|
655
|
+
....: size = dict(default=42,
|
|
656
|
+
....: description='integer or None',
|
|
657
|
+
....: checker=lambda val: val is None or val >= 0)
|
|
658
|
+
sage: config.size()
|
|
659
|
+
42
|
|
660
|
+
sage: config.size(None)
|
|
661
|
+
sage: config.size() is None
|
|
662
|
+
True
|
|
663
|
+
sage: config._reset()
|
|
664
|
+
|
|
665
|
+
Check the deprecation::
|
|
666
|
+
|
|
667
|
+
sage: config.size(value=None)
|
|
668
|
+
doctest:...: DeprecationWarning: keyword argument "value" should be replaced by positional argument
|
|
669
|
+
See https://github.com/sagemath/sage/issues/30763 for details.
|
|
670
|
+
sage: config.size() is None
|
|
671
|
+
True
|
|
672
|
+
sage: config.size(1, 2)
|
|
673
|
+
Traceback (most recent call last):
|
|
674
|
+
...
|
|
675
|
+
TypeError: option takes at most one argument "value"
|
|
676
|
+
sage: config.size(unknown=3)
|
|
677
|
+
Traceback (most recent call last):
|
|
678
|
+
...
|
|
679
|
+
TypeError: option takes at most one argument "value"
|
|
680
|
+
sage: config.size(4, value=5)
|
|
681
|
+
Traceback (most recent call last):
|
|
682
|
+
...
|
|
683
|
+
TypeError: option takes at most one argument "value"
|
|
684
|
+
"""
|
|
685
|
+
if not args and not kwds:
|
|
686
|
+
return self._options[self._name]
|
|
687
|
+
if 'value' in kwds:
|
|
688
|
+
from sage.misc.superseded import deprecation
|
|
689
|
+
deprecation(30763, 'keyword argument "value" should be replaced '
|
|
690
|
+
'by positional argument')
|
|
691
|
+
args += (kwds.pop('value'),)
|
|
692
|
+
if len(args) > 1 or kwds:
|
|
693
|
+
raise TypeError('option takes at most one argument "value"')
|
|
694
|
+
self._options[self._name] = args[0]
|
|
695
|
+
|
|
696
|
+
def __eq__(self, other):
|
|
697
|
+
r"""
|
|
698
|
+
Equality testing for an option in based on the value of the attribute.
|
|
699
|
+
|
|
700
|
+
EXAMPLES::
|
|
701
|
+
|
|
702
|
+
sage: Tableaux.options.convention # needs sage.combinat
|
|
703
|
+
English
|
|
704
|
+
sage: Tableaux.options.convention == "English" # needs sage.combinat
|
|
705
|
+
True
|
|
706
|
+
sage: Tableaux.options.convention == "French" # needs sage.combinat
|
|
707
|
+
False
|
|
708
|
+
"""
|
|
709
|
+
return self._options[self._name] == other
|
|
710
|
+
|
|
711
|
+
def __ne__(self, other):
|
|
712
|
+
r"""
|
|
713
|
+
Inequality testing for an option in based on the value of
|
|
714
|
+
the attribute.
|
|
715
|
+
|
|
716
|
+
EXAMPLES::
|
|
717
|
+
|
|
718
|
+
sage: Tableaux.options.convention # needs sage.combinat
|
|
719
|
+
English
|
|
720
|
+
sage: Tableaux.options.convention != "English" # needs sage.combinat
|
|
721
|
+
False
|
|
722
|
+
sage: Tableaux.options.convention != "French" # needs sage.combinat
|
|
723
|
+
True
|
|
724
|
+
"""
|
|
725
|
+
return self._options[self._name] != other
|
|
726
|
+
|
|
727
|
+
def __hash__(self):
|
|
728
|
+
r"""
|
|
729
|
+
Return the hash of ``self``, which is the hash of the corresponding
|
|
730
|
+
value.
|
|
731
|
+
|
|
732
|
+
EXAMPLES::
|
|
733
|
+
|
|
734
|
+
sage: hash(Tableaux.options.convention) == hash(Tableaux.options('convention')) # needs sage.combinat
|
|
735
|
+
True
|
|
736
|
+
"""
|
|
737
|
+
return hash(self._options[self._name])
|
|
738
|
+
|
|
739
|
+
def __str__(self):
|
|
740
|
+
r"""
|
|
741
|
+
Return the string representation of ``self``, which is the string of
|
|
742
|
+
the corresponding value.
|
|
743
|
+
|
|
744
|
+
EXAMPLES::
|
|
745
|
+
|
|
746
|
+
sage: str(Tableaux.options.convention) # needs sage.combinat
|
|
747
|
+
'English'
|
|
748
|
+
"""
|
|
749
|
+
return str(self._options[self._name])
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
class GlobalOptionsMetaMeta(type):
|
|
753
|
+
def __call__(self, name, bases, dict):
|
|
754
|
+
"""
|
|
755
|
+
Called when subclassing an instance of ``self``.
|
|
756
|
+
|
|
757
|
+
Python translates ``class C(B): ...`` to
|
|
758
|
+
``meta = type(B); C = meta("C", (B,), ...)``.
|
|
759
|
+
If we want to intercept this call ``meta(...)``, we need to
|
|
760
|
+
customize ``__call__`` in the metaclass of ``meta``, which is
|
|
761
|
+
this metametaclass.
|
|
762
|
+
|
|
763
|
+
EXAMPLES::
|
|
764
|
+
|
|
765
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
766
|
+
sage: type(GlobalOptions)
|
|
767
|
+
<class 'sage.structure.global_options.GlobalOptionsMeta'>
|
|
768
|
+
sage: type(type(GlobalOptions))
|
|
769
|
+
<class 'sage.structure.global_options.GlobalOptionsMetaMeta'>
|
|
770
|
+
sage: class G(GlobalOptions): pass
|
|
771
|
+
sage: type(G)
|
|
772
|
+
<class 'sage.structure.global_options.GlobalOptions'>
|
|
773
|
+
|
|
774
|
+
Since ``G`` is constructed using ``class`` syntax, the object
|
|
775
|
+
gets a ``__module__`` attribute, different from the
|
|
776
|
+
``__module__`` attribute of its type (:class:`GlobalOptions`)::
|
|
777
|
+
|
|
778
|
+
sage: G.__module__
|
|
779
|
+
'__main__'
|
|
780
|
+
sage: type(G).__module__
|
|
781
|
+
'sage.structure.global_options'
|
|
782
|
+
|
|
783
|
+
Multiple base classes are not allowed::
|
|
784
|
+
|
|
785
|
+
sage: class G(GlobalOptions, object): pass
|
|
786
|
+
Traceback (most recent call last):
|
|
787
|
+
...
|
|
788
|
+
TypeError: GlobalOptions must be the only base class
|
|
789
|
+
"""
|
|
790
|
+
# Allow only a single base class (which is GlobalOptions in
|
|
791
|
+
# practice).
|
|
792
|
+
# If we ever find a reasonable meaning for multiple base
|
|
793
|
+
# classes, note that Python 2 and Python 3 have different
|
|
794
|
+
# semantics for determining the metaclass when multiple base
|
|
795
|
+
# classes are involved.
|
|
796
|
+
#
|
|
797
|
+
# Note: On Python 3 bases is empty if the class was declared
|
|
798
|
+
# without any explicted bases:
|
|
799
|
+
if not bases:
|
|
800
|
+
bases = (object,)
|
|
801
|
+
|
|
802
|
+
if len(bases) != 1:
|
|
803
|
+
raise TypeError("GlobalOptions must be the only base class")
|
|
804
|
+
|
|
805
|
+
base = bases[0]
|
|
806
|
+
if not isinstance(base, self):
|
|
807
|
+
# For the creation of the initial GlobalOptions class, fall
|
|
808
|
+
# back to the usual class creation
|
|
809
|
+
return self.__new__(self, name, bases, dict)
|
|
810
|
+
|
|
811
|
+
# Create an instance of the base class (as opposed to an
|
|
812
|
+
# instance of self, which would be typical for a metaclass)
|
|
813
|
+
instance = base.__new__(base)
|
|
814
|
+
|
|
815
|
+
# Split dict in options for instance.__init__ and attributes to
|
|
816
|
+
# insert in the class __dict__
|
|
817
|
+
kwds = {"NAME": name}
|
|
818
|
+
for key, value in dict.items():
|
|
819
|
+
if key.startswith("__"):
|
|
820
|
+
instance.__dict__[key] = value
|
|
821
|
+
else:
|
|
822
|
+
kwds[key] = value
|
|
823
|
+
|
|
824
|
+
instance.__init__(**kwds)
|
|
825
|
+
return instance
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
class GlobalOptionsMeta(type, metaclass=GlobalOptionsMetaMeta):
|
|
829
|
+
"""
|
|
830
|
+
Metaclass for :class:`GlobalOptions`.
|
|
831
|
+
|
|
832
|
+
This class is itself an instance of :class:`GlobalOptionsMetaMeta`,
|
|
833
|
+
which implements the subclass magic.
|
|
834
|
+
"""
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
@instancedoc
|
|
838
|
+
class GlobalOptions(metaclass=GlobalOptionsMeta):
|
|
839
|
+
r"""
|
|
840
|
+
The :class:`GlobalOptions` class is a generic class for setting and
|
|
841
|
+
accessing global options for Sage objects.
|
|
842
|
+
|
|
843
|
+
While it is possible to create instances of :class:`GlobalOptions`
|
|
844
|
+
the usual way, the recommended syntax is to subclass from
|
|
845
|
+
``GlobalOptions``. Thanks to some metaclass magic, this actually
|
|
846
|
+
creates an instance of ``GlobalOptions`` instead of a subclass.
|
|
847
|
+
|
|
848
|
+
INPUT (as "attributes" of the class):
|
|
849
|
+
|
|
850
|
+
- ``NAME`` -- specifies a name for the options class (optional;
|
|
851
|
+
default: class name)
|
|
852
|
+
|
|
853
|
+
- ``module`` -- gives the module that contains the associated options class
|
|
854
|
+
|
|
855
|
+
- ``option_class`` -- gives the name of the associated module class
|
|
856
|
+
(default: ``NAME``)
|
|
857
|
+
|
|
858
|
+
- option = ``dict(...)`` -- dictionary specifying an option
|
|
859
|
+
|
|
860
|
+
The options are specified by keyword arguments with their values
|
|
861
|
+
being a dictionary which describes the option. The
|
|
862
|
+
allowed/expected keys in the dictionary are:
|
|
863
|
+
|
|
864
|
+
- ``alias`` -- defines alias/synonym for option values
|
|
865
|
+
- ``alt_name`` -- alternative name for an option
|
|
866
|
+
- ``checker`` -- a function for checking whether a particular value for
|
|
867
|
+
the option is valid
|
|
868
|
+
- ``default`` -- the default value of the option
|
|
869
|
+
- ``description`` -- documentation string
|
|
870
|
+
- ``link_to`` -- links to an option for this set of options to an
|
|
871
|
+
option in another :class:`GlobalOptions`
|
|
872
|
+
- ``setter`` -- a function (class method) which is called whenever this
|
|
873
|
+
option changes
|
|
874
|
+
- ``values`` -- dictionary of the legal values for this option (this
|
|
875
|
+
automatically defines the corresponding ``checker``); this dictionary
|
|
876
|
+
gives the possible options, as keys, together with a brief description
|
|
877
|
+
of them
|
|
878
|
+
- ``case_sensitive`` -- boolean (default: ``True``); depending on whether
|
|
879
|
+
the values of the option are case sensitive
|
|
880
|
+
|
|
881
|
+
Options and their values can be abbreviated provided that this
|
|
882
|
+
abbreviation is a prefix of a unique option.
|
|
883
|
+
|
|
884
|
+
EXAMPLES::
|
|
885
|
+
|
|
886
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
887
|
+
sage: class Menu():
|
|
888
|
+
....: class options(GlobalOptions):
|
|
889
|
+
....: '''
|
|
890
|
+
....: Fancy documentation
|
|
891
|
+
....: -------------------
|
|
892
|
+
....:
|
|
893
|
+
....: @OPTIONS@
|
|
894
|
+
....:
|
|
895
|
+
....: End of Fancy documentation
|
|
896
|
+
....: '''
|
|
897
|
+
....: NAME = 'menu'
|
|
898
|
+
....: entree = dict(default='soup',
|
|
899
|
+
....: description='The first course of a meal',
|
|
900
|
+
....: values=dict(soup='soup of the day', bread='oven baked'),
|
|
901
|
+
....: alias=dict(rye='bread'))
|
|
902
|
+
....: appetizer = dict(alt_name='entree')
|
|
903
|
+
....: main = dict(default='pizza', description='Main meal',
|
|
904
|
+
....: values=dict(pizza='thick crust', pasta='penne arrabiata'),
|
|
905
|
+
....: case_sensitive=False)
|
|
906
|
+
....: dessert = dict(default='espresso', description='Dessert',
|
|
907
|
+
....: values=dict(espresso='life begins again',
|
|
908
|
+
....: cake='waist begins again',
|
|
909
|
+
....: cream='fluffy white stuff'))
|
|
910
|
+
....: tip = dict(default=10, description='Reward for good service',
|
|
911
|
+
....: checker=lambda tip: tip in range(20))
|
|
912
|
+
sage: Menu.options
|
|
913
|
+
Current options for menu
|
|
914
|
+
- dessert: espresso
|
|
915
|
+
- entree: soup
|
|
916
|
+
- main: pizza
|
|
917
|
+
- tip: 10
|
|
918
|
+
sage: Menu.options(entree='s') # unambiguous abbreviations are allowed
|
|
919
|
+
sage: Menu.options(t=15)
|
|
920
|
+
sage: (Menu.options['tip'], Menu.options('t'))
|
|
921
|
+
(15, 15)
|
|
922
|
+
sage: Menu.options()
|
|
923
|
+
Current options for menu
|
|
924
|
+
- dessert: espresso
|
|
925
|
+
- entree: soup
|
|
926
|
+
- main: pizza
|
|
927
|
+
- tip: 15
|
|
928
|
+
sage: Menu.options._reset(); Menu.options()
|
|
929
|
+
Current options for menu
|
|
930
|
+
- dessert: espresso
|
|
931
|
+
- entree: soup
|
|
932
|
+
- main: pizza
|
|
933
|
+
- tip: 10
|
|
934
|
+
sage: Menu.options.tip=40
|
|
935
|
+
Traceback (most recent call last):
|
|
936
|
+
...
|
|
937
|
+
ValueError: 40 is not a valid value for tip in the options for menu
|
|
938
|
+
sage: Menu.options(m='p') # ambiguous abbreviations are not allowed
|
|
939
|
+
Traceback (most recent call last):
|
|
940
|
+
...
|
|
941
|
+
ValueError: p is not a valid value for main in the options for menu
|
|
942
|
+
|
|
943
|
+
The documentation for the options class is automatically generated from the
|
|
944
|
+
information which specifies the options:
|
|
945
|
+
|
|
946
|
+
.. CODE-BLOCK:: text
|
|
947
|
+
|
|
948
|
+
Fancy documentation
|
|
949
|
+
-------------------
|
|
950
|
+
|
|
951
|
+
OPTIONS:
|
|
952
|
+
|
|
953
|
+
- dessert: (default: espresso)
|
|
954
|
+
Dessert
|
|
955
|
+
|
|
956
|
+
- ``cake`` -- waist begins again
|
|
957
|
+
- ``cream`` -- fluffy white stuff
|
|
958
|
+
- ``espresso`` -- life begins again
|
|
959
|
+
|
|
960
|
+
- entree: (default: soup)
|
|
961
|
+
The first course of a meal
|
|
962
|
+
|
|
963
|
+
- ``bread`` -- oven baked
|
|
964
|
+
- ``rye`` -- alias for bread
|
|
965
|
+
- ``soup`` -- soup of the day
|
|
966
|
+
|
|
967
|
+
- main: (default: pizza)
|
|
968
|
+
Main meal
|
|
969
|
+
|
|
970
|
+
- ``pasta`` -- penne arrabiata
|
|
971
|
+
- ``pizza`` -- thick crust
|
|
972
|
+
|
|
973
|
+
- tip: (default: 10)
|
|
974
|
+
Reward for good service
|
|
975
|
+
|
|
976
|
+
End of Fancy documentation
|
|
977
|
+
|
|
978
|
+
See :class:`~sage.structure.global_options.GlobalOptions` for more features of these options.
|
|
979
|
+
|
|
980
|
+
The possible values for an individual option can be obtained by
|
|
981
|
+
(trying to) set it equal to '?'::
|
|
982
|
+
|
|
983
|
+
sage: Menu.options(des='?')
|
|
984
|
+
- ``dessert`` -- (default: ``espresso``)
|
|
985
|
+
Dessert
|
|
986
|
+
<BLANKLINE>
|
|
987
|
+
- ``cake`` -- waist begins again
|
|
988
|
+
- ``cream`` -- fluffy white stuff
|
|
989
|
+
- ``espresso`` -- life begins again
|
|
990
|
+
<BLANKLINE>
|
|
991
|
+
Current value: espresso
|
|
992
|
+
"""
|
|
993
|
+
__name__ = 'options'
|
|
994
|
+
|
|
995
|
+
def __init__(self, NAME=None, module='', option_class='', doc='', end_doc='', **options):
|
|
996
|
+
r"""
|
|
997
|
+
Initialize ``self``.
|
|
998
|
+
|
|
999
|
+
TESTS::
|
|
1000
|
+
|
|
1001
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1002
|
+
sage: class menu(GlobalOptions):
|
|
1003
|
+
....: entree = dict(default='soup',
|
|
1004
|
+
....: description='The first course of a meal',
|
|
1005
|
+
....: values=dict(soup='soup of the day', bread='oven baked'),
|
|
1006
|
+
....: alias=dict(rye='bread'))
|
|
1007
|
+
....: appetizer = dict(alt_name='entree')
|
|
1008
|
+
....: main = dict(default='pizza', description='Main meal',
|
|
1009
|
+
....: values=dict(pizza='thick crust', pasta='penne arrabiata'),
|
|
1010
|
+
....: case_sensitive=False)
|
|
1011
|
+
....: dessert = dict(default='espresso', description='Dessert',
|
|
1012
|
+
....: values=dict(espresso='life begins again',
|
|
1013
|
+
....: cake='waist begins again',
|
|
1014
|
+
....: cream='fluffy white stuff'))
|
|
1015
|
+
....: tip = dict(default=10, description='Reward for good service',
|
|
1016
|
+
....: checker=lambda tip: tip in range(20))
|
|
1017
|
+
sage: menu._name # Default name is class name
|
|
1018
|
+
'menu'
|
|
1019
|
+
sage: class specials(GlobalOptions):
|
|
1020
|
+
....: entree = dict(link_to=(menu, 'entree'))
|
|
1021
|
+
....: main_specials = dict(default='salmon', description='main course specials',
|
|
1022
|
+
....: values=dict(salmon='a fish', crab='Sebastian'))
|
|
1023
|
+
sage: specials['entree'] = 'rye'
|
|
1024
|
+
sage: menu['entree']
|
|
1025
|
+
'bread'
|
|
1026
|
+
|
|
1027
|
+
sage: class alias_test(GlobalOptions):
|
|
1028
|
+
....: "Test aliases with case sensitivity"
|
|
1029
|
+
....: test_opt = dict(default='Upper',
|
|
1030
|
+
....: description = 'Starts with an uppercase',
|
|
1031
|
+
....: values = dict(Upper="Starts with uppercase",
|
|
1032
|
+
....: lower="only lowercase"),
|
|
1033
|
+
....: case_sensitive = False,
|
|
1034
|
+
....: alias = dict(UpperAlias='Upper', lower_alias='lower'))
|
|
1035
|
+
sage: alias_test['test_opt'] = 'Lower_Alias'
|
|
1036
|
+
sage: alias_test['test_opt']
|
|
1037
|
+
'lower'
|
|
1038
|
+
sage: alias_test['test_opt'] = 'upperalias'
|
|
1039
|
+
sage: alias_test['test_opt']
|
|
1040
|
+
'Upper'
|
|
1041
|
+
"""
|
|
1042
|
+
if NAME is None:
|
|
1043
|
+
# Require a "name" keyword in **options
|
|
1044
|
+
try:
|
|
1045
|
+
NAME = options.pop("name")
|
|
1046
|
+
except KeyError:
|
|
1047
|
+
raise TypeError("GlobalOptions() is missing keyword argument 'name'")
|
|
1048
|
+
self._name = NAME
|
|
1049
|
+
|
|
1050
|
+
# initialise the various dictionaries used by GlobalOptions
|
|
1051
|
+
self._alias = {} # a dictionary of alias for the values of some options
|
|
1052
|
+
self._alt_names = {} # a dictionary of alternative names for some options
|
|
1053
|
+
self._case_sensitive = {} # a dictionary of booleans indicating to check case sensitivity
|
|
1054
|
+
self._checker = {} # a dictionary of validity checkers for each option
|
|
1055
|
+
self.__default_value = {} # a dictionary of the default options
|
|
1056
|
+
self._display_values = {} # a dictionary of the output of the values
|
|
1057
|
+
self._doc = {} # a dictionary of doc strings, forced by the linked options
|
|
1058
|
+
self._legal_values = {} # a dictionary of lists of the legal values for each option
|
|
1059
|
+
self._linked_value = {} # a dictionary of linked to other global options as (link, linked_option)
|
|
1060
|
+
self._setter = {} # a dictionary of the list of setters
|
|
1061
|
+
self._value = {} # a dictionary of the current options
|
|
1062
|
+
for option in options:
|
|
1063
|
+
self._add_option(option, options[option])
|
|
1064
|
+
|
|
1065
|
+
self._options_module = module
|
|
1066
|
+
self._option_class = option_class or self._name
|
|
1067
|
+
|
|
1068
|
+
# If the instance dict has a __doc__ attribute, use that as
|
|
1069
|
+
# docstring.
|
|
1070
|
+
try:
|
|
1071
|
+
self._docstring = dedent(self.__dict__.pop("__doc__"))
|
|
1072
|
+
except KeyError:
|
|
1073
|
+
self._docstring = "@OPTIONS@"
|
|
1074
|
+
|
|
1075
|
+
# Finally, we build the doc string for the options
|
|
1076
|
+
# First we strip common white space off the front of doc and end_doc
|
|
1077
|
+
if doc:
|
|
1078
|
+
self._docstring = dedent(doc) + "\n\n" + self._docstring
|
|
1079
|
+
|
|
1080
|
+
if end_doc:
|
|
1081
|
+
self._docstring = self._docstring + "\n\n" + dedent(end_doc)
|
|
1082
|
+
|
|
1083
|
+
# Add docstring footer
|
|
1084
|
+
self._docstring += "\nSee :class:`~sage.structure.global_options.GlobalOptions` for more features of these options."
|
|
1085
|
+
|
|
1086
|
+
def __repr__(self):
|
|
1087
|
+
r"""
|
|
1088
|
+
Return a string representation for this collection of options.
|
|
1089
|
+
|
|
1090
|
+
EXAMPLES::
|
|
1091
|
+
|
|
1092
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1093
|
+
sage: class FoodOptions(GlobalOptions):
|
|
1094
|
+
....: NAME = 'daily meal'
|
|
1095
|
+
....: food = dict(default='apple', values=dict(apple='a fruit', pair='of what?'))
|
|
1096
|
+
....: drink = dict(default='water', values=dict(water='a drink', coffee='a lifestyle'))
|
|
1097
|
+
sage: FoodOptions
|
|
1098
|
+
Current options for daily meal
|
|
1099
|
+
- drink: water
|
|
1100
|
+
- food: apple
|
|
1101
|
+
"""
|
|
1102
|
+
options = list(self._value) + list(self._linked_value)
|
|
1103
|
+
for x in self._alt_names:
|
|
1104
|
+
options.remove(x)
|
|
1105
|
+
if not options:
|
|
1106
|
+
return 'Current options for {}'.format(self._name)
|
|
1107
|
+
|
|
1108
|
+
options.sort()
|
|
1109
|
+
width = 1 + max(len(option) for option in options)
|
|
1110
|
+
txt = '\n'.join(' - {:{}} {}'.format(option + ':', width, self[option])
|
|
1111
|
+
for option in options)
|
|
1112
|
+
return 'Current options for {}\n{}'.format(self._name, txt)
|
|
1113
|
+
|
|
1114
|
+
def __call__(self, *get_value, **set_value):
|
|
1115
|
+
r"""
|
|
1116
|
+
Get or set value of the option ``option``.
|
|
1117
|
+
|
|
1118
|
+
EXAMPLES::
|
|
1119
|
+
|
|
1120
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1121
|
+
sage: class FoodOptions(GlobalOptions):
|
|
1122
|
+
....: NAME = 'daily meal'
|
|
1123
|
+
....: food = dict(default='apple', values=dict(apple='a fruit', pair='of what?'))
|
|
1124
|
+
....: drink = dict(default='water', values=dict(water='a drink', coffee='a lifestyle'))
|
|
1125
|
+
....: beverage = dict(alt_name='drink')
|
|
1126
|
+
sage: FoodOptions()
|
|
1127
|
+
Current options for daily meal
|
|
1128
|
+
- drink: water
|
|
1129
|
+
- food: apple
|
|
1130
|
+
sage: FoodOptions('food')
|
|
1131
|
+
'apple'
|
|
1132
|
+
sage: FoodOptions(food='pair'); FoodOptions()
|
|
1133
|
+
Current options for daily meal
|
|
1134
|
+
- drink: water
|
|
1135
|
+
- food: pair
|
|
1136
|
+
sage: FoodOptions('beverage')
|
|
1137
|
+
'water'
|
|
1138
|
+
sage: FoodOptions(food='apple', drink='coffee'); FoodOptions()
|
|
1139
|
+
Current options for daily meal
|
|
1140
|
+
- drink: coffee
|
|
1141
|
+
- food: apple
|
|
1142
|
+
"""
|
|
1143
|
+
if not get_value and not set_value:
|
|
1144
|
+
return self
|
|
1145
|
+
|
|
1146
|
+
if get_value:
|
|
1147
|
+
# use __getitem__ to return these options
|
|
1148
|
+
if len(get_value) == 1:
|
|
1149
|
+
return self[get_value[0]]
|
|
1150
|
+
else:
|
|
1151
|
+
return [self[option] for option in get_value]
|
|
1152
|
+
|
|
1153
|
+
# use __setitem__ to set these options
|
|
1154
|
+
if set_value:
|
|
1155
|
+
for option in set_value:
|
|
1156
|
+
self[option] = set_value[option]
|
|
1157
|
+
|
|
1158
|
+
def __getitem__(self, option):
|
|
1159
|
+
r"""
|
|
1160
|
+
Return the current value of the option ``option``.
|
|
1161
|
+
|
|
1162
|
+
EXAMPLES::
|
|
1163
|
+
|
|
1164
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1165
|
+
sage: class FoodOptions(GlobalOptions):
|
|
1166
|
+
....: NAME = 'daily meal'
|
|
1167
|
+
....: food = dict(default='apple', values=dict(apple='a fruit', pair='of what?'))
|
|
1168
|
+
....: drink = dict(default='water', values=dict(water='a drink', coffee='a lifestyle'))
|
|
1169
|
+
sage: FoodOptions['drink']
|
|
1170
|
+
'water'
|
|
1171
|
+
sage: FoodOptions['d']
|
|
1172
|
+
'water'
|
|
1173
|
+
"""
|
|
1174
|
+
option = self._match_option(option)
|
|
1175
|
+
if option in self._linked_value:
|
|
1176
|
+
link, linked_opt = self._linked_value[option]
|
|
1177
|
+
return link[linked_opt]
|
|
1178
|
+
elif option in self._value:
|
|
1179
|
+
if option in self._display_values:
|
|
1180
|
+
return self._display_values[option][self._value[option]]
|
|
1181
|
+
return self._value[option]
|
|
1182
|
+
|
|
1183
|
+
def __setitem__(self, option, value):
|
|
1184
|
+
r"""
|
|
1185
|
+
The ``__setitem__`` method is used to change the current values of the
|
|
1186
|
+
options. It also checks that the supplied options are valid and changes
|
|
1187
|
+
any alias to its generic value.
|
|
1188
|
+
|
|
1189
|
+
EXAMPLES::
|
|
1190
|
+
|
|
1191
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1192
|
+
sage: class FoodOptions(GlobalOptions):
|
|
1193
|
+
....: NAME = 'daily meal'
|
|
1194
|
+
....: food = dict(default='apple', values=dict(apple='a fruit', pair='of what?'))
|
|
1195
|
+
....: drink = dict(default='water', values=dict(water='a drink', coffee='a lifestyle'))
|
|
1196
|
+
sage: FoodOptions['drink']='coffee'; FoodOptions()
|
|
1197
|
+
Current options for daily meal
|
|
1198
|
+
- drink: coffee
|
|
1199
|
+
- food: apple
|
|
1200
|
+
sage: FoodOptions(drink='w'); FoodOptions()
|
|
1201
|
+
Current options for daily meal
|
|
1202
|
+
- drink: water
|
|
1203
|
+
- food: apple
|
|
1204
|
+
sage: FoodOptions(drink='?')
|
|
1205
|
+
- ``drink`` -- (default: ``water``)
|
|
1206
|
+
<BLANKLINE>
|
|
1207
|
+
- ``coffee`` -- a lifestyle
|
|
1208
|
+
- ``water`` -- a drink
|
|
1209
|
+
<BLANKLINE>
|
|
1210
|
+
Current value: water
|
|
1211
|
+
"""
|
|
1212
|
+
option = self._match_option(option)
|
|
1213
|
+
if not callable(value):
|
|
1214
|
+
value = self._match_value(option, value)
|
|
1215
|
+
|
|
1216
|
+
if value == '?': # return help
|
|
1217
|
+
print('%s\nCurrent value: %s' % (self._doc[option], self[option]))
|
|
1218
|
+
return # we do not want to call the setter below
|
|
1219
|
+
|
|
1220
|
+
elif option in self._linked_value:
|
|
1221
|
+
link, linked_opt = self._linked_value[option]
|
|
1222
|
+
link[linked_opt] = value
|
|
1223
|
+
|
|
1224
|
+
else:
|
|
1225
|
+
self._value[option] = value
|
|
1226
|
+
|
|
1227
|
+
if option in self._setter:
|
|
1228
|
+
# if a setter function exists then call it with the associated
|
|
1229
|
+
# class, option and value
|
|
1230
|
+
self._setter[option](option, value)
|
|
1231
|
+
|
|
1232
|
+
def _instancedoc_(self):
|
|
1233
|
+
r"""
|
|
1234
|
+
Return the docstring for the options class ``self``.
|
|
1235
|
+
|
|
1236
|
+
EXAMPLES::
|
|
1237
|
+
|
|
1238
|
+
sage: print(Partitions.options.__doc__)
|
|
1239
|
+
<BLANKLINE>
|
|
1240
|
+
Set and display the global options for elements of the partition,
|
|
1241
|
+
skew partition, and partition tuple classes. If no parameters are
|
|
1242
|
+
set, then the function returns a copy of the options dictionary.
|
|
1243
|
+
<BLANKLINE>
|
|
1244
|
+
The ``options`` to partitions can be accessed as the method
|
|
1245
|
+
:obj:`Partitions.options` of :class:`Partitions` and
|
|
1246
|
+
related parent classes.
|
|
1247
|
+
<BLANKLINE>
|
|
1248
|
+
<BLANKLINE>
|
|
1249
|
+
OPTIONS:
|
|
1250
|
+
<BLANKLINE>
|
|
1251
|
+
- ``convention`` -- (default: ``English``)
|
|
1252
|
+
Sets the convention used for displaying tableaux and partitions
|
|
1253
|
+
<BLANKLINE>
|
|
1254
|
+
- ``English`` -- use the English convention
|
|
1255
|
+
- ``French`` -- use the French convention
|
|
1256
|
+
...
|
|
1257
|
+
|
|
1258
|
+
TESTS::
|
|
1259
|
+
|
|
1260
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1261
|
+
sage: print(GlobalOptions.__doc__)
|
|
1262
|
+
<BLANKLINE>
|
|
1263
|
+
The :class:`GlobalOptions` class is a generic class for setting...
|
|
1264
|
+
"""
|
|
1265
|
+
options = 'OPTIONS:\n\n{}\n\n'.format('\n'.join(self._doc[opt] for opt in sorted(self._doc)))
|
|
1266
|
+
return self._docstring.replace("@OPTIONS@", options)
|
|
1267
|
+
|
|
1268
|
+
def __setattr__(self, name, value=None):
|
|
1269
|
+
r"""
|
|
1270
|
+
Set the attribute ``name`` of the option class ``self`` equal to
|
|
1271
|
+
``value``, if the attribute ``name`` exists.
|
|
1272
|
+
|
|
1273
|
+
As the attributes of an option class are the actual options we need
|
|
1274
|
+
to be able to "trap" invalid options in a sensible way. We do this
|
|
1275
|
+
by sending any "non-standard" to :meth:`__setitem__` for processing.
|
|
1276
|
+
|
|
1277
|
+
EXAMPLES::
|
|
1278
|
+
|
|
1279
|
+
sage: Partitions.options.display = 'exp'
|
|
1280
|
+
sage: Partitions.options.dispplay = 'list'
|
|
1281
|
+
Traceback (most recent call last):
|
|
1282
|
+
...
|
|
1283
|
+
ValueError: dispplay is not an option for Partitions
|
|
1284
|
+
sage: Partitions.options._reset()
|
|
1285
|
+
"""
|
|
1286
|
+
# Underscore, and "special", attributes are set using type.__setattr__
|
|
1287
|
+
if name[0] == '_' or name in ['reset', 'dispatch', 'default_value']:
|
|
1288
|
+
return super().__setattr__(name, value)
|
|
1289
|
+
|
|
1290
|
+
# General case: redirect to __setitem__
|
|
1291
|
+
self[name] = value
|
|
1292
|
+
|
|
1293
|
+
def __setstate__(self, state):
|
|
1294
|
+
r"""
|
|
1295
|
+
This is a custom :meth:`__setstate__` method for unpickling instances of
|
|
1296
|
+
the :class:`GlobalOptions` class.
|
|
1297
|
+
|
|
1298
|
+
The :meth:`__getstate__` method returns a dictionary with an
|
|
1299
|
+
``options_class`` key which identifies the "parent" class for the options.
|
|
1300
|
+
This is then used to unpickle the options class.
|
|
1301
|
+
|
|
1302
|
+
EXAMPLES::
|
|
1303
|
+
|
|
1304
|
+
sage: # needs sage.combinat
|
|
1305
|
+
sage: Partitions.options()
|
|
1306
|
+
Current options for Partitions
|
|
1307
|
+
- convention: English
|
|
1308
|
+
- diagram_str: *
|
|
1309
|
+
- display: list
|
|
1310
|
+
- latex: young_diagram
|
|
1311
|
+
- latex_diagram_str: \ast
|
|
1312
|
+
sage: Partitions.options.convention = "French"
|
|
1313
|
+
sage: pickle = dumps(Partitions.options)
|
|
1314
|
+
sage: Partitions.options._reset() # reset options
|
|
1315
|
+
sage: loads(pickle) # indirect doctest
|
|
1316
|
+
Current options for Partitions
|
|
1317
|
+
- convention: French
|
|
1318
|
+
- diagram_str: *
|
|
1319
|
+
- display: list
|
|
1320
|
+
- latex: young_diagram
|
|
1321
|
+
- latex_diagram_str: \ast
|
|
1322
|
+
sage: Partitions.options._reset()
|
|
1323
|
+
"""
|
|
1324
|
+
# open the options for the corresponding "parent" and copy all of
|
|
1325
|
+
# the data from its options class into unpickle
|
|
1326
|
+
options_class = getattr(import_module(state['options_module']), state['option_class'])
|
|
1327
|
+
unpickle = options_class.options
|
|
1328
|
+
state.pop('option_class')
|
|
1329
|
+
state.pop('options_module')
|
|
1330
|
+
for setting in unpickle.__dict__:
|
|
1331
|
+
self.__dict__[setting] = unpickle.__dict__[setting]
|
|
1332
|
+
|
|
1333
|
+
# reset the options in ``self`` to their defaults
|
|
1334
|
+
self._reset()
|
|
1335
|
+
# apply the options stored in state
|
|
1336
|
+
for opt in state:
|
|
1337
|
+
self[opt] = state[opt]
|
|
1338
|
+
options_class.options = self
|
|
1339
|
+
|
|
1340
|
+
def __getstate__(self):
|
|
1341
|
+
r"""
|
|
1342
|
+
Return a dictionary that can be used to pickle an instance of a
|
|
1343
|
+
:class:`GlobalOptions` class.
|
|
1344
|
+
|
|
1345
|
+
This is called by :func:`pickle_GlobalOptions`.
|
|
1346
|
+
|
|
1347
|
+
Typically instances of :class:`GlobalOptions` are
|
|
1348
|
+
complicated to create, so they do no pickle. If the options are
|
|
1349
|
+
associated to "parent" class then it is possible to create the
|
|
1350
|
+
default version of the class and then add the non-default
|
|
1351
|
+
settings on top of this. This method returns a dictionary of the
|
|
1352
|
+
non-default options that can then be used to unpickle an
|
|
1353
|
+
instance of the option using :func:`unpickle_GlobalOptions`.
|
|
1354
|
+
|
|
1355
|
+
EXAMPLES::
|
|
1356
|
+
|
|
1357
|
+
sage: Partitions.options._reset()
|
|
1358
|
+
sage: Partitions.options.__getstate__()
|
|
1359
|
+
{'convention': 'English',
|
|
1360
|
+
'option_class': 'Partitions',
|
|
1361
|
+
'options_module': 'sage.combinat.partition'}
|
|
1362
|
+
"""
|
|
1363
|
+
|
|
1364
|
+
# options classes can be pickled only if they are the options for an
|
|
1365
|
+
# associated "parent" class that lives in self._module
|
|
1366
|
+
|
|
1367
|
+
if not self._options_module:
|
|
1368
|
+
pickleable = False
|
|
1369
|
+
else:
|
|
1370
|
+
opt_mod = import_module(self._options_module)
|
|
1371
|
+
pickleable = hasattr(opt_mod, self._name) and hasattr(getattr(opt_mod, self._name), 'options')
|
|
1372
|
+
|
|
1373
|
+
if not pickleable:
|
|
1374
|
+
raise PicklingError('%s cannot be pickled because it is not associated with a class' % self)
|
|
1375
|
+
|
|
1376
|
+
pickle = {'option_class': self._option_class, 'options_module': self._options_module}
|
|
1377
|
+
for opt in self._value:
|
|
1378
|
+
if opt not in self._alt_names and self[opt] != self.__default_value[opt]:
|
|
1379
|
+
pickle[opt] = self[opt]
|
|
1380
|
+
for opt in self._linked_value:
|
|
1381
|
+
link, linked_opt = self._linked_value[opt]
|
|
1382
|
+
if opt not in self._alt_names and link[opt] != link.__default_value[opt]:
|
|
1383
|
+
pickle[opt] = self[opt]
|
|
1384
|
+
return pickle
|
|
1385
|
+
|
|
1386
|
+
def __eq__(self, other):
|
|
1387
|
+
r"""
|
|
1388
|
+
Two options classes are equal if they return the same :meth:`__getstate__`.
|
|
1389
|
+
|
|
1390
|
+
EXAMPLES::
|
|
1391
|
+
|
|
1392
|
+
sage: Partitions.options == PartitionsGreatestLE.options # indirect doctest
|
|
1393
|
+
True
|
|
1394
|
+
sage: Partitions.options == Tableaux.options # needs sage.combinat
|
|
1395
|
+
False
|
|
1396
|
+
"""
|
|
1397
|
+
return isinstance(other, GlobalOptions) and self.__getstate__() == other.__getstate__()
|
|
1398
|
+
|
|
1399
|
+
def _add_option(self, option, specifications):
|
|
1400
|
+
r"""
|
|
1401
|
+
Add an option.
|
|
1402
|
+
|
|
1403
|
+
INPUT:
|
|
1404
|
+
|
|
1405
|
+
- ``option`` -- string
|
|
1406
|
+
- ``specifications`` -- dictionary
|
|
1407
|
+
|
|
1408
|
+
.. SEEALSO::
|
|
1409
|
+
|
|
1410
|
+
:class:`GlobalOptions` for a description of the required
|
|
1411
|
+
``specifications``.
|
|
1412
|
+
"""
|
|
1413
|
+
if not isinstance(specifications, dict):
|
|
1414
|
+
raise TypeError("expected dict as specification of %r, got %r" % (option, specifications))
|
|
1415
|
+
|
|
1416
|
+
doc = {} # will be used to build the doc string
|
|
1417
|
+
self._case_sensitive[option] = True # ``True`` by default
|
|
1418
|
+
self._legal_values[option] = []
|
|
1419
|
+
for spec in sorted(specifications): # NB: options processed alphabetically!
|
|
1420
|
+
if spec == 'alias':
|
|
1421
|
+
self._alias[option] = specifications[spec]
|
|
1422
|
+
self._legal_values[option] += list(specifications[spec])
|
|
1423
|
+
for opt in specifications[spec]:
|
|
1424
|
+
doc[opt] = 'alias for ``%s``' % specifications[spec][opt]
|
|
1425
|
+
elif spec == 'alt_name':
|
|
1426
|
+
self._alt_names[option] = specifications[spec]
|
|
1427
|
+
self._linked_value[option] = (self, specifications[spec])
|
|
1428
|
+
doc = '- ``%s`` -- alternative name for ``%s``' % (option, specifications[spec].lower())
|
|
1429
|
+
elif spec == 'case_sensitive':
|
|
1430
|
+
if not specifications[spec]:
|
|
1431
|
+
for opt in self._legal_values:
|
|
1432
|
+
self._display_values[option] = {val.lower(): val for val in self._legal_values[option]}
|
|
1433
|
+
self._legal_values[option] = [val.lower() for val in self._legal_values[option]]
|
|
1434
|
+
if option in self._alias:
|
|
1435
|
+
self._alias[option] = {k.lower(): v.lower()
|
|
1436
|
+
for k, v in self._alias[option].items()}
|
|
1437
|
+
self._case_sensitive[option] = bool(specifications[spec])
|
|
1438
|
+
elif spec == 'checker':
|
|
1439
|
+
if not callable(specifications[spec]):
|
|
1440
|
+
raise ValueError('the checker for %s must be callable' % option)
|
|
1441
|
+
self._checker[option] = specifications[spec]
|
|
1442
|
+
elif spec == 'default':
|
|
1443
|
+
self.__default_value[option] = specifications[spec]
|
|
1444
|
+
elif spec == 'link_to':
|
|
1445
|
+
if (isinstance(specifications[spec], tuple) and
|
|
1446
|
+
len(specifications[spec]) == 2 and
|
|
1447
|
+
isinstance(specifications[spec][0], GlobalOptions)):
|
|
1448
|
+
link, linked_opt = specifications['link_to'] # for sanity
|
|
1449
|
+
if linked_opt in link._value:
|
|
1450
|
+
self._linked_value[option] = specifications['link_to']
|
|
1451
|
+
link, linked_opt = specifications['link_to'] # for sanity
|
|
1452
|
+
doc = link._doc[linked_opt]
|
|
1453
|
+
elif linked_opt in link._linked_value:
|
|
1454
|
+
self._linked_value[option] = link._linked_value[linked_opt]
|
|
1455
|
+
doc = link._doc[linked_opt]
|
|
1456
|
+
else:
|
|
1457
|
+
raise ValueError("could not find link to {1} in {0}".format(*specifications[spec]))
|
|
1458
|
+
else:
|
|
1459
|
+
raise ValueError("linked options must be specified as a string: 'linked_option' or a tuple: (link,linked_option)")
|
|
1460
|
+
elif spec == 'setter':
|
|
1461
|
+
if callable(specifications[spec]):
|
|
1462
|
+
self._setter[option] = specifications[spec]
|
|
1463
|
+
else:
|
|
1464
|
+
raise ValueError('the setter for %s must be a function' % option)
|
|
1465
|
+
elif spec == 'values':
|
|
1466
|
+
for val in specifications[spec]:
|
|
1467
|
+
doc[val] = specifications[spec][val]
|
|
1468
|
+
doc.update(specifications[spec])
|
|
1469
|
+
if self._case_sensitive[option]:
|
|
1470
|
+
self._legal_values[option] += list(specifications[spec])
|
|
1471
|
+
self._display_values[option] = {val: val for val in specifications[spec]}
|
|
1472
|
+
else:
|
|
1473
|
+
self._legal_values[option] += [val.lower() for val in specifications[spec]]
|
|
1474
|
+
self._display_values[option] = {val.lower(): val for val in specifications[spec]}
|
|
1475
|
+
elif spec != 'description':
|
|
1476
|
+
raise ValueError('Initialization error in Global options for %s: %s not recognized!' % (self._name, spec))
|
|
1477
|
+
|
|
1478
|
+
# now build the doc string for this option
|
|
1479
|
+
if doc == {} and 'description' not in specifications:
|
|
1480
|
+
raise ValueError('no documentation specified for %s in the options for %s' % (option, self._name))
|
|
1481
|
+
|
|
1482
|
+
# first a necessary hack to initialise the option in self._doc because __setitem__ calls _match_option
|
|
1483
|
+
self._doc[option] = ''
|
|
1484
|
+
if option in self._linked_value:
|
|
1485
|
+
self._doc[option] = doc
|
|
1486
|
+
else:
|
|
1487
|
+
width = max(len(v) for v in doc) + 4 if doc != {} else 4
|
|
1488
|
+
if len(doc) > 0:
|
|
1489
|
+
self._doc[option] = '- ``{}`` -- (default: ``{}``)\n{}\n{}\n'.format(
|
|
1490
|
+
option, self._default_value(option),
|
|
1491
|
+
' %s\n' % specifications['description'] if 'description' in specifications else '',
|
|
1492
|
+
'\n'.join(' - {:{}} -- {}'.format('``' + val + '``', width, doc[val])
|
|
1493
|
+
for val in sorted(doc)))
|
|
1494
|
+
else:
|
|
1495
|
+
self._doc[option] = '- ``{}`` -- (default: ``{}``)\n{}'.format(
|
|
1496
|
+
option, self._default_value(option),
|
|
1497
|
+
' %s\n' % specifications['description'] if 'description' in specifications else '')
|
|
1498
|
+
|
|
1499
|
+
# sanity check for non-linked options
|
|
1500
|
+
if option not in self._linked_value:
|
|
1501
|
+
if 'default' not in specifications:
|
|
1502
|
+
raise ValueError('a default value for %s must be given' % option)
|
|
1503
|
+
|
|
1504
|
+
if not (option in self._checker or option in self._legal_values):
|
|
1505
|
+
raise ValueError('a value checker or a list of valid values for %s must be given' % option)
|
|
1506
|
+
|
|
1507
|
+
# finally, set, check and process the default value using __setitem__
|
|
1508
|
+
self[option] = self.__default_value[option]
|
|
1509
|
+
self.__default_value[option] = self._value[option] # in case the default is an alias
|
|
1510
|
+
|
|
1511
|
+
# Build getters and setters for this option. As we have
|
|
1512
|
+
# overridden __setattr__, we call object.__setattr__ directly
|
|
1513
|
+
super().__setattr__(option, Option(self, option))
|
|
1514
|
+
|
|
1515
|
+
def _match_option(self, option):
|
|
1516
|
+
r"""
|
|
1517
|
+
Check whether ``option`` is the name of an option of ``self``, or a
|
|
1518
|
+
prefix thereof.
|
|
1519
|
+
|
|
1520
|
+
INPUT:
|
|
1521
|
+
|
|
1522
|
+
- ``option`` -- string
|
|
1523
|
+
|
|
1524
|
+
EXAMPLES::
|
|
1525
|
+
|
|
1526
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1527
|
+
sage: class FoodOptions(GlobalOptions):
|
|
1528
|
+
....: NAME = 'daily meal'
|
|
1529
|
+
....: food = dict(default='apple', values=dict(apple='a fruit', pair='of what?'))
|
|
1530
|
+
....: drink = dict(default='water', values=dict(water='a drink', coffee='a lifestyle'))
|
|
1531
|
+
sage: FoodOptions('food') # indirect doctest
|
|
1532
|
+
'apple'
|
|
1533
|
+
sage: FoodOptions('f')
|
|
1534
|
+
'apple'
|
|
1535
|
+
"""
|
|
1536
|
+
if option in self._doc:
|
|
1537
|
+
return option
|
|
1538
|
+
|
|
1539
|
+
# a lower case version of the option
|
|
1540
|
+
loption = option.lower()
|
|
1541
|
+
|
|
1542
|
+
# as it is not an option try and match it with a prefix to an option,
|
|
1543
|
+
# without checking case using the fact that the keys of self._doc is a
|
|
1544
|
+
# list of the options, both normal and linked
|
|
1545
|
+
matches = [opt for opt in self._doc if opt.lower().startswith(loption)]
|
|
1546
|
+
if matches and all(m.startswith(matches[0]) for m in matches):
|
|
1547
|
+
return matches[0]
|
|
1548
|
+
elif len(matches) > 1:
|
|
1549
|
+
# as there is more than one match check case as well
|
|
1550
|
+
matches = [mat for mat in matches if mat.startswith(option)]
|
|
1551
|
+
if matches and all(m.startswith(matches[0]) for m in matches):
|
|
1552
|
+
return matches[0]
|
|
1553
|
+
else:
|
|
1554
|
+
raise ValueError('%s is an ambiguous option for %s' % (option, self._name))
|
|
1555
|
+
|
|
1556
|
+
# if we are still here this is not a good option!
|
|
1557
|
+
raise ValueError('%s is not an option for %s' % (option, self._name))
|
|
1558
|
+
|
|
1559
|
+
def _match_value(self, option, value):
|
|
1560
|
+
r"""
|
|
1561
|
+
Check whether ``value`` is a valid value for ``option``.
|
|
1562
|
+
|
|
1563
|
+
INPUT:
|
|
1564
|
+
|
|
1565
|
+
- ``option`` -- string: the name of an option, or prefix thereof
|
|
1566
|
+
- ``value`` -- a value or ``'?'``
|
|
1567
|
+
|
|
1568
|
+
EXAMPLES::
|
|
1569
|
+
|
|
1570
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1571
|
+
sage: class FoodOptions(GlobalOptions):
|
|
1572
|
+
....: NAME = 'daily meal'
|
|
1573
|
+
....: food = dict(default='apple', values=dict(apple='a fruit', pair='of what?'))
|
|
1574
|
+
....: drink = dict(default='water', values=dict(water='a drink', wine='a lifestyle'))
|
|
1575
|
+
sage: FoodOptions(f='a') # indirect doctest
|
|
1576
|
+
sage: FoodOptions('f')
|
|
1577
|
+
'apple'
|
|
1578
|
+
sage: FoodOptions(d='wi'); FoodOptions('f')
|
|
1579
|
+
'apple'
|
|
1580
|
+
sage: FoodOptions(d='w')
|
|
1581
|
+
Traceback (most recent call last):
|
|
1582
|
+
...
|
|
1583
|
+
ValueError: w is not a valid value for drink in the options for daily meal
|
|
1584
|
+
"""
|
|
1585
|
+
if value == "?":
|
|
1586
|
+
return value # help on this value
|
|
1587
|
+
|
|
1588
|
+
if option in self._linked_value:
|
|
1589
|
+
link, linked_opt = self._linked_value[option]
|
|
1590
|
+
return link._match_value(linked_opt, value)
|
|
1591
|
+
|
|
1592
|
+
orig_value = value
|
|
1593
|
+
|
|
1594
|
+
# convert to proper case if it is a string
|
|
1595
|
+
if isinstance(value, str) and not self._case_sensitive[option]:
|
|
1596
|
+
value = value.lower()
|
|
1597
|
+
|
|
1598
|
+
if option in self._legal_values:
|
|
1599
|
+
if value in self._legal_values[option]:
|
|
1600
|
+
if option in self._alias and value in self._alias[option]:
|
|
1601
|
+
return self._alias[option][value]
|
|
1602
|
+
return value
|
|
1603
|
+
|
|
1604
|
+
# as it is not a value try and match it with a prefix of a value
|
|
1605
|
+
matches = [val for val in self._legal_values[option] if val.startswith(value)]
|
|
1606
|
+
if matches and all(m.startswith(matches[0]) for m in matches):
|
|
1607
|
+
val = matches[0]
|
|
1608
|
+
if option in self._alias and val in self._alias[option]:
|
|
1609
|
+
return self._alias[option][val]
|
|
1610
|
+
return val
|
|
1611
|
+
|
|
1612
|
+
if option in self._checker and self._checker[option](value):
|
|
1613
|
+
return value
|
|
1614
|
+
|
|
1615
|
+
# if we are still here this is not a good value!
|
|
1616
|
+
|
|
1617
|
+
# replace any value alias with its "real" value
|
|
1618
|
+
if option in self._alias and value in self._alias[option]:
|
|
1619
|
+
orig_value = self._alias[option][value]
|
|
1620
|
+
raise ValueError('%s is not a valid value for %s in the options for %s' % (orig_value, option, self._name))
|
|
1621
|
+
|
|
1622
|
+
def _default_value(self, option):
|
|
1623
|
+
r"""
|
|
1624
|
+
Return the default value of the option.
|
|
1625
|
+
|
|
1626
|
+
EXAMPLES::
|
|
1627
|
+
|
|
1628
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1629
|
+
sage: class FoodOptions(GlobalOptions):
|
|
1630
|
+
....: NAME = 'daily meal'
|
|
1631
|
+
....: food = dict(default='apple', values=dict(apple='a fruit', pair='of what?'))
|
|
1632
|
+
....: drink = dict(default='water', values=dict(water='a drink', coffee='a lifestyle'))
|
|
1633
|
+
sage: FoodOptions._default_value('food')
|
|
1634
|
+
'apple'
|
|
1635
|
+
"""
|
|
1636
|
+
option = self._match_option(option)
|
|
1637
|
+
if option in self.__default_value:
|
|
1638
|
+
return self.__default_value[option]
|
|
1639
|
+
else:
|
|
1640
|
+
link, linked_opt = self._linked_value[option]
|
|
1641
|
+
return link._default_value(linked_opt)
|
|
1642
|
+
|
|
1643
|
+
def _dispatch(self, obj, dispatch_to, option, *args, **kargs):
|
|
1644
|
+
r"""
|
|
1645
|
+
The *dispatchable* options are options which dispatch related methods of
|
|
1646
|
+
the corresponding class - or user defined methods which are passed to
|
|
1647
|
+
:class:`GlobalOptions`. The format for specifying a dispatchable option
|
|
1648
|
+
is to include ``dispatch_to = <option name>`` in the specifications for
|
|
1649
|
+
the options and then to add the options to the (element) class. Each
|
|
1650
|
+
option is then assumed to be a method of the element class with a name
|
|
1651
|
+
of the form ``<option name> + '_' + <current vale for this option'``.
|
|
1652
|
+
These options are called by the element class via::
|
|
1653
|
+
|
|
1654
|
+
return self.options._dispatch(self, dispatch_to, option, *args, **kargs)
|
|
1655
|
+
|
|
1656
|
+
Note that the argument ``self`` is necessary here because the
|
|
1657
|
+
dispatcher is a method of the options class and not of
|
|
1658
|
+
``self``. The value of the option can also be set to a
|
|
1659
|
+
user-defined function, with arguments ``self`` and ``option``;
|
|
1660
|
+
in this case the user's function is called instead.
|
|
1661
|
+
|
|
1662
|
+
EXAMPLES:
|
|
1663
|
+
|
|
1664
|
+
Here is a contrived example::
|
|
1665
|
+
|
|
1666
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1667
|
+
sage: class DelimitedListOptions(GlobalOptions):
|
|
1668
|
+
....: delim=dict(default='b', values={'b':'brackets', 'p':'parentheses'})
|
|
1669
|
+
sage: class DelimitedList(SageObject):
|
|
1670
|
+
....: options = DelimitedListOptions
|
|
1671
|
+
....: def __init__(self, L):
|
|
1672
|
+
....: self._list = L
|
|
1673
|
+
....: def _repr_b(self): return '[%s]' % ','.join('%s'%i for i in self._list)
|
|
1674
|
+
....: def _repr_p(self): return '(%s)' % ','.join('%s'%i for i in self._list)
|
|
1675
|
+
....: def _repr_(self): return self.options._dispatch(self, '_repr_','delim')
|
|
1676
|
+
sage: dlist = DelimitedList([1,2,3]); dlist
|
|
1677
|
+
[1,2,3]
|
|
1678
|
+
sage: dlist.options.delim='p'; dlist
|
|
1679
|
+
(1,2,3)
|
|
1680
|
+
sage: dlist.options(delim=lambda self: '<%s>' % ','.join('%s'%i for i in self._list)); dlist
|
|
1681
|
+
<1,2,3>
|
|
1682
|
+
"""
|
|
1683
|
+
if callable(self._value[option]):
|
|
1684
|
+
try:
|
|
1685
|
+
return self._value[option](obj, *args, **kargs)
|
|
1686
|
+
except TypeError:
|
|
1687
|
+
raise ValueError('the user defined dispatcher function failed!')
|
|
1688
|
+
else:
|
|
1689
|
+
if dispatch_to[-1] == '_':
|
|
1690
|
+
dispatch_to = dispatch_to[:-1]
|
|
1691
|
+
dispatch = getattr(obj, dispatch_to + '_' + self._value[option])
|
|
1692
|
+
return dispatch(*args, **kargs)
|
|
1693
|
+
|
|
1694
|
+
raise ValueError('%s is not a dispatchable option!' % option)
|
|
1695
|
+
|
|
1696
|
+
def _reset(self, option=None):
|
|
1697
|
+
r"""
|
|
1698
|
+
Reset options to their default value.
|
|
1699
|
+
|
|
1700
|
+
INPUT:
|
|
1701
|
+
|
|
1702
|
+
- ``option`` -- (default: ``None``) the name of an option as a string
|
|
1703
|
+
or ``None``. If ``option`` is specified only this option is reset to
|
|
1704
|
+
its default value; otherwise all options are reset.
|
|
1705
|
+
|
|
1706
|
+
EXAMPLES::
|
|
1707
|
+
|
|
1708
|
+
sage: from sage.structure.global_options import GlobalOptions
|
|
1709
|
+
sage: class Meal():
|
|
1710
|
+
....: class options(GlobalOptions):
|
|
1711
|
+
....: NAME = 'daily meal'
|
|
1712
|
+
....: food = dict(default='bread', values=dict(bread='rye bread', salmon='a fish'))
|
|
1713
|
+
....: drink = dict(default='water',values=dict(water='essential for life', wine='essential'))
|
|
1714
|
+
sage: Meal.options.food='salmon'; Meal.options
|
|
1715
|
+
Current options for daily meal
|
|
1716
|
+
- drink: water
|
|
1717
|
+
- food: salmon
|
|
1718
|
+
sage: Meal.options._reset('drink'); Meal.options()
|
|
1719
|
+
Current options for daily meal
|
|
1720
|
+
- drink: water
|
|
1721
|
+
- food: salmon
|
|
1722
|
+
sage: Meal.options._reset(); Meal.options()
|
|
1723
|
+
Current options for daily meal
|
|
1724
|
+
- drink: water
|
|
1725
|
+
- food: bread
|
|
1726
|
+
"""
|
|
1727
|
+
if option is None:
|
|
1728
|
+
for option in self.__default_value:
|
|
1729
|
+
self._value[option] = self.__default_value[option]
|
|
1730
|
+
if not self._case_sensitive[option] and isinstance(self._value[option], str):
|
|
1731
|
+
self._value[option] = self._value[option].lower()
|
|
1732
|
+
for option in self._linked_value:
|
|
1733
|
+
link, linked_opt = self._linked_value[option]
|
|
1734
|
+
link._reset(linked_opt)
|
|
1735
|
+
else:
|
|
1736
|
+
option = self._match_option(option)
|
|
1737
|
+
if option in self.__default_value:
|
|
1738
|
+
self._value[option] = self.__default_value[option]
|
|
1739
|
+
if not self._case_sensitive[option] and isinstance(self._value[option], str):
|
|
1740
|
+
self._value[option] = self._value[option].lower()
|
|
1741
|
+
elif option in self._linked_value:
|
|
1742
|
+
link, linked_opt = self._linked_value[option]
|
|
1743
|
+
link._reset(linked_opt)
|