Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Readd unused functions, but deprecated them
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiasdiez committed Nov 11, 2020
1 parent 3a433f7 commit 8b05582
Show file tree
Hide file tree
Showing 2 changed files with 307 additions and 3 deletions.
278 changes: 275 additions & 3 deletions src/sage/misc/decorators.py
Expand Up @@ -8,7 +8,9 @@
- 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) -- put this file into the reference manual.
- 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
"""
#*****************************************************************************
Expand All @@ -28,13 +30,154 @@
from functools import (partial, update_wrapper, WRAPPER_ASSIGNMENTS,
WRAPPER_UPDATES)
from copy import copy
from sage.misc.superseded import deprecated

from decorator import decorate, decorator
from decorator import decorator

from sage.misc.sageinspect import (sage_getsource, sage_getsourcelines,
sage_getargspec)
from inspect import ArgSpec

@deprecated(30884, 'replaced by the decorator library')
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.<x,y> = 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):
"""
Expand Down Expand Up @@ -159,6 +302,54 @@ def _right(self, left):
return self.function(left, self.right)


@deprecated(30884, 'replaced by the decorator library')
def decorator_defaults(func):
"""
This function allows a decorator to have default arguments.
Normally, a decorator can be called with or without arguments.
However, the two cases call for different types of return values.
If a decorator is called with no parentheses, it should be run
directly on the function. However, if a decorator is called with
parentheses (i.e., arguments), then it should return a function
that is then in turn called with the defined function as an
argument.
This decorator allows us to have these default arguments without
worrying about the return type.
EXAMPLES::
sage: from sage.misc.decorators import decorator_defaults
sage: @decorator_defaults
....: def my_decorator(f,*args,**kwds):
....: print(kwds)
....: print(args)
....: print(f.__name__)
sage: @my_decorator
....: def my_fun(a,b):
....: return a,b
{}
()
my_fun
sage: @my_decorator(3,4,c=1,d=2)
....: def my_fun(a,b):
....: return a,b
{'c': 1, 'd': 2}
(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


class suboptions(object):
def __init__(self, name, **options):
"""
Expand Down Expand Up @@ -301,7 +492,7 @@ def wrapper(func, *args, **kwds):
options['__original_opts'] = kwds
options.update(kwds)
return func(*args, **options)

#Add the options specified by @options to the signature of the wrapped
#function in the Sphinx-generated documentation (Trac 9976), using the
#special attribute _sage_argspec_ (see e.g. sage.misc.sageinspect)
Expand Down Expand Up @@ -453,3 +644,84 @@ def wrapper(func, *args, **kwds):
return func(*args, **kwds)

return decorator(wrapper)


@deprecated(30884, 'not used anymore')
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))


@deprecated(30884, 'replaced by the decorator library')
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
32 changes: 32 additions & 0 deletions src/sage/misc/superseded.py
Expand Up @@ -101,6 +101,38 @@ def deprecation(trac_number, message, stacklevel=4):
"""
warning(trac_number, message, DeprecationWarning, stacklevel)

def deprecated(trac_number, message, stacklevel=4):
"""
Decorator that issues a deprecation warning on use.
INPUT:
- ``trac_number`` -- integer. The trac ticket number where the
deprecation is introduced.
- ``message`` -- string. An explanation why things are deprecated
and by what it should be replaced.
- ``stack_level`` -- (default: ``4``) an integer. This is passed on to
:func:`warnings.warn`.
EXAMPLES::
sage: @sage.misc.superseded.deprecated(13109, 'the function foo is replaced by bar')
sage: def foo(): pass
sage: foo()
doctest:...: DeprecationWarning: the function foo is replaced by bar
See http://trac.sagemath.org/13109 for details.
.. SEEALSO::
:func:`deprecation`
"""
def wrapper(func, *args, **kw):
deprecation(trac_number, message, stacklevel)
func(args, kw)
return decorator(wrapper)


def warning(trac_number, message, warning_class=Warning, stacklevel=3):
r"""
Expand Down

0 comments on commit 8b05582

Please sign in to comment.