passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-objects might be problematic. Click here for more details.
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.45.dist-info/METADATA +115 -0
- passagemath_objects-10.6.45.dist-info/RECORD +280 -0
- passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
- passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
- passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- sage/all__sagemath_objects.py +37 -0
- sage/arith/all__sagemath_objects.py +5 -0
- sage/arith/long.pxd +411 -0
- sage/arith/numerical_approx.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/action.pxd +29 -0
- sage/categories/action.pyx +641 -0
- sage/categories/algebra_functor.py +745 -0
- sage/categories/all__sagemath_objects.py +33 -0
- sage/categories/basic.py +62 -0
- sage/categories/cartesian_product.py +295 -0
- sage/categories/category.py +3401 -0
- sage/categories/category_cy_helper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_singleton.pxd +3 -0
- sage/categories/category_singleton.pyx +342 -0
- sage/categories/category_types.py +637 -0
- sage/categories/category_with_axiom.py +2876 -0
- sage/categories/covariant_functorial_construction.py +703 -0
- sage/categories/facade_sets.py +228 -0
- sage/categories/functor.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/functor.pxd +7 -0
- sage/categories/functor.pyx +691 -0
- sage/categories/homset.py +1338 -0
- sage/categories/homsets.py +364 -0
- sage/categories/isomorphic_objects.py +73 -0
- sage/categories/map.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2106 -0
- sage/categories/morphism.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/morphism.pxd +14 -0
- sage/categories/morphism.pyx +895 -0
- sage/categories/objects.py +167 -0
- sage/categories/primer.py +1696 -0
- sage/categories/pushout.py +4834 -0
- sage/categories/quotients.py +64 -0
- sage/categories/realizations.py +200 -0
- sage/categories/sets_cat.py +3290 -0
- sage/categories/sets_with_partial_maps.py +52 -0
- sage/categories/subobjects.py +64 -0
- sage/categories/subquotients.py +21 -0
- sage/categories/with_realizations.py +311 -0
- sage/cpython/__init__.py +19 -0
- sage/cpython/_py2_random.py +619 -0
- sage/cpython/all.py +3 -0
- sage/cpython/atexit.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/cython_metaclass.h +117 -0
- sage/cpython/cython_metaclass.pxd +3 -0
- sage/cpython/cython_metaclass.pyx +130 -0
- sage/cpython/debug.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/dict_del_by_value.pxd +9 -0
- sage/cpython/dict_del_by_value.pyx +191 -0
- sage/cpython/dict_internal.h +245 -0
- sage/cpython/getattr.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/getattr.pxd +9 -0
- sage/cpython/getattr.pyx +439 -0
- sage/cpython/pycore_long.h +97 -0
- sage/cpython/pycore_long.pxd +10 -0
- sage/cpython/python_debug.h +44 -0
- sage/cpython/python_debug.pxd +47 -0
- sage/cpython/pyx_visit.h +13 -0
- sage/cpython/string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/string.pxd +76 -0
- sage/cpython/string.pyx +34 -0
- sage/cpython/string_impl.h +60 -0
- sage/cpython/type.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/type.pxd +2 -0
- sage/cpython/type.pyx +40 -0
- sage/cpython/wrapperdescr.pxd +67 -0
- sage/ext/all__sagemath_objects.py +3 -0
- sage/ext/ccobject.h +64 -0
- sage/ext/cplusplus.pxd +17 -0
- sage/ext/mod_int.h +30 -0
- sage/ext/mod_int.pxd +24 -0
- sage/ext/stdsage.pxd +39 -0
- sage/groups/all__sagemath_objects.py +1 -0
- sage/groups/group.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/old.pxd +14 -0
- sage/groups/old.pyx +219 -0
- sage/libs/all__sagemath_objects.py +3 -0
- sage/libs/gmp/__init__.py +1 -0
- sage/libs/gmp/all.pxd +6 -0
- sage/libs/gmp/binop.pxd +23 -0
- sage/libs/gmp/misc.pxd +8 -0
- sage/libs/gmp/mpf.pxd +88 -0
- sage/libs/gmp/mpn.pxd +57 -0
- sage/libs/gmp/mpq.pxd +57 -0
- sage/libs/gmp/mpz.pxd +202 -0
- sage/libs/gmp/pylong.cpython-313-x86_64-linux-musl.so +0 -0
- sage/libs/gmp/pylong.pxd +12 -0
- sage/libs/gmp/pylong.pyx +150 -0
- sage/libs/gmp/random.pxd +25 -0
- sage/libs/gmp/randomize.pxd +59 -0
- sage/libs/gmp/types.pxd +53 -0
- sage/libs/gmpxx.pxd +19 -0
- sage/misc/abstract_method.py +276 -0
- sage/misc/all__sagemath_objects.py +43 -0
- sage/misc/bindable_class.py +253 -0
- sage/misc/c3_controlled.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/cachefunc.pxd +43 -0
- sage/misc/cachefunc.pyx +3781 -0
- sage/misc/call.py +188 -0
- sage/misc/classcall_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fast_methods.pxd +20 -0
- sage/misc/fast_methods.pyx +351 -0
- sage/misc/flatten.py +90 -0
- sage/misc/fpickle.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_string.pxd +7 -0
- sage/misc/lazy_string.pyx +546 -0
- sage/misc/misc.py +1066 -0
- sage/misc/misc_c.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/misc_c.pxd +3 -0
- sage/misc/misc_c.pyx +766 -0
- sage/misc/namespace_package.py +37 -0
- sage/misc/nested_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/randstate.pxd +30 -0
- sage/misc/randstate.pyx +1059 -0
- sage/misc/repr.py +203 -0
- sage/misc/reset.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +275 -0
- sage/misc/sage_timeit_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_timeit_class.pyx +120 -0
- sage/misc/sage_unittest.py +637 -0
- sage/misc/sageinspect.py +2768 -0
- sage/misc/session.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/session.pyx +392 -0
- sage/misc/superseded.py +557 -0
- sage/misc/test_nested_class.py +228 -0
- sage/misc/timing.py +264 -0
- sage/misc/unknown.py +222 -0
- sage/misc/verbose.py +253 -0
- sage/misc/weak_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/weak_dict.pxd +15 -0
- sage/misc/weak_dict.pyx +1231 -0
- sage/modules/all__sagemath_objects.py +1 -0
- sage/modules/module.cpython-313-x86_64-linux-musl.so +0 -0
- sage/modules/module.pxd +5 -0
- sage/modules/module.pyx +329 -0
- sage/rings/all__sagemath_objects.py +3 -0
- sage/rings/integer_fake.h +22 -0
- sage/rings/integer_fake.pxd +55 -0
- sage/sets/all__sagemath_objects.py +3 -0
- sage/sets/pythonclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/sets/pythonclass.pxd +9 -0
- sage/sets/pythonclass.pyx +247 -0
- sage/structure/__init__.py +4 -0
- sage/structure/all.py +30 -0
- sage/structure/category_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_dict.pxd +51 -0
- sage/structure/coerce_dict.pyx +1557 -0
- sage/structure/coerce_exceptions.py +23 -0
- sage/structure/coerce_maps.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/debug_options.pxd +6 -0
- sage/structure/debug_options.pyx +54 -0
- sage/structure/dynamic_class.py +541 -0
- sage/structure/element.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element_wrapper.pxd +12 -0
- sage/structure/element_wrapper.pyx +582 -0
- sage/structure/factorization.py +1422 -0
- sage/structure/factorization_integer.py +105 -0
- sage/structure/factory.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/factory.pyx +786 -0
- sage/structure/formal_sum.py +489 -0
- sage/structure/gens_py.py +73 -0
- sage/structure/global_options.py +1743 -0
- sage/structure/indexed_generators.py +863 -0
- sage/structure/list_clone.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_demo.pyx +248 -0
- sage/structure/list_clone_timings.py +179 -0
- sage/structure/list_clone_timings_cy.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/mutability.pxd +21 -0
- sage/structure/mutability.pyx +348 -0
- sage/structure/nonexact.py +69 -0
- sage/structure/parent.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_old.pxd +25 -0
- sage/structure/parent_old.pyx +294 -0
- sage/structure/proof/__init__.py +1 -0
- sage/structure/proof/all.py +243 -0
- sage/structure/proof/proof.py +300 -0
- sage/structure/richcmp.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/sage_object.pxd +3 -0
- sage/structure/sage_object.pyx +988 -0
- sage/structure/sage_object_test.py +19 -0
- sage/structure/sequence.py +937 -0
- sage/structure/set_factories.py +1178 -0
- sage/structure/set_factories_example.py +527 -0
- sage/structure/support_view.py +179 -0
- sage/structure/test_factory.py +56 -0
- sage/structure/unique_representation.py +1359 -0
sage/misc/decorators.py
ADDED
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
"""
|
|
3
|
+
Decorators
|
|
4
|
+
|
|
5
|
+
Python decorators for use in Sage.
|
|
6
|
+
|
|
7
|
+
AUTHORS:
|
|
8
|
+
|
|
9
|
+
- Tim Dumol (5 Dec 2009) -- initial version.
|
|
10
|
+
- Johan S. R. Nielsen (2010) -- collect decorators from various modules.
|
|
11
|
+
- Johan S. R. Nielsen (8 apr 2011) -- improve introspection on decorators.
|
|
12
|
+
- Simon King (2011-05-26) -- improve introspection of sage_wraps. Put this
|
|
13
|
+
file into the reference manual.
|
|
14
|
+
- Julian Rueth (2014-03-19): added ``decorator_keywords`` decorator
|
|
15
|
+
"""
|
|
16
|
+
# *****************************************************************************
|
|
17
|
+
# Copyright (C) 2009 Tim Dumol
|
|
18
|
+
# 2010,2011 Johan S. R. Nielsen
|
|
19
|
+
# 2011 Simon King <simon.king@uni-jena.de>
|
|
20
|
+
# 2014 Julian Rueth <julian.rueth@fsfe.org>
|
|
21
|
+
#
|
|
22
|
+
# This program is free software: you can redistribute it and/or modify
|
|
23
|
+
# it under the terms of the GNU General Public License as published by
|
|
24
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
25
|
+
# (at your option) any later version.
|
|
26
|
+
# http://www.gnu.org/licenses/
|
|
27
|
+
# *****************************************************************************
|
|
28
|
+
|
|
29
|
+
from functools import (partial, update_wrapper, WRAPPER_ASSIGNMENTS,
|
|
30
|
+
WRAPPER_UPDATES)
|
|
31
|
+
from copy import copy
|
|
32
|
+
|
|
33
|
+
from sage.misc.sageinspect import (sage_getsource, sage_getsourcelines,
|
|
34
|
+
sage_getargspec)
|
|
35
|
+
|
|
36
|
+
from inspect import FullArgSpec
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):
|
|
40
|
+
r"""
|
|
41
|
+
Decorator factory which should be used in decorators for making sure that
|
|
42
|
+
meta-information on the decorated callables are retained through the
|
|
43
|
+
decorator, such that the introspection functions of
|
|
44
|
+
``sage.misc.sageinspect`` retrieves them correctly. This includes
|
|
45
|
+
documentation string, source, and argument specification. This is an
|
|
46
|
+
extension of the Python standard library decorator functools.wraps.
|
|
47
|
+
|
|
48
|
+
That the argument specification is retained from the decorated functions
|
|
49
|
+
implies, that if one uses ``sage_wraps`` in a decorator which intentionally
|
|
50
|
+
changes the argument specification, one should add this information to
|
|
51
|
+
the special attribute ``_sage_argspec_`` of the wrapping function (for an
|
|
52
|
+
example, see e.g. ``@options`` decorator in this module).
|
|
53
|
+
|
|
54
|
+
Note that in ``.pyx`` files which is compiled by Cython, because Sage uses
|
|
55
|
+
``binding=False`` compiler directive by default, you need to explicitly
|
|
56
|
+
specify ``binding=True`` for all functions decorated with ``sage_wraps``::
|
|
57
|
+
|
|
58
|
+
sage: import cython
|
|
59
|
+
sage: def square(f):
|
|
60
|
+
....: @sage_wraps(f)
|
|
61
|
+
....: @cython.binding(True)
|
|
62
|
+
....: def new_f(x):
|
|
63
|
+
....: return f(x)*f(x)
|
|
64
|
+
....: return new_f
|
|
65
|
+
|
|
66
|
+
EXAMPLES:
|
|
67
|
+
|
|
68
|
+
Demonstrate that documentation string and source are retained from the
|
|
69
|
+
decorated function::
|
|
70
|
+
|
|
71
|
+
sage: def square(f):
|
|
72
|
+
....: @sage_wraps(f)
|
|
73
|
+
....: def new_f(x):
|
|
74
|
+
....: return f(x)*f(x)
|
|
75
|
+
....: return new_f
|
|
76
|
+
sage: @square
|
|
77
|
+
....: def g(x):
|
|
78
|
+
....: "My little function"
|
|
79
|
+
....: return x
|
|
80
|
+
sage: g(2)
|
|
81
|
+
4
|
|
82
|
+
sage: g(x) # needs sage.symbolic
|
|
83
|
+
x^2
|
|
84
|
+
sage: g.__doc__
|
|
85
|
+
'My little function'
|
|
86
|
+
sage: from sage.misc.sageinspect import sage_getsource, sage_getsourcelines, sage_getfile
|
|
87
|
+
sage: sage_getsource(g)
|
|
88
|
+
'@square...def g(x)...'
|
|
89
|
+
|
|
90
|
+
Demonstrate that the argument description are retained from the
|
|
91
|
+
decorated function through the special method (when left
|
|
92
|
+
unchanged) (see :issue:`9976`)::
|
|
93
|
+
|
|
94
|
+
sage: def diff_arg_dec(f):
|
|
95
|
+
....: @sage_wraps(f)
|
|
96
|
+
....: def new_f(y, some_def_arg=2):
|
|
97
|
+
....: return f(y+some_def_arg)
|
|
98
|
+
....: return new_f
|
|
99
|
+
sage: @diff_arg_dec
|
|
100
|
+
....: def g(x):
|
|
101
|
+
....: return x
|
|
102
|
+
sage: g(1)
|
|
103
|
+
3
|
|
104
|
+
sage: g(1, some_def_arg=4)
|
|
105
|
+
5
|
|
106
|
+
sage: from sage.misc.sageinspect import sage_getargspec
|
|
107
|
+
sage: sage_getargspec(g)
|
|
108
|
+
FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=None,
|
|
109
|
+
kwonlyargs=[], kwonlydefaults=None, annotations={})
|
|
110
|
+
|
|
111
|
+
Demonstrate that it correctly gets the source lines and the source
|
|
112
|
+
file, which is essential for interactive code edition; note that we
|
|
113
|
+
do not test the line numbers, as they may easily change::
|
|
114
|
+
|
|
115
|
+
sage: P.<x,y> = QQ[]
|
|
116
|
+
sage: I = P*[x,y]
|
|
117
|
+
sage: sage_getfile(I.interreduced_basis) # known bug
|
|
118
|
+
'.../sage/rings/polynomial/multi_polynomial_ideal.py'
|
|
119
|
+
sage: sage_getsourcelines(I.interreduced_basis) # needs sage.libs.singular
|
|
120
|
+
([' @handle_AA_and_QQbar\n',
|
|
121
|
+
' @singular_gb_standard_options\n',
|
|
122
|
+
' @libsingular_gb_standard_options\n',
|
|
123
|
+
' def interreduced_basis(self):\n',
|
|
124
|
+
...
|
|
125
|
+
' return self.basis.reduced()\n'], ...)
|
|
126
|
+
|
|
127
|
+
The ``f`` attribute of the decorated function refers to the
|
|
128
|
+
original function::
|
|
129
|
+
|
|
130
|
+
sage: foo = object()
|
|
131
|
+
sage: @sage_wraps(foo)
|
|
132
|
+
....: def func():
|
|
133
|
+
....: pass
|
|
134
|
+
sage: wrapped = sage_wraps(foo)(func)
|
|
135
|
+
sage: wrapped.f is foo
|
|
136
|
+
True
|
|
137
|
+
|
|
138
|
+
Demonstrate that sage_wraps works for non-function callables
|
|
139
|
+
(:issue:`9919`)::
|
|
140
|
+
|
|
141
|
+
sage: def square_for_met(f):
|
|
142
|
+
....: @sage_wraps(f)
|
|
143
|
+
....: def new_f(self, x):
|
|
144
|
+
....: return f(self,x)*f(self,x)
|
|
145
|
+
....: return new_f
|
|
146
|
+
sage: class T:
|
|
147
|
+
....: @square_for_met
|
|
148
|
+
....: def g(self, x):
|
|
149
|
+
....: "My little method"
|
|
150
|
+
....: return x
|
|
151
|
+
sage: t = T()
|
|
152
|
+
sage: t.g(2)
|
|
153
|
+
4
|
|
154
|
+
sage: t.g.__doc__
|
|
155
|
+
'My little method'
|
|
156
|
+
|
|
157
|
+
The bug described in :issue:`11734` is fixed::
|
|
158
|
+
|
|
159
|
+
sage: def square(f):
|
|
160
|
+
....: @sage_wraps(f)
|
|
161
|
+
....: def new_f(x):
|
|
162
|
+
....: return f(x)*f(x)
|
|
163
|
+
....: return new_f
|
|
164
|
+
sage: f = lambda x:x^2
|
|
165
|
+
sage: g = square(f)
|
|
166
|
+
sage: g(3) # this line used to fail for some people if these command were manually entered on the sage prompt
|
|
167
|
+
81
|
|
168
|
+
"""
|
|
169
|
+
# TRAC 9919: Workaround for bug in @update_wrapper when used with
|
|
170
|
+
# non-function callables.
|
|
171
|
+
assigned = set(assigned).intersection(set(dir(wrapped)))
|
|
172
|
+
# end workaround
|
|
173
|
+
|
|
174
|
+
def f(wrapper, assigned=assigned, updated=updated):
|
|
175
|
+
update_wrapper(wrapper, wrapped, assigned=assigned, updated=updated)
|
|
176
|
+
# For backwards-compatibility with old versions of sage_wraps
|
|
177
|
+
wrapper.f = wrapped
|
|
178
|
+
# For forwards-compatibility with functools.wraps on Python 3
|
|
179
|
+
wrapper.__wrapped__ = wrapped
|
|
180
|
+
wrapper._sage_src_ = lambda: sage_getsource(wrapped)
|
|
181
|
+
wrapper._sage_src_lines_ = lambda: sage_getsourcelines(wrapped)
|
|
182
|
+
# Getting the signature right in documentation by Sphinx (Issue 9976)
|
|
183
|
+
# The attribute _sage_argspec_() is read by Sphinx if present and used
|
|
184
|
+
# as the argspec of the function instead of using reflection.
|
|
185
|
+
wrapper._sage_argspec_ = lambda: sage_getargspec(wrapped)
|
|
186
|
+
return wrapper
|
|
187
|
+
return f
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Infix operator decorator
|
|
191
|
+
class infix_operator:
|
|
192
|
+
"""
|
|
193
|
+
A decorator for functions which allows for a hack that makes
|
|
194
|
+
the function behave like an infix operator.
|
|
195
|
+
|
|
196
|
+
This decorator exists as a convenience for interactive use.
|
|
197
|
+
|
|
198
|
+
EXAMPLES:
|
|
199
|
+
|
|
200
|
+
An infix dot product operator::
|
|
201
|
+
|
|
202
|
+
sage: @infix_operator('multiply')
|
|
203
|
+
....: def dot(a, b):
|
|
204
|
+
....: '''Dot product.'''
|
|
205
|
+
....: return a.dot_product(b)
|
|
206
|
+
sage: u = vector([1, 2, 3]) # needs sage.modules
|
|
207
|
+
sage: v = vector([5, 4, 3]) # needs sage.modules
|
|
208
|
+
sage: u *dot* v # needs sage.modules
|
|
209
|
+
22
|
|
210
|
+
|
|
211
|
+
An infix element-wise addition operator::
|
|
212
|
+
|
|
213
|
+
sage: # needs sage.modules
|
|
214
|
+
sage: @infix_operator('add')
|
|
215
|
+
....: def eadd(a, b):
|
|
216
|
+
....: return a.parent([i + j for i, j in zip(a, b)])
|
|
217
|
+
sage: u = vector([1, 2, 3])
|
|
218
|
+
sage: v = vector([5, 4, 3])
|
|
219
|
+
sage: u +eadd+ v
|
|
220
|
+
(6, 6, 6)
|
|
221
|
+
sage: 2*u +eadd+ v
|
|
222
|
+
(7, 8, 9)
|
|
223
|
+
|
|
224
|
+
A hack to simulate a postfix operator::
|
|
225
|
+
|
|
226
|
+
sage: @infix_operator('or')
|
|
227
|
+
....: def thendo(a, b):
|
|
228
|
+
....: return b(a)
|
|
229
|
+
sage: x |thendo| cos |thendo| (lambda x: x^2) # needs sage.symbolic
|
|
230
|
+
cos(x)^2
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
operators = {
|
|
234
|
+
'add': {'left': '__add__', 'right': '__radd__'},
|
|
235
|
+
'multiply': {'left': '__mul__', 'right': '__rmul__'},
|
|
236
|
+
'or': {'left': '__or__', 'right': '__ror__'},
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
def __init__(self, precedence):
|
|
240
|
+
"""
|
|
241
|
+
INPUT:
|
|
242
|
+
|
|
243
|
+
- ``precedence`` -- one of ``'add'``, ``'multiply'``, or ``'or'``
|
|
244
|
+
indicating the new operator's precedence in the order of operations
|
|
245
|
+
"""
|
|
246
|
+
self.precedence = precedence
|
|
247
|
+
|
|
248
|
+
def __call__(self, func):
|
|
249
|
+
"""Returns a function which acts as an inline operator."""
|
|
250
|
+
|
|
251
|
+
left_meth = self.operators[self.precedence]['left']
|
|
252
|
+
right_meth = self.operators[self.precedence]['right']
|
|
253
|
+
wrapper_name = func.__name__
|
|
254
|
+
wrapper_members = {
|
|
255
|
+
'function': staticmethod(func),
|
|
256
|
+
left_meth: _infix_wrapper._left,
|
|
257
|
+
right_meth: _infix_wrapper._right,
|
|
258
|
+
'_sage_src_': lambda: sage_getsource(func)
|
|
259
|
+
}
|
|
260
|
+
for attr in WRAPPER_ASSIGNMENTS:
|
|
261
|
+
try:
|
|
262
|
+
wrapper_members[attr] = getattr(func, attr)
|
|
263
|
+
except AttributeError:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
wrapper = type(wrapper_name, (_infix_wrapper,), wrapper_members)
|
|
267
|
+
|
|
268
|
+
wrapper_inst = wrapper()
|
|
269
|
+
wrapper_inst.__dict__.update(getattr(func, '__dict__', {}))
|
|
270
|
+
return wrapper_inst
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class _infix_wrapper:
|
|
274
|
+
function = None
|
|
275
|
+
|
|
276
|
+
def __init__(self, left=None, right=None):
|
|
277
|
+
"""
|
|
278
|
+
Initialize the actual infix object, with possibly a specified left
|
|
279
|
+
and/or right operand.
|
|
280
|
+
"""
|
|
281
|
+
self.left = left
|
|
282
|
+
self.right = right
|
|
283
|
+
|
|
284
|
+
def __call__(self, *args, **kwds):
|
|
285
|
+
"""Call the passed function."""
|
|
286
|
+
return self.function(*args, **kwds)
|
|
287
|
+
|
|
288
|
+
def _left(self, right):
|
|
289
|
+
"""The function for the operation on the left (e.g., __add__)."""
|
|
290
|
+
if self.left is None:
|
|
291
|
+
if self.right is None:
|
|
292
|
+
new = copy(self)
|
|
293
|
+
new.right = right
|
|
294
|
+
return new
|
|
295
|
+
else:
|
|
296
|
+
raise SyntaxError("Infix operator already has its "
|
|
297
|
+
"right argument")
|
|
298
|
+
else:
|
|
299
|
+
return self.function(self.left, right)
|
|
300
|
+
|
|
301
|
+
def _right(self, left):
|
|
302
|
+
"""The function for the operation on the right (e.g., __radd__)."""
|
|
303
|
+
if self.right is None:
|
|
304
|
+
if self.left is None:
|
|
305
|
+
new = copy(self)
|
|
306
|
+
new.left = left
|
|
307
|
+
return new
|
|
308
|
+
else:
|
|
309
|
+
raise SyntaxError("Infix operator already has its "
|
|
310
|
+
"left argument")
|
|
311
|
+
else:
|
|
312
|
+
return self.function(left, self.right)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def decorator_defaults(func):
|
|
316
|
+
"""
|
|
317
|
+
This function allows a decorator to have default arguments.
|
|
318
|
+
|
|
319
|
+
Normally, a decorator can be called with or without arguments.
|
|
320
|
+
However, the two cases call for different types of return values.
|
|
321
|
+
If a decorator is called with no parentheses, it should be run
|
|
322
|
+
directly on the function. However, if a decorator is called with
|
|
323
|
+
parentheses (i.e., arguments), then it should return a function
|
|
324
|
+
that is then in turn called with the defined function as an
|
|
325
|
+
argument.
|
|
326
|
+
|
|
327
|
+
This decorator allows us to have these default arguments without
|
|
328
|
+
worrying about the return type.
|
|
329
|
+
|
|
330
|
+
EXAMPLES::
|
|
331
|
+
|
|
332
|
+
sage: from sage.misc.decorators import decorator_defaults
|
|
333
|
+
sage: @decorator_defaults
|
|
334
|
+
....: def my_decorator(f, *args, **kwds):
|
|
335
|
+
....: print(kwds)
|
|
336
|
+
....: print(args)
|
|
337
|
+
....: print(f.__name__)
|
|
338
|
+
|
|
339
|
+
sage: @my_decorator
|
|
340
|
+
....: def my_fun(a, b):
|
|
341
|
+
....: return a,b
|
|
342
|
+
{}
|
|
343
|
+
()
|
|
344
|
+
my_fun
|
|
345
|
+
sage: @my_decorator(3,4,c=1,d=2)
|
|
346
|
+
....: def my_fun(a, b):
|
|
347
|
+
....: return a,b
|
|
348
|
+
{'c': 1, 'd': 2}
|
|
349
|
+
(3, 4)
|
|
350
|
+
my_fun
|
|
351
|
+
"""
|
|
352
|
+
@sage_wraps(func)
|
|
353
|
+
def my_wrap(*args, **kwds):
|
|
354
|
+
if len(kwds) == 0 and len(args) == 1:
|
|
355
|
+
# call without parentheses
|
|
356
|
+
return func(*args)
|
|
357
|
+
else:
|
|
358
|
+
return lambda f: func(f, *args, **kwds)
|
|
359
|
+
return my_wrap
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class suboptions:
|
|
363
|
+
def __init__(self, name, **options):
|
|
364
|
+
"""
|
|
365
|
+
A decorator for functions which collects all keywords
|
|
366
|
+
starting with ``name+'_'`` and collects them into a dictionary
|
|
367
|
+
which will be passed on to the wrapped function as a
|
|
368
|
+
dictionary called ``name_options``.
|
|
369
|
+
|
|
370
|
+
The keyword arguments passed into the constructor are taken
|
|
371
|
+
to be default for the ``name_options`` dictionary.
|
|
372
|
+
|
|
373
|
+
EXAMPLES::
|
|
374
|
+
|
|
375
|
+
sage: from sage.misc.decorators import suboptions
|
|
376
|
+
sage: s = suboptions('arrow', size=2)
|
|
377
|
+
sage: s.name
|
|
378
|
+
'arrow_'
|
|
379
|
+
sage: s.options
|
|
380
|
+
{'size': 2}
|
|
381
|
+
"""
|
|
382
|
+
self.name = name + "_"
|
|
383
|
+
self.options = options
|
|
384
|
+
|
|
385
|
+
def __call__(self, func):
|
|
386
|
+
"""
|
|
387
|
+
Return a wrapper around ``func``.
|
|
388
|
+
|
|
389
|
+
EXAMPLES::
|
|
390
|
+
|
|
391
|
+
sage: from sage.misc.decorators import suboptions
|
|
392
|
+
sage: def f(*args, **kwds): print(sorted(kwds.items()))
|
|
393
|
+
sage: f = suboptions('arrow', size=2)(f)
|
|
394
|
+
sage: f(size=2)
|
|
395
|
+
[('arrow_options', {'size': 2}), ('size', 2)]
|
|
396
|
+
sage: f(arrow_size=3)
|
|
397
|
+
[('arrow_options', {'size': 3})]
|
|
398
|
+
sage: f(arrow_options={'size':4})
|
|
399
|
+
[('arrow_options', {'size': 4})]
|
|
400
|
+
sage: f(arrow_options={'size':4}, arrow_size=5)
|
|
401
|
+
[('arrow_options', {'size': 5})]
|
|
402
|
+
|
|
403
|
+
Demonstrate that the introspected argument specification of the
|
|
404
|
+
wrapped function is updated (see :issue:`9976`)::
|
|
405
|
+
|
|
406
|
+
sage: from sage.misc.sageinspect import sage_getargspec
|
|
407
|
+
sage: sage_getargspec(f)
|
|
408
|
+
FullArgSpec(args=['arrow_size'], varargs='args', varkw='kwds', defaults=(2,),
|
|
409
|
+
kwonlyargs=[], kwonlydefaults=None, annotations={})
|
|
410
|
+
"""
|
|
411
|
+
@sage_wraps(func)
|
|
412
|
+
def wrapper(*args, **kwds):
|
|
413
|
+
suboptions = copy(self.options)
|
|
414
|
+
suboptions.update(kwds.pop(self.name+"options", {}))
|
|
415
|
+
|
|
416
|
+
# Collect all the relevant keywords in kwds
|
|
417
|
+
# and put them in suboptions
|
|
418
|
+
for key, value in list(kwds.items()):
|
|
419
|
+
if key.startswith(self.name):
|
|
420
|
+
suboptions[key[len(self.name):]] = value
|
|
421
|
+
del kwds[key]
|
|
422
|
+
|
|
423
|
+
kwds[self.name + "options"] = suboptions
|
|
424
|
+
|
|
425
|
+
return func(*args, **kwds)
|
|
426
|
+
|
|
427
|
+
# Add the options specified by @options to the signature of the wrapped
|
|
428
|
+
# function in the Sphinx-generated documentation (Issue 9976), using the
|
|
429
|
+
# special attribute _sage_argspec_ (see e.g. sage.misc.sageinspect)
|
|
430
|
+
def argspec():
|
|
431
|
+
argspec = sage_getargspec(func)
|
|
432
|
+
|
|
433
|
+
def listForNone(l):
|
|
434
|
+
return l if l is not None else []
|
|
435
|
+
newArgs = [self.name + opt for opt in self.options.keys()]
|
|
436
|
+
args = (argspec.args if argspec.args is not None else []) + newArgs
|
|
437
|
+
defaults = (argspec.defaults if argspec.defaults is not None else ()) \
|
|
438
|
+
+ tuple(self.options.values())
|
|
439
|
+
# Note: argspec.defaults is not always a tuple for some reason
|
|
440
|
+
return FullArgSpec(args, argspec.varargs, argspec.varkw, defaults,
|
|
441
|
+
kwonlyargs=[], kwonlydefaults=None, annotations={})
|
|
442
|
+
wrapper._sage_argspec_ = argspec
|
|
443
|
+
|
|
444
|
+
return wrapper
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
class options:
|
|
448
|
+
def __init__(self, **options):
|
|
449
|
+
"""
|
|
450
|
+
A decorator for functions which allows for default options to be
|
|
451
|
+
set and reset by the end user. Additionally, if one needs to, one
|
|
452
|
+
can get at the original keyword arguments passed into the
|
|
453
|
+
decorator.
|
|
454
|
+
|
|
455
|
+
TESTS::
|
|
456
|
+
|
|
457
|
+
sage: from sage.misc.decorators import options
|
|
458
|
+
sage: o = options(rgbcolor=(0,0,1))
|
|
459
|
+
sage: o.options
|
|
460
|
+
{'rgbcolor': (0, 0, 1)}
|
|
461
|
+
sage: o = options(rgbcolor=(0,0,1), __original_opts=True)
|
|
462
|
+
sage: o.original_opts
|
|
463
|
+
True
|
|
464
|
+
sage: loads(dumps(o)).options
|
|
465
|
+
{'rgbcolor': (0, 0, 1)}
|
|
466
|
+
|
|
467
|
+
Demonstrate that the introspected argument specification of the wrapped
|
|
468
|
+
function is updated (see :issue:`9976`)::
|
|
469
|
+
|
|
470
|
+
sage: from sage.misc.decorators import options
|
|
471
|
+
sage: o = options(rgbcolor=(0,0,1))
|
|
472
|
+
sage: def f(*args, **kwds):
|
|
473
|
+
....: print("{} {}".format(args, sorted(kwds.items())))
|
|
474
|
+
sage: f1 = o(f)
|
|
475
|
+
sage: from sage.misc.sageinspect import sage_getargspec
|
|
476
|
+
sage: sage_getargspec(f1)
|
|
477
|
+
FullArgSpec(args=['rgbcolor'], varargs='args', varkw='kwds', defaults=((0, 0, 1),),
|
|
478
|
+
kwonlyargs=[], kwonlydefaults=None, annotations={})
|
|
479
|
+
"""
|
|
480
|
+
self.options = options
|
|
481
|
+
self.original_opts = options.pop('__original_opts', False)
|
|
482
|
+
|
|
483
|
+
def __call__(self, func):
|
|
484
|
+
"""
|
|
485
|
+
EXAMPLES::
|
|
486
|
+
|
|
487
|
+
sage: from sage.misc.decorators import options
|
|
488
|
+
sage: o = options(rgbcolor=(0,0,1))
|
|
489
|
+
sage: def f(*args, **kwds):
|
|
490
|
+
....: print("{} {}".format(args, sorted(kwds.items())))
|
|
491
|
+
sage: f1 = o(f)
|
|
492
|
+
sage: f1()
|
|
493
|
+
() [('rgbcolor', (0, 0, 1))]
|
|
494
|
+
sage: f1(rgbcolor=1)
|
|
495
|
+
() [('rgbcolor', 1)]
|
|
496
|
+
sage: o = options(rgbcolor=(0,0,1), __original_opts=True)
|
|
497
|
+
sage: f2 = o(f)
|
|
498
|
+
sage: f2(alpha=1)
|
|
499
|
+
() [('__original_opts', {'alpha': 1}), ('alpha', 1), ('rgbcolor', (0, 0, 1))]
|
|
500
|
+
"""
|
|
501
|
+
@sage_wraps(func)
|
|
502
|
+
def wrapper(*args, **kwds):
|
|
503
|
+
options = copy(wrapper.options)
|
|
504
|
+
if self.original_opts:
|
|
505
|
+
options['__original_opts'] = kwds
|
|
506
|
+
options.update(kwds)
|
|
507
|
+
return func(*args, **options)
|
|
508
|
+
|
|
509
|
+
# Add the options specified by @options to the signature of the wrapped
|
|
510
|
+
# function in the Sphinx-generated documentation (Issue 9976), using the
|
|
511
|
+
# special attribute _sage_argspec_ (see e.g. sage.misc.sageinspect)
|
|
512
|
+
def argspec():
|
|
513
|
+
argspec = sage_getargspec(func)
|
|
514
|
+
args = ((argspec.args if argspec.args is not None else []) +
|
|
515
|
+
list(self.options))
|
|
516
|
+
defaults = (argspec.defaults or ()) + tuple(self.options.values())
|
|
517
|
+
# Note: argspec.defaults is not always a tuple for some reason
|
|
518
|
+
return FullArgSpec(args, argspec.varargs, argspec.varkw, defaults,
|
|
519
|
+
kwonlyargs=[], kwonlydefaults=None, annotations={})
|
|
520
|
+
|
|
521
|
+
wrapper._sage_argspec_ = argspec
|
|
522
|
+
|
|
523
|
+
def defaults():
|
|
524
|
+
"""
|
|
525
|
+
Return the default options.
|
|
526
|
+
|
|
527
|
+
EXAMPLES::
|
|
528
|
+
|
|
529
|
+
sage: from sage.misc.decorators import options
|
|
530
|
+
sage: o = options(rgbcolor=(0,0,1))
|
|
531
|
+
sage: def f(*args, **kwds):
|
|
532
|
+
....: print("{} {}".format(args, sorted(kwds.items())))
|
|
533
|
+
sage: f = o(f)
|
|
534
|
+
sage: f.options['rgbcolor']=(1,1,1)
|
|
535
|
+
sage: f.defaults()
|
|
536
|
+
{'rgbcolor': (0, 0, 1)}
|
|
537
|
+
"""
|
|
538
|
+
return copy(self.options)
|
|
539
|
+
|
|
540
|
+
def reset():
|
|
541
|
+
"""
|
|
542
|
+
Reset the options to the defaults.
|
|
543
|
+
|
|
544
|
+
EXAMPLES::
|
|
545
|
+
|
|
546
|
+
sage: from sage.misc.decorators import options
|
|
547
|
+
sage: o = options(rgbcolor=(0,0,1))
|
|
548
|
+
sage: def f(*args, **kwds):
|
|
549
|
+
....: print("{} {}".format(args, sorted(kwds.items())))
|
|
550
|
+
sage: f = o(f)
|
|
551
|
+
sage: f.options
|
|
552
|
+
{'rgbcolor': (0, 0, 1)}
|
|
553
|
+
sage: f.options['rgbcolor']=(1,1,1)
|
|
554
|
+
sage: f.options
|
|
555
|
+
{'rgbcolor': (1, 1, 1)}
|
|
556
|
+
sage: f()
|
|
557
|
+
() [('rgbcolor', (1, 1, 1))]
|
|
558
|
+
sage: f.reset()
|
|
559
|
+
sage: f.options
|
|
560
|
+
{'rgbcolor': (0, 0, 1)}
|
|
561
|
+
sage: f()
|
|
562
|
+
() [('rgbcolor', (0, 0, 1))]
|
|
563
|
+
"""
|
|
564
|
+
wrapper.options = copy(self.options)
|
|
565
|
+
|
|
566
|
+
wrapper.options = copy(self.options)
|
|
567
|
+
wrapper.reset = reset
|
|
568
|
+
wrapper.reset.__doc__ = """
|
|
569
|
+
Reset the options to the defaults.
|
|
570
|
+
|
|
571
|
+
Defaults:
|
|
572
|
+
%s
|
|
573
|
+
""" % self.options
|
|
574
|
+
|
|
575
|
+
wrapper.defaults = defaults
|
|
576
|
+
wrapper.defaults.__doc__ = """
|
|
577
|
+
Return the default options.
|
|
578
|
+
|
|
579
|
+
Defaults:
|
|
580
|
+
%s
|
|
581
|
+
""" % self.options
|
|
582
|
+
|
|
583
|
+
return wrapper
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
class rename_keyword:
|
|
587
|
+
def __init__(self, deprecated=None, deprecation=None, **renames):
|
|
588
|
+
"""
|
|
589
|
+
A decorator which renames keyword arguments and optionally
|
|
590
|
+
deprecates the new keyword.
|
|
591
|
+
|
|
592
|
+
INPUT:
|
|
593
|
+
|
|
594
|
+
- ``deprecation`` -- integer; the github issue number where the
|
|
595
|
+
deprecation was introduced
|
|
596
|
+
|
|
597
|
+
- the rest of the arguments is a list of keyword arguments in the
|
|
598
|
+
form ``renamed_option='existing_option'``. This will have the
|
|
599
|
+
effect of renaming ``renamed_option`` so that the function only
|
|
600
|
+
sees ``existing_option``. If both ``renamed_option`` and
|
|
601
|
+
``existing_option`` are passed to the function, ``existing_option``
|
|
602
|
+
will override the ``renamed_option`` value.
|
|
603
|
+
|
|
604
|
+
EXAMPLES::
|
|
605
|
+
|
|
606
|
+
sage: from sage.misc.decorators import rename_keyword
|
|
607
|
+
sage: r = rename_keyword(color='rgbcolor')
|
|
608
|
+
sage: r.renames
|
|
609
|
+
{'color': 'rgbcolor'}
|
|
610
|
+
sage: loads(dumps(r)).renames
|
|
611
|
+
{'color': 'rgbcolor'}
|
|
612
|
+
|
|
613
|
+
To deprecate an old keyword::
|
|
614
|
+
|
|
615
|
+
sage: r = rename_keyword(deprecation=13109, color='rgbcolor')
|
|
616
|
+
"""
|
|
617
|
+
assert deprecated is None, 'Use @rename_keyword(deprecation=<issue_number>, ...)'
|
|
618
|
+
self.renames = renames
|
|
619
|
+
self.deprecation = deprecation
|
|
620
|
+
|
|
621
|
+
def __call__(self, func):
|
|
622
|
+
"""
|
|
623
|
+
Rename keywords.
|
|
624
|
+
|
|
625
|
+
EXAMPLES::
|
|
626
|
+
|
|
627
|
+
sage: from sage.misc.decorators import rename_keyword
|
|
628
|
+
sage: r = rename_keyword(color='rgbcolor')
|
|
629
|
+
sage: def f(*args, **kwds):
|
|
630
|
+
....: print("{} {}".format(args, kwds))
|
|
631
|
+
sage: f = r(f)
|
|
632
|
+
sage: f()
|
|
633
|
+
() {}
|
|
634
|
+
sage: f(alpha=1)
|
|
635
|
+
() {'alpha': 1}
|
|
636
|
+
sage: f(rgbcolor=1)
|
|
637
|
+
() {'rgbcolor': 1}
|
|
638
|
+
sage: f(color=1)
|
|
639
|
+
() {'rgbcolor': 1}
|
|
640
|
+
|
|
641
|
+
We can also deprecate the renamed keyword::
|
|
642
|
+
|
|
643
|
+
sage: r = rename_keyword(deprecation=13109, deprecated_option='new_option')
|
|
644
|
+
sage: def f(*args, **kwds):
|
|
645
|
+
....: print("{} {}".format(args, kwds))
|
|
646
|
+
sage: f = r(f)
|
|
647
|
+
sage: f()
|
|
648
|
+
() {}
|
|
649
|
+
sage: f(alpha=1)
|
|
650
|
+
() {'alpha': 1}
|
|
651
|
+
sage: f(new_option=1)
|
|
652
|
+
() {'new_option': 1}
|
|
653
|
+
sage: f(deprecated_option=1)
|
|
654
|
+
doctest:...: DeprecationWarning: use the option 'new_option' instead of 'deprecated_option'
|
|
655
|
+
See https://github.com/sagemath/sage/issues/13109 for details.
|
|
656
|
+
() {'new_option': 1}
|
|
657
|
+
"""
|
|
658
|
+
@sage_wraps(func)
|
|
659
|
+
def wrapper(*args, **kwds):
|
|
660
|
+
for old_name, new_name in self.renames.items():
|
|
661
|
+
if old_name in kwds and new_name not in kwds:
|
|
662
|
+
if self.deprecation is not None:
|
|
663
|
+
from sage.misc.superseded import deprecation
|
|
664
|
+
deprecation(self.deprecation, "use the option "
|
|
665
|
+
"%r instead of %r" % (new_name, old_name))
|
|
666
|
+
kwds[new_name] = kwds[old_name]
|
|
667
|
+
del kwds[old_name]
|
|
668
|
+
return func(*args, **kwds)
|
|
669
|
+
|
|
670
|
+
return wrapper
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
class specialize:
|
|
674
|
+
r"""
|
|
675
|
+
A decorator generator that returns a decorator that in turn
|
|
676
|
+
returns a specialized function for function ``f``. In other words,
|
|
677
|
+
it returns a function that acts like ``f`` with arguments
|
|
678
|
+
``*args`` and ``**kwargs`` supplied.
|
|
679
|
+
|
|
680
|
+
INPUT:
|
|
681
|
+
|
|
682
|
+
- ``*args``, ``**kwargs`` -- arguments to specialize the function for
|
|
683
|
+
|
|
684
|
+
OUTPUT: a decorator that accepts a function ``f`` and specializes it
|
|
685
|
+
with ``*args`` and ``**kwargs``
|
|
686
|
+
|
|
687
|
+
EXAMPLES::
|
|
688
|
+
|
|
689
|
+
sage: f = specialize(5)(lambda x, y: x+y)
|
|
690
|
+
sage: f(10)
|
|
691
|
+
15
|
|
692
|
+
sage: f(5)
|
|
693
|
+
10
|
|
694
|
+
sage: @specialize("Bon Voyage")
|
|
695
|
+
....: def greet(greeting, name):
|
|
696
|
+
....: print("{0}, {1}!".format(greeting, name))
|
|
697
|
+
sage: greet("Monsieur Jean Valjean")
|
|
698
|
+
Bon Voyage, Monsieur Jean Valjean!
|
|
699
|
+
sage: greet(name = 'Javert')
|
|
700
|
+
Bon Voyage, Javert!
|
|
701
|
+
"""
|
|
702
|
+
def __init__(self, *args, **kwargs):
|
|
703
|
+
self.args = args
|
|
704
|
+
self.kwargs = kwargs
|
|
705
|
+
|
|
706
|
+
def __call__(self, f):
|
|
707
|
+
return sage_wraps(f)(partial(f, *self.args, **self.kwargs))
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def decorator_keywords(func):
|
|
711
|
+
r"""
|
|
712
|
+
A decorator for decorators with optional keyword arguments.
|
|
713
|
+
|
|
714
|
+
EXAMPLES::
|
|
715
|
+
|
|
716
|
+
sage: from sage.misc.decorators import decorator_keywords
|
|
717
|
+
sage: @decorator_keywords
|
|
718
|
+
....: def preprocess(f=None, processor=None):
|
|
719
|
+
....: def wrapper(*args, **kwargs):
|
|
720
|
+
....: if processor is not None:
|
|
721
|
+
....: args, kwargs = processor(*args, **kwargs)
|
|
722
|
+
....: return f(*args, **kwargs)
|
|
723
|
+
....: return wrapper
|
|
724
|
+
|
|
725
|
+
This decorator can be called with and without arguments::
|
|
726
|
+
|
|
727
|
+
sage: @preprocess
|
|
728
|
+
....: def foo(x): return x
|
|
729
|
+
sage: foo(None)
|
|
730
|
+
sage: foo(1)
|
|
731
|
+
1
|
|
732
|
+
|
|
733
|
+
sage: def normalize(x): return ((0,),{}) if x is None else ((x,),{})
|
|
734
|
+
sage: @preprocess(processor=normalize)
|
|
735
|
+
....: def foo(x): return x
|
|
736
|
+
sage: foo(None)
|
|
737
|
+
0
|
|
738
|
+
sage: foo(1)
|
|
739
|
+
1
|
|
740
|
+
"""
|
|
741
|
+
@sage_wraps(func)
|
|
742
|
+
def wrapped(f=None, **kwargs):
|
|
743
|
+
if f is None:
|
|
744
|
+
return sage_wraps(func)(lambda f: func(f, **kwargs))
|
|
745
|
+
else:
|
|
746
|
+
return func(f, **kwargs)
|
|
747
|
+
return wrapped
|