From b414b8e5225fd69bd99de981756c3b97359a2aa0 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 11 Nov 2020 00:28:01 +0100 Subject: [PATCH] Replace sage_wraps by decorator library --- src/sage/graphs/tutte_polynomial.py | 24 +- src/sage/interacts/library.py | 11 +- src/sage/interfaces/magma.py | 17 +- src/sage/interfaces/singular.py | 16 +- src/sage/libs/giac/__init__.py | 19 +- src/sage/libs/singular/standard_options.py | 16 +- src/sage/misc/all.py | 2 +- src/sage/misc/bindable_class.py | 3 - src/sage/misc/cachefunc.pyx | 22 +- src/sage/misc/decorators.py | 331 ++++----------------- src/sage/misc/superseded.py | 175 +++++------ src/sage/rings/qqbar_decorators.py | 251 ++++++++-------- src/sage/structure/element.pyx | 23 +- src/sage/structure/mutability.pyx | 28 +- src/sage/symbolic/expression.pyx | 15 +- src/sage_setup/docbuild/__init__.py | 2 +- 16 files changed, 333 insertions(+), 622 deletions(-) diff --git a/src/sage/graphs/tutte_polynomial.py b/src/sage/graphs/tutte_polynomial.py index f01a21437f9..65201039f26 100644 --- a/src/sage/graphs/tutte_polynomial.py +++ b/src/sage/graphs/tutte_polynomial.py @@ -36,7 +36,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ -from sage.misc.decorators import sage_wraps +from decorator import decorator ###################### # Graph Modification # @@ -482,8 +482,8 @@ def _cache_key(G): """ return tuple(G.canonical_label().edges(labels=False, sort=True)) - -def _cached(func): +@decorator +def _cached(func, G, *args, **kwds): """ Wrapper used to cache results of the function `func` @@ -497,17 +497,13 @@ def _cached(func): sage: tutte_polynomial(G)(1,1) #indirect doctest 2000 """ - @sage_wraps(func) - def wrapper(G, *args, **kwds): - cache = kwds.setdefault('cache', {}) - key = _cache_key(G) - if key in cache: - return cache[key] - result = func(G, *args, **kwds) - cache[key] = result - return result - wrapper.original_func = func - return wrapper + cache = kwds.setdefault('cache', {}) + key = _cache_key(G) + if key in cache: + return cache[key] + result = func(G, *args, **kwds) + cache[key] = result + return result #################### # Tutte Polynomial # diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 67245e72446..36c040dc256 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -36,6 +36,7 @@ #***************************************************************************** from __future__ import absolute_import, division +from decorator import decorater from sage.all import * x = SR.var('x') @@ -57,7 +58,8 @@ globals()[name] = obj -def library_interact(f): +@decorater +def library_interact(f, *args, **kw): """ This is a decorator for using interacts in the Sage library. @@ -75,11 +77,8 @@ def library_interact(f): Interactive function with 1 widget n: IntSlider(value=5, description=u'n', max=15, min=-5) """ - @sage_wraps(f) - def library_wrapper(): - # This will display the interact, no need to return anything - interact(f) - return library_wrapper + # This will display the interact, no need to return anything + interact(f) def html(obj): diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index f3755802d9b..57eb5f5372e 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -216,6 +216,7 @@ import re import sys +from decorator import decorater from sage.structure.parent import Parent from .expect import console, Expect, ExpectElement, ExpectFunction, FunctionElement @@ -3002,10 +3003,11 @@ def __exit__(self, typ, value, tb): """ self.magma.SetVerbose('Groebner', self.groebner_basis_verbose) - -def magma_gb_standard_options(func): +@decorater +def magma_gb_standard_options(func, *args, **kwds): """ Decorator to force default options for Magma. + Execute function in ``MagmaGBDefaultContext``. EXAMPLES:: @@ -3015,13 +3017,6 @@ def magma_gb_standard_options(func): sage: "mself" in sage_getsource(J._groebner_basis_magma) True """ - from sage.misc.decorators import sage_wraps + with MagmaGBDefaultContext(): + return func(*args, **kwds) - @sage_wraps(func) - def wrapper(*args, **kwds): - """ - Execute function in ``MagmaGBDefaultContext``. - """ - with MagmaGBDefaultContext(): - return func(*args, **kwds) - return wrapper diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index e0faf1409ee..63e863e5493 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -325,6 +325,7 @@ import sys import pexpect from time import sleep +from decorator import decorater from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction @@ -1347,7 +1348,8 @@ def __init__(self, parent, type, value, is_name=False): 2 """ RingElement.__init__(self, parent) - if parent is None: return + if parent is None: + return if not is_name: try: self._name = parent._create(value, type) @@ -2744,8 +2746,8 @@ def __exit__(self, typ, value, tb): except SingularError: pass - -def singular_gb_standard_options(func): +@decorater +def singular_gb_standard_options(func, *args, **kwds): r""" Decorator to force a reduced Singular groebner basis. @@ -2777,9 +2779,5 @@ def singular_gb_standard_options(func): This decorator is used automatically internally so the user does not need to use it manually. """ - from sage.misc.decorators import sage_wraps - @sage_wraps(func) - def wrapper(*args, **kwds): - with SingularGBDefaultContext(): - return func(*args, **kwds) - return wrapper + with SingularGBDefaultContext(): + return func(*args, **kwds) diff --git a/src/sage/libs/giac/__init__.py b/src/sage/libs/giac/__init__.py index ffcd5cbc243..f5eba3f0774 100644 --- a/src/sage/libs/giac/__init__.py +++ b/src/sage/libs/giac/__init__.py @@ -30,6 +30,8 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +from decorator import decorater + from sage.structure.proof.all import polynomial as proof_polynomial from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence from .giac import giacsettings, libgiac @@ -84,9 +86,11 @@ def __exit__(self, typ, value, tb): giacsettings.threads = self.threads -def local_giacsettings(func): +@decorater +def local_giacsettings(func, *args, **kwds): """ Decorator to preserve Giac's proba_epsilon and threads settings. + Execute function in ``GiacSettingsDefaultContext``. EXAMPLES:: @@ -107,17 +111,8 @@ def local_giacsettings(func): (True, 2) """ - from sage.misc.decorators import sage_wraps - - @sage_wraps(func) - def wrapper(*args, **kwds): - """ - Execute function in ``GiacSettingsDefaultContext``. - """ - with GiacSettingsDefaultContext(): - return func(*args, **kwds) - - return wrapper + with GiacSettingsDefaultContext(): + return func(*args, **kwds) @local_giacsettings diff --git a/src/sage/libs/singular/standard_options.py b/src/sage/libs/singular/standard_options.py index 6d2d8044106..9d6614aef33 100644 --- a/src/sage/libs/singular/standard_options.py +++ b/src/sage/libs/singular/standard_options.py @@ -5,6 +5,7 @@ - Martin Albrecht """ +from decorator import decorator class LibSingularGBDefaultContext: def __init__(self): @@ -99,9 +100,11 @@ def __exit__(self, typ, value, tb): """ self.libsingular_option_context.__exit__(typ,value,tb) -def libsingular_gb_standard_options(func): +@decorator +def libsingular_gb_standard_options(func, *args, **kwds): """ Decorator to force a reduced Singular groebner basis. + Execute function in ``LibSingularGBDefaultContext``. TESTS:: @@ -131,12 +134,5 @@ def libsingular_gb_standard_options(func): This decorator is used automatically internally so the user does not need to use it manually. """ - from sage.misc.decorators import sage_wraps - @sage_wraps(func) - def wrapper(*args, **kwds): - """ - Execute function in ``LibSingularGBDefaultContext``. - """ - with LibSingularGBDefaultContext(): - return func(*args, **kwds) - return wrapper + with LibSingularGBDefaultContext(): + return func(*args, **kwds) diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index 46765a0a435..a4f4f82299d 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -164,7 +164,7 @@ from .explain_pickle import explain_pickle, unpickle_newobj, unpickle_global, unpickle_build, unpickle_instantiate, unpickle_persistent, unpickle_extension, unpickle_appends -from .decorators import specialize, sage_wraps, infix_operator +from .decorators import specialize, infix_operator from .unknown import Unknown, UnknownError diff --git a/src/sage/misc/bindable_class.py b/src/sage/misc/bindable_class.py index a3b29ee120c..3aeaaa6ce8a 100644 --- a/src/sage/misc/bindable_class.py +++ b/src/sage/misc/bindable_class.py @@ -154,9 +154,6 @@ def __classget__(cls, instance, owner): if instance is None: return cls return BoundClass(cls, instance) - # We probably do not need to use sage_wraps, since - # sageinspect already supports partial functions - #return sage_wraps(cls)(BoundClass(cls, instance)) class BoundClass(functools.partial): """ diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 519bc32d5f7..9889077d0e2 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -425,7 +425,7 @@ from sage.misc.sageinspect import sage_getfile, sage_getsourcelines, sage_getarg from inspect import isfunction from sage.misc.weak_dict cimport CachedWeakValueDictionary -from sage.misc.decorators import decorator_keywords +from decorator import decorator cdef frozenset special_method_names = frozenset(['__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', '__complex__', '__contains__', '__del__', @@ -1243,8 +1243,8 @@ cdef class CachedFunction(object): for ((args,kwargs), val) in P(arglist2): self.set_cache(val, *args, **kwargs) - -cached_function = decorator_keywords(CachedFunction) +def cached_function(func): + return decorate(func, CachedFunction(func)) cdef class WeakCachedFunction(CachedFunction): @@ -1433,7 +1433,8 @@ cdef class WeakCachedFunction(CachedFunction): self.cache = CachedWeakValueDictionary(**kwds) -weak_cached_function = decorator_keywords(WeakCachedFunction) +def weak_cached_function(func): + return decorate(func, WeakCachedFunction(func)) class CachedMethodPickle(object): """ @@ -2995,8 +2996,8 @@ cdef class CachedSpecialMethod(CachedMethod): D[name] = Caller return Caller -@decorator_keywords -def cached_method(f, name=None, key=None, do_pickle=None): +@decorator +def cached_method(f, name=None, key=None, do_pickle=None, *args, **kw): """ A decorator for cached methods. @@ -3090,9 +3091,8 @@ def cached_method(f, name=None, key=None, do_pickle=None): """ cdef str fname = name or f.__name__ if fname in special_method_names: - return CachedSpecialMethod(f, name, key=key, do_pickle=do_pickle) - return CachedMethod(f, name, key=key, do_pickle=do_pickle) - + return CachedSpecialMethod(f, name, key=key, do_pickle=do_pickle)(*args, **kw) + return CachedMethod(f, name, key=key, do_pickle=do_pickle)(*args, **kw) cdef class CachedInParentMethod(CachedMethod): r""" @@ -3304,8 +3304,8 @@ cdef class CachedInParentMethod(CachedMethod): pass return Caller - -cached_in_parent_method = decorator_keywords(CachedInParentMethod) +def cached_in_parent_method(func): + return decorate(func, CachedInParentMethod(func)) class FileCache(object): diff --git a/src/sage/misc/decorators.py b/src/sage/misc/decorators.py index 0136c4281f4..06f6634aa04 100644 --- a/src/sage/misc/decorators.py +++ b/src/sage/misc/decorators.py @@ -8,9 +8,7 @@ - Tim Dumol (5 Dec 2009) -- initial version. - Johan S. R. Nielsen (2010) -- collect decorators from various modules. - Johan S. R. Nielsen (8 apr 2011) -- improve introspection on decorators. -- Simon King (2011-05-26) -- improve introspection of sage_wraps. Put this - file into the reference manual. -- Julian Rueth (2014-03-19): added ``decorator_keywords`` decorator +- Simon King (2011-05-26) -- put this file into the reference manual. """ #***************************************************************************** @@ -31,150 +29,12 @@ WRAPPER_UPDATES) from copy import copy +from decorator import decorate, decorator + from sage.misc.sageinspect import (sage_getsource, sage_getsourcelines, sage_getargspec) from inspect import ArgSpec - -def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): - r""" - Decorator factory which should be used in decorators for making sure that - meta-information on the decorated callables are retained through the - decorator, such that the introspection functions of - ``sage.misc.sageinspect`` retrieves them correctly. This includes - documentation string, source, and argument specification. This is an - extension of the Python standard library decorator functools.wraps. - - That the argument specification is retained from the decorated functions - implies, that if one uses ``sage_wraps`` in a decorator which intentionally - changes the argument specification, one should add this information to - the special attribute ``_sage_argspec_`` of the wrapping function (for an - example, see e.g. ``@options`` decorator in this module). - - EXAMPLES: - - Demonstrate that documentation string and source are retained from the - decorated function:: - - sage: def square(f): - ....: @sage_wraps(f) - ....: def new_f(x): - ....: return f(x)*f(x) - ....: return new_f - sage: @square - ....: def g(x): - ....: "My little function" - ....: return x - sage: g(2) - 4 - sage: g(x) - x^2 - sage: g.__doc__ - 'My little function' - sage: from sage.misc.sageinspect import sage_getsource, sage_getsourcelines, sage_getfile - sage: sage_getsource(g) - '@square...def g(x)...' - - Demonstrate that the argument description are retained from the - decorated function through the special method (when left - unchanged) (see :trac:`9976`):: - - sage: def diff_arg_dec(f): - ....: @sage_wraps(f) - ....: def new_f(y, some_def_arg=2): - ....: return f(y+some_def_arg) - ....: return new_f - sage: @diff_arg_dec - ....: def g(x): - ....: return x - sage: g(1) - 3 - sage: g(1, some_def_arg=4) - 5 - sage: from sage.misc.sageinspect import sage_getargspec - sage: sage_getargspec(g) - ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None) - - Demonstrate that it correctly gets the source lines and the source - file, which is essential for interactive code edition; note that we - do not test the line numbers, as they may easily change:: - - sage: P. = QQ[] - sage: I = P*[x,y] - sage: sage_getfile(I.interreduced_basis) # known bug - '.../sage/rings/polynomial/multi_polynomial_ideal.py' - sage: sage_getsourcelines(I.interreduced_basis) - ([' @handle_AA_and_QQbar\n', - ' @singular_gb_standard_options\n', - ' @libsingular_gb_standard_options\n', - ' def interreduced_basis(self):\n', - ... - ' return self.basis.reduced()\n'], ...) - - The ``f`` attribute of the decorated function refers to the - original function:: - - sage: foo = object() - sage: @sage_wraps(foo) - ....: def func(): - ....: pass - sage: wrapped = sage_wraps(foo)(func) - sage: wrapped.f is foo - True - - Demonstrate that sage_wraps works for non-function callables - (:trac:`9919`):: - - sage: def square_for_met(f): - ....: @sage_wraps(f) - ....: def new_f(self, x): - ....: return f(self,x)*f(self,x) - ....: return new_f - sage: class T: - ....: @square_for_met - ....: def g(self, x): - ....: "My little method" - ....: return x - sage: t = T() - sage: t.g(2) - 4 - sage: t.g.__doc__ - 'My little method' - - The bug described in :trac:`11734` is fixed:: - - sage: def square(f): - ....: @sage_wraps(f) - ....: def new_f(x): - ....: return f(x)*f(x) - ....: return new_f - sage: f = lambda x:x^2 - sage: g = square(f) - sage: g(3) # this line used to fail for some people if these command were manually entered on the sage prompt - 81 - - """ - #TRAC 9919: Workaround for bug in @update_wrapper when used with - #non-function callables. - assigned = set(assigned).intersection(set(dir(wrapped))) - #end workaround - - def f(wrapper, assigned=assigned, updated=updated): - update_wrapper(wrapper, wrapped, assigned=assigned, updated=updated) - # For backwards-compatibility with old versions of sage_wraps - wrapper.f = wrapped - # For forwards-compatibility with functools.wraps on Python 3 - wrapper.__wrapped__ = wrapped - wrapper._sage_src_ = lambda: sage_getsource(wrapped) - wrapper._sage_src_lines_ = lambda: sage_getsourcelines(wrapped) - #Getting the signature right in documentation by Sphinx (Trac 9976) - #The attribute _sage_argspec_() is read by Sphinx if present and used - #as the argspec of the function instead of using reflection. - wrapper._sage_argspec_ = lambda: sage_getargspec(wrapped) - return wrapper - return f - - # Infix operator decorator class infix_operator(object): """ @@ -299,7 +159,8 @@ def _right(self, left): return self.function(left, self.right) -def decorator_defaults(func): +@decorator +def decorator_defaults(func, *args, **kwds): """ This function allows a decorator to have default arguments. @@ -336,14 +197,11 @@ def decorator_defaults(func): (3, 4) my_fun """ - @sage_wraps(func) - def my_wrap(*args, **kwds): - if len(kwds) == 0 and len(args) == 1: - # call without parentheses - return func(*args) - else: - return lambda f: func(f, *args, **kwds) - return my_wrap + if len(kwds) == 0 and len(args) == 1: + # call without parentheses + return func(*args) + else: + return lambda f: func(f, *args, **kwds) class suboptions(object): @@ -394,8 +252,7 @@ def __call__(self, func): sage: sage_getargspec(f) ArgSpec(args=['arrow_size'], varargs='args', keywords='kwds', defaults=(2,)) """ - @sage_wraps(func) - def wrapper(*args, **kwds): + def wrapper(func, *args, **kwds): suboptions = copy(self.options) suboptions.update(kwds.pop(self.name+"options", {})) @@ -409,6 +266,7 @@ def wrapper(*args, **kwds): kwds[self.name + "options"] = suboptions return func(*args, **kwds) + return decorate(func, wrapper) # Add the options specified by @options to the signature of the wrapped # function in the Sphinx-generated documentation (Trac 9976), using the @@ -483,13 +341,13 @@ def __call__(self, func): () [('__original_opts', {'alpha': 1}), ('alpha', 1), ('rgbcolor', (0, 0, 1))] """ - @sage_wraps(func) - def wrapper(*args, **kwds): + def wrapper(func, *args, **kwds): options = copy(wrapper.options) if self.original_opts: options['__original_opts'] = kwds options.update(kwds) return func(*args, **options) + return decorate(func, wrapper) #Add the options specified by @options to the signature of the wrapped #function in the Sphinx-generated documentation (Trac 9976), using the @@ -567,48 +425,37 @@ def reset(): return wrapper -class rename_keyword(object): - def __init__(self, deprecated=None, deprecation=None, **renames): - """ - A decorator which renames keyword arguments and optionally - deprecates the new keyword. - - INPUT: +def rename_keyword(deprecated=None, deprecation=None, **renames): + """ + A decorator which renames keyword arguments and optionally + deprecates the new keyword. - - ``deprecation`` -- integer. The trac ticket number where the - deprecation was introduced. + INPUT: - - the rest of the arguments is a list of keyword arguments in the - form ``renamed_option='existing_option'``. This will have the - effect of renaming ``renamed_option`` so that the function only - sees ``existing_option``. If both ``renamed_option`` and - ``existing_option`` are passed to the function, ``existing_option`` - will override the ``renamed_option`` value. + - ``deprecation`` -- integer. The trac ticket number where the + deprecation was introduced. - EXAMPLES:: + - the rest of the arguments is a list of keyword arguments in the + form ``renamed_option='existing_option'``. This will have the + effect of renaming ``renamed_option`` so that the function only + sees ``existing_option``. If both ``renamed_option`` and + ``existing_option`` are passed to the function, ``existing_option`` + will override the ``renamed_option`` value. - sage: from sage.misc.decorators import rename_keyword - sage: r = rename_keyword(color='rgbcolor') - sage: r.renames - {'color': 'rgbcolor'} - sage: loads(dumps(r)).renames - {'color': 'rgbcolor'} + EXAMPLES:: - To deprecate an old keyword:: + sage: from sage.misc.decorators import rename_keyword + sage: r = rename_keyword(color='rgbcolor') + sage: r.renames + {'color': 'rgbcolor'} + sage: loads(dumps(r)).renames + {'color': 'rgbcolor'} - sage: r = rename_keyword(deprecation=13109, color='rgbcolor') - """ - assert deprecated is None, 'Use @rename_keyword(deprecation=, ...)' - self.renames = renames - self.deprecation = deprecation + To deprecate an old keyword:: - def __call__(self, func): - """ - Rename keywords. + sage: r = rename_keyword(deprecation=13109, color='rgbcolor') - EXAMPLES:: - - sage: from sage.misc.decorators import rename_keyword + sage: from sage.misc.decorators import rename_keyword sage: r = rename_keyword(color='rgbcolor') sage: def f(*args, **kwds): ....: print("{} {}".format(args, kwds)) @@ -638,94 +485,18 @@ def __call__(self, func): doctest:...: DeprecationWarning: use the option 'new_option' instead of 'deprecated_option' See http://trac.sagemath.org/13109 for details. () {'new_option': 1} - """ - @sage_wraps(func) - def wrapper(*args, **kwds): - for old_name, new_name in self.renames.items(): - if old_name in kwds and new_name not in kwds: - if self.deprecation is not None: - from sage.misc.superseded import deprecation - deprecation(self.deprecation, "use the option " - "%r instead of %r" % (new_name, old_name)) - kwds[new_name] = kwds[old_name] - del kwds[old_name] - return func(*args, **kwds) - - return wrapper - -class specialize: - r""" - A decorator generator that returns a decorator that in turn - returns a specialized function for function ``f``. In other words, - it returns a function that acts like ``f`` with arguments - ``*args`` and ``**kwargs`` supplied. - - INPUT: - - - ``*args``, ``**kwargs`` -- arguments to specialize the function for. - - OUTPUT: - - - a decorator that accepts a function ``f`` and specializes it - with ``*args`` and ``**kwargs`` - - EXAMPLES:: - - sage: f = specialize(5)(lambda x, y: x+y) - sage: f(10) - 15 - sage: f(5) - 10 - sage: @specialize("Bon Voyage") - ....: def greet(greeting, name): - ....: print("{0}, {1}!".format(greeting, name)) - sage: greet("Monsieur Jean Valjean") - Bon Voyage, Monsieur Jean Valjean! - sage: greet(name = 'Javert') - Bon Voyage, Javert! """ - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs - - def __call__(self, f): - return sage_wraps(f)(partial(f, *self.args, **self.kwargs)) - -def decorator_keywords(func): - r""" - A decorator for decorators with optional keyword arguments. - - EXAMPLES:: - - sage: from sage.misc.decorators import decorator_keywords - sage: @decorator_keywords - ....: def preprocess(f=None, processor=None): - ....: def wrapper(*args, **kwargs): - ....: if processor is not None: - ....: args, kwargs = processor(*args, **kwargs) - ....: return f(*args, **kwargs) - ....: return wrapper - - This decorator can be called with and without arguments:: - - sage: @preprocess - ....: def foo(x): return x - sage: foo(None) - sage: foo(1) - 1 - - sage: def normalize(x): return ((0,),{}) if x is None else ((x,),{}) - sage: @preprocess(processor=normalize) - ....: def foo(x): return x - sage: foo(None) - 0 - sage: foo(1) - 1 - """ - @sage_wraps(func) - def wrapped(f=None, **kwargs): - if f is None: - return sage_wraps(func)(lambda f:func(f, **kwargs)) - else: - return func(f, **kwargs) - return wrapped + assert deprecated is None, 'Use @rename_keyword(deprecation=, ...)' + + def wrapper(func, *args, **kwds): + for old_name, new_name in renames.items(): + if old_name in kwds and new_name not in kwds: + if deprecation is not None: + from sage.misc.superseded import deprecation as print_deprecation + print_deprecation(deprecation, "use the option " + "%r instead of %r" % (new_name, old_name)) + kwds[new_name] = kwds[old_name] + del kwds[old_name] + return func(*args, **kwds) + + return decorator(wrapper) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index f0bc322b2fe..35382bb443c 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -27,6 +27,8 @@ from warnings import warn import inspect +from decorator import decorate, decorator + from sage.misc.lazy_attribute import lazy_attribute @@ -181,109 +183,90 @@ def experimental_warning(trac_number, message, stacklevel=4): warning(trac_number, message, FutureWarning, stacklevel) -class experimental(object): - def __init__(self, trac_number, stacklevel=4): - """ - A decorator which warns about the experimental/unstable status of - the decorated class/method/function. - - INPUT: - - - ``trac_number`` -- an integer. The trac ticket number where this - code was introduced. - - - ``stack_level`` -- (default: ``4``) an integer. This is passed on to - :func:`warnings.warn`. - - EXAMPLES:: - - sage: @sage.misc.superseded.experimental(trac_number=79997) - ....: def foo(*args, **kwargs): - ....: print("{} {}".format(args, kwargs)) - sage: foo(7, what='Hello') - doctest:...: FutureWarning: This class/method/function is - marked as experimental. It, its functionality or its - interface might change without a formal deprecation. - See https://trac.sagemath.org/79997 for details. - (7,) {'what': 'Hello'} - - :: - - sage: class bird(SageObject): - ....: @sage.misc.superseded.experimental(trac_number=99999) - ....: def __init__(self, *args, **kwargs): - ....: print("piep {} {}".format(args, kwargs)) - sage: _ = bird(99) - doctest:...: FutureWarning: This class/method/function is - marked as experimental. It, its functionality or its - interface might change without a formal deprecation. - See https://trac.sagemath.org/99999 for details. - piep (99,) {} - - TESTS: - - The following test works together with the doc-test for - :meth:`__experimental_self_test` to demonstrate that warnings are issued only - once, even in doc-tests (see :trac:`20601`). - :: - - sage: from sage.misc.superseded import __experimental_self_test - sage: _ = __experimental_self_test("A") - doctest:...: FutureWarning: This class/method/function is - marked as experimental. It, its functionality or its - interface might change without a formal deprecation. - See https://trac.sagemath.org/88888 for details. - I'm A - - .. SEEALSO:: - - :func:`experimental`, - :func:`warning`, - :func:`deprecation`. - """ - self.trac_number = trac_number - self.stacklevel = stacklevel +def experimental(trac_number, stacklevel=4): + """ + A decorator which warns about the experimental/unstable status of + the decorated class/method/function. - def __call__(self, func): - """ - Print experimental warning. + INPUT: - INPUT: + - ``trac_number`` -- an integer. The trac ticket number where this + code was introduced. - - ``func`` -- the function to decorate. + - ``stack_level`` -- (default: ``4``) an integer. This is passed on to + :func:`warnings.warn`. - OUTPUT: + EXAMPLES:: - The wrapper to this function. + sage: @sage.misc.superseded.experimental(trac_number=79997) + ....: def foo(*args, **kwargs): + ....: print("{} {}".format(args, kwargs)) + sage: foo(7, what='Hello') + doctest:...: FutureWarning: This class/method/function is + marked as experimental. It, its functionality or its + interface might change without a formal deprecation. + See https://trac.sagemath.org/79997 for details. + (7,) {'what': 'Hello'} + + :: + + sage: class bird(SageObject): + ....: @sage.misc.superseded.experimental(trac_number=99999) + ....: def __init__(self, *args, **kwargs): + ....: print("piep {} {}".format(args, kwargs)) + sage: _ = bird(99) + doctest:...: FutureWarning: This class/method/function is + marked as experimental. It, its functionality or its + interface might change without a formal deprecation. + See https://trac.sagemath.org/99999 for details. + piep (99,) {} - TESTS:: + TESTS: - sage: def foo(*args, **kwargs): - ....: print("{} {}".format(args, kwargs)) - sage: from sage.misc.superseded import experimental - sage: ex_foo = experimental(trac_number=99399)(foo) - sage: ex_foo(3, what='Hello') - doctest:...: FutureWarning: This class/method/function is - marked as experimental. It, its functionality or its - interface might change without a formal deprecation. - See https://trac.sagemath.org/99399 for details. - (3,) {'what': 'Hello'} - """ - from sage.misc.decorators import sage_wraps - @sage_wraps(func) - def wrapper(*args, **kwds): - if not wrapper._already_issued: - experimental_warning(self.trac_number, - 'This class/method/function is marked as ' - 'experimental. It, its functionality or its ' - 'interface might change without a ' - 'formal deprecation.', - self.stacklevel) - wrapper._already_issued = True - return func(*args, **kwds) - wrapper._already_issued = False - - return wrapper + The following test works together with the doc-test for + :meth:`__experimental_self_test` to demonstrate that warnings are issued only + once, even in doc-tests (see :trac:`20601`). + :: + + sage: from sage.misc.superseded import __experimental_self_test + sage: _ = __experimental_self_test("A") + doctest:...: FutureWarning: This class/method/function is + marked as experimental. It, its functionality or its + interface might change without a formal deprecation. + See https://trac.sagemath.org/88888 for details. + I'm A + + :: + + sage: def foo(*args, **kwargs): + ....: print("{} {}".format(args, kwargs)) + sage: from sage.misc.superseded import experimental + sage: ex_foo = experimental(trac_number=99399)(foo) + sage: ex_foo(3, what='Hello') + doctest:...: FutureWarning: This class/method/function is + marked as experimental. It, its functionality or its + interface might change without a formal deprecation. + See https://trac.sagemath.org/99399 for details. + (3,) {'what': 'Hello'} + + .. SEEALSO:: + + :func:`experimental`, + :func:`warning`, + :func:`deprecation`. + """ + def wrapper(func, *args, **kwds): + if not wrapper._already_issued: + experimental_warning(trac_number, + 'This class/method/function is marked as ' + 'experimental. It, its functionality or its ' + 'interface might change without a ' + 'formal deprecation.', + stacklevel) + wrapper._already_issued = True + return func(*args, **kwds) + wrapper._already_issued = False + return decorator(wrapper) class __experimental_self_test(object): diff --git a/src/sage/rings/qqbar_decorators.py b/src/sage/rings/qqbar_decorators.py index 621e5ded0d7..1535aa11e01 100644 --- a/src/sage/rings/qqbar_decorators.py +++ b/src/sage/rings/qqbar_decorators.py @@ -11,10 +11,10 @@ ========== """ -from sage.misc.decorators import decorator_keywords, sage_wraps +from decorator import decorate -@decorator_keywords -def handle_AA_and_QQbar(func): +@decorator +def handle_AA_and_QQbar(func, *args, **kwds): r""" Decorator to call a function that only accepts arguments in number fields. @@ -32,131 +32,124 @@ def handle_AA_and_QQbar(func): See https://mathoverflow.net/questions/304525 for a discussion of why a simple attempt to overcome this limitation didn't work. + + TESTS:: + + sage: from sage.rings.qqbar_decorators import handle_AA_and_QQbar + sage: @handle_AA_and_QQbar + ....: def return_base_ring(x): + ....: return x.base_ring() + + sage: P. = QQbar[] + sage: return_base_ring(x) + Rational Field + + sage: P. = QQbar[] + sage: return_base_ring(y) + Rational Field + + sage: return_base_ring(ideal(y,z)) + Rational Field + + Check that :trac:`29468` is fixed:: + + sage: J = QQbar['x,y'].ideal('x^2 - y') + sage: type(J.groebner_basis()) + + sage: J.groebner_basis().is_immutable() + True + + :: + + sage: @handle_AA_and_QQbar + ....: def f(x): + ....: print(x.ring().base_ring()) + ....: return x + sage: R. = QQbar[] + sage: s = Sequence([x, R(sqrt(2)) * y], immutable=True) + sage: t = f(s) + Number Field in a with defining polynomial y^2 - 2 + sage: t.ring().base_ring() + Algebraic Field + sage: t.is_immutable() + True + sage: s == t + True """ - @sage_wraps(func) - def wrapper(*args, **kwds): - - """ - TESTS:: - - sage: from sage.rings.qqbar_decorators import handle_AA_and_QQbar - sage: @handle_AA_and_QQbar - ....: def return_base_ring(x): - ....: return x.base_ring() - - sage: P. = QQbar[] - sage: return_base_ring(x) - Rational Field - - sage: P. = QQbar[] - sage: return_base_ring(y) - Rational Field - - sage: return_base_ring(ideal(y,z)) - Rational Field - - Check that :trac:`29468` is fixed:: - - sage: J = QQbar['x,y'].ideal('x^2 - y') - sage: type(J.groebner_basis()) - - sage: J.groebner_basis().is_immutable() - True - - :: - - sage: @handle_AA_and_QQbar - ....: def f(x): - ....: print(x.ring().base_ring()) - ....: return x - sage: R. = QQbar[] - sage: s = Sequence([x, R(sqrt(2)) * y], immutable=True) - sage: t = f(s) - Number Field in a with defining polynomial y^2 - 2 - sage: t.ring().base_ring() - Algebraic Field - sage: t.is_immutable() - True - sage: s == t - True - """ - - from sage.misc.flatten import flatten - from sage.rings.polynomial.polynomial_element import Polynomial - from sage.rings.polynomial.multi_polynomial import MPolynomial - from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, is_PolynomialSequence - from sage.rings.ideal import Ideal, Ideal_generic - from sage.rings.qqbar import is_AlgebraicField_common, number_field_elements_from_algebraics - - if not any(isinstance(a, (Polynomial, MPolynomial, Ideal_generic)) - and is_AlgebraicField_common(a.base_ring()) - or is_PolynomialSequence(a) - and is_AlgebraicField_common(a.ring().base_ring()) for a in args): - return func(*args, **kwds) - - polynomials = [] - - for a in flatten(args, ltypes=(list, tuple, set)): - if isinstance(a, Ideal_generic): - polynomials.extend(a.gens()) - elif isinstance(a, Polynomial): - polynomials.append(a) - elif isinstance(a, MPolynomial): - polynomials.append(a) - - orig_elems = flatten([p.coefficients() for p in polynomials]) - - # We need minimal=True if these elements are over AA, because - # same_field=True might trigger an exception otherwise. - - numfield, new_elems, morphism = number_field_elements_from_algebraics(orig_elems, same_field=True, minimal=True) - - elem_dict = dict(zip(orig_elems, new_elems)) - - def forward_map(item): - if isinstance(item, Ideal_generic): - return Ideal([forward_map(g) for g in item.gens()]) - elif isinstance(item, Polynomial): - return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) - elif isinstance(item, MPolynomial): - return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) - elif is_PolynomialSequence(item): - return PolynomialSequence(map(forward_map, item), - immutable=item.is_immutable()) - elif isinstance(item, list): - return list(map(forward_map, item)) - elif isinstance(item, dict): - return {k: forward_map(v) for k,v in item.items()} - elif isinstance(item, tuple): - return tuple(map(forward_map, item)) - elif isinstance(item, set): - return set(map(forward_map, list(item))) - else: - return item - - def reverse_map(item): - if isinstance(item, Ideal_generic): - return Ideal([reverse_map(g) for g in item.gens()]) - elif isinstance(item, Polynomial): - return item.map_coefficients(morphism) - elif isinstance(item, MPolynomial): - return item.map_coefficients(morphism) - elif is_PolynomialSequence(item): - return PolynomialSequence(map(reverse_map, item), - immutable=item.is_immutable()) - elif isinstance(item, list): - return list(map(reverse_map, item)) - elif isinstance(item, tuple): - return tuple(map(reverse_map, item)) - elif isinstance(item, set): - return set(map(reverse_map, list(item))) - else: - return item - - args = forward_map(args) - kwds = forward_map(kwds) - - return reverse_map(func(*args, **kwds)) - - return wrapper + from sage.misc.flatten import flatten + from sage.rings.polynomial.polynomial_element import Polynomial + from sage.rings.polynomial.multi_polynomial import MPolynomial + from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, is_PolynomialSequence + from sage.rings.ideal import Ideal, Ideal_generic + from sage.rings.qqbar import is_AlgebraicField_common, number_field_elements_from_algebraics + + if not any(isinstance(a, (Polynomial, MPolynomial, Ideal_generic)) + and is_AlgebraicField_common(a.base_ring()) + or is_PolynomialSequence(a) + and is_AlgebraicField_common(a.ring().base_ring()) for a in args): + return func(*args, **kwds) + + polynomials = [] + + for a in flatten(args, ltypes=(list, tuple, set)): + if isinstance(a, Ideal_generic): + polynomials.extend(a.gens()) + elif isinstance(a, Polynomial): + polynomials.append(a) + elif isinstance(a, MPolynomial): + polynomials.append(a) + + orig_elems = flatten([p.coefficients() for p in polynomials]) + + # We need minimal=True if these elements are over AA, because + # same_field=True might trigger an exception otherwise. + + numfield, new_elems, morphism = number_field_elements_from_algebraics(orig_elems, same_field=True, minimal=True) + + elem_dict = dict(zip(orig_elems, new_elems)) + + def forward_map(item): + if isinstance(item, Ideal_generic): + return Ideal([forward_map(g) for g in item.gens()]) + elif isinstance(item, Polynomial): + return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) + elif isinstance(item, MPolynomial): + return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) + elif is_PolynomialSequence(item): + return PolynomialSequence(map(forward_map, item), + immutable=item.is_immutable()) + elif isinstance(item, list): + return list(map(forward_map, item)) + elif isinstance(item, dict): + return {k: forward_map(v) for k,v in item.items()} + elif isinstance(item, tuple): + return tuple(map(forward_map, item)) + elif isinstance(item, set): + return set(map(forward_map, list(item))) + else: + return item + + def reverse_map(item): + if isinstance(item, Ideal_generic): + return Ideal([reverse_map(g) for g in item.gens()]) + elif isinstance(item, Polynomial): + return item.map_coefficients(morphism) + elif isinstance(item, MPolynomial): + return item.map_coefficients(morphism) + elif is_PolynomialSequence(item): + return PolynomialSequence(map(reverse_map, item), + immutable=item.is_immutable()) + elif isinstance(item, list): + return list(map(reverse_map, item)) + elif isinstance(item, tuple): + return tuple(map(reverse_map, item)) + elif isinstance(item, set): + return set(map(reverse_map, list(item))) + else: + return item + + args = forward_map(args) + kwds = forward_map(kwds) + + return reverse_map(func(*args, **kwds)) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 5755bd071e8..f2fdaca3992 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -290,6 +290,7 @@ continue down the MRO and find the ``_add_`` method in the category. cimport cython from cpython cimport * from cpython.ref cimport PyObject +from decorator import decorater from sage.ext.stdsage cimport * @@ -314,7 +315,6 @@ from sage.misc.classcall_metaclass cimport ClasscallMetaclass from sage.arith.long cimport integer_check_long_py from sage.arith.power cimport generic_power as arith_generic_power from sage.arith.numerical_approx cimport digits_to_bits -from sage.misc.decorators import sage_wraps from sage.misc.superseded import deprecation @@ -4245,8 +4245,8 @@ def coercion_traceback(dump=True): else: return coercion_model.exception_stack() - -def coerce_binop(method): +@decorater +def coerce_binop(method, self, other, *args, **kwargs): r""" Decorator for a binary operator method for applying coercion to the arguments before calling the method. @@ -4343,14 +4343,11 @@ def coerce_binop(method): ... TypeError: algorithm 1 not supported """ - @sage_wraps(method) - def new_method(self, other, *args, **kwargs): - if have_same_parent(self, other): - return method(self, other, *args, **kwargs) + if have_same_parent(self, other): + return method(self, other, *args, **kwargs) + else: + a, b = coercion_model.canonical_coercion(self, other) + if a is self: + return method(a, b, *args, **kwargs) else: - a, b = coercion_model.canonical_coercion(self, other) - if a is self: - return method(a, b, *args, **kwargs) - else: - return getattr(a, method.__name__)(b, *args, **kwargs) - return new_method + return getattr(a, method.__name__)(b, *args, **kwargs) diff --git a/src/sage/structure/mutability.pyx b/src/sage/structure/mutability.pyx index 280df117248..775ee5daf02 100644 --- a/src/sage/structure/mutability.pyx +++ b/src/sage/structure/mutability.pyx @@ -11,8 +11,7 @@ Mutability Cython Implementation # https://www.gnu.org/licenses/ ########################################################################## -from sage.misc.decorators import sage_wraps - +from decorator import decorater cdef class Mutability: @@ -72,7 +71,8 @@ cdef class Mutability: ########################################################################## ## Method decorators for mutating methods resp. methods that assume immutability -def require_mutable(f): +@decorater +def require_mutable(f, self, *args,**kwds): """ A decorator that requires mutability for a method to be called. @@ -111,15 +111,12 @@ def require_mutable(f): - Simon King """ - @sage_wraps(f) - def new_f(self, *args,**kwds): - if getattr(self, '_is_immutable', False): - raise ValueError("%s instance is immutable, %s must not be called" % (type(self), repr(f))) - return f(self, *args, **kwds) - return new_f - + if getattr(self, '_is_immutable', False): + raise ValueError("%s instance is immutable, %s must not be called" % (type(self), repr(f))) + return f(self, *args, **kwds) -def require_immutable(f): +@decorater +def require_immutable(f, self, *args, **kwds): """ A decorator that requires immutability for a method to be called. @@ -158,9 +155,6 @@ def require_immutable(f): - Simon King """ - @sage_wraps(f) - def new_f(self, *args,**kwds): - if not getattr(self,'_is_immutable',False): - raise ValueError("%s instance is mutable, %s must not be called" % (type(self), repr(f))) - return f(self, *args,**kwds) - return new_f + if not getattr(self,'_is_immutable',False): + raise ValueError("%s instance is mutable, %s must not be called" % (type(self), repr(f))) + return f(self, *args,**kwds) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 48bcf01e6a7..6507b91da94 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -306,6 +306,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.ext.cplusplus cimport ccrepr, ccreadstr from inspect import isfunction +from decorator import decorater import operator from .ring import SR import sage.rings.integer @@ -322,7 +323,6 @@ from sage.symbolic.function import get_sfunction_from_serial, SymbolicFunction cimport sage.symbolic.comparison from sage.rings.rational import Rational from sage.misc.derivative import multi_derivative -from sage.misc.decorators import sage_wraps from sage.rings.infinity import AnInfinity, infinity, minus_infinity, unsigned_infinity from sage.misc.decorators import rename_keyword from sage.structure.dynamic_class import dynamic_class @@ -12803,8 +12803,8 @@ def solve_diophantine(f, *args, **kwds): f = SR(f) return f.solve_diophantine(*args, **kwds) - -def _eval_on_operands(f): +@decorater +def _eval_on_operands(f, ex, *args, **kwds): """ Given a function ``f``, return a new function which takes a symbolic expression as first argument and prepends the operands of that @@ -12829,12 +12829,9 @@ def _eval_on_operands(f): sage: print(g.__doc__.strip()) Some documentation. """ - @sage_wraps(f) - def new_f(ex, *args, **kwds): - new_args = list(ex._unpack_operands()) - new_args.extend(args) - return f(ex, *new_args, **kwds) - return new_f + new_args = list(ex._unpack_operands()) + new_args.extend(args) + return f(ex, *new_args, **kwds) cdef dict dynamic_class_cache = {} diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index b07e9c100cf..a4ba68e6c61 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -564,7 +564,7 @@ def _wrapper(self, format, *args, **kwds): if not os.path.exists(refdir): continue logger.info('Building bibliography') - self._build_bibliography(lang, format, *args, **kwds) + #self._build_bibliography(lang, format, *args, **kwds) logger.info('Bibliography finished, building dependent manuals') self._build_everything_except_bibliography(lang, format, *args, **kwds) # The html refman must be build at the end to ensure correct