import inspect
from sympy.core.decorators import wraps
from sympy.core.compatibility import iterable
[docs]def threaded_factory(func, use_add):
"""A factory for ``threaded`` decorators. """
from sympy.core import sympify
from sympy.matrices import Matrix
@wraps(func)
def threaded_func(expr, *args, **kwargs):
if isinstance(expr, Matrix):
return expr.applyfunc(lambda f: func(f, *args, **kwargs))
elif iterable(expr):
try:
return expr.__class__([func(f, *args, **kwargs) for f in expr])
except TypeError:
return expr
else:
expr = sympify(expr)
if use_add and expr.is_Add:
return expr.__class__(*[ func(f, *args, **kwargs) for f in expr.args ])
elif expr.is_Relational:
return expr.__class__(func(expr.lhs, *args, **kwargs),
func(expr.rhs, *args, **kwargs))
else:
return func(expr, *args, **kwargs)
return threaded_func
[docs]def threaded(func):
"""Apply ``func`` to sub--elements of an object, including :class:`Add`.
This decorator is intended to make it uniformly possible to apply a
function to all elements of composite objects, e.g. matrices, lists, tuples
and other iterable containers, or just expressions.
This version of :func:`threaded` decorator allows threading over
elements of :class:`Add` class. If this behavior is not desirable
use :func:`xthreaded` decorator.
Functions using this decorator must have the following signature::
@threaded
def function(expr, *args, **kwargs):
"""
return threaded_factory(func, True)
[docs]def xthreaded(func):
"""Apply ``func`` to sub--elements of an object, excluding :class:`Add`.
This decorator is intended to make it uniformly possible to apply a
function to all elements of composite objects, e.g. matrices, lists, tuples
and other iterable containers, or just expressions.
This version of :func:`threaded` decorator disallows threading over
elements of :class:`Add` class. If this behavior is not desirable
use :func:`threaded` decorator.
Functions using this decorator must have the following signature::
@xthreaded
def function(expr, *args, **kwargs):
"""
return threaded_factory(func, False)
[docs]def conserve_mpmath_dps(func):
"""After the function finishes, resets the value of mpmath.mp.dps to
the value it had before the function was run."""
import functools
import mpmath
def func_wrapper():
dps = mpmath.mp.dps
try:
func()
finally:
mpmath.mp.dps = dps
func_wrapper = functools.update_wrapper(func_wrapper, func)
return func_wrapper
[docs]class no_attrs_in_subclass(object):
"""Don't 'inherit' certain attributes from a base class
>>> from sympy.utilities.decorator import no_attrs_in_subclass
>>> class A(object):
... x = 'test'
>>> A.x = no_attrs_in_subclass(A, A.x)
>>> class B(A):
... pass
>>> hasattr(A, 'x')
True
>>> hasattr(B, 'x')
False
"""
def __init__(self, cls, f):
self.cls = cls
self.f = f
def __get__(self, instance, owner=None):
if owner == self.cls:
if hasattr(self.f, '__get__'):
return self.f.__get__(instance, owner)
return self.f
raise AttributeError
[docs]def doctest_depends_on(exe=None, modules=None, disable_viewers=None):
"""Adds metadata about the depenencies which need to be met for doctesting
the docstrings of the decorated objects."""
pyglet = False
if modules is not None and 'pyglet' in modules:
pyglet = True
def depends_on_deco(fn):
fn._doctest_depends_on = dict(exe=exe, modules=modules,
disable_viewers=disable_viewers,
pyglet=pyglet)
# once we drop py2.5 support and use class decorators this evaluates
# to True
if inspect.isclass(fn):
fn._doctest_depdends_on = no_attrs_in_subclass(fn, fn._doctest_depends_on)
return fn
return depends_on_deco