pruned venvs
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
"""
|
||||
Module containing private utility functions
|
||||
===========================================
|
||||
|
||||
The ``scipy._lib`` namespace is empty (for now). Tests for all
|
||||
utilities in submodules of ``_lib`` can be run with::
|
||||
|
||||
from scipy import _lib
|
||||
_lib.test()
|
||||
|
||||
"""
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
from scipy._lib._testutils import PytestTester
|
||||
test = PytestTester(__name__)
|
||||
del PytestTester
|
||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,227 +0,0 @@
|
||||
from . import _ccallback_c
|
||||
|
||||
import ctypes
|
||||
|
||||
PyCFuncPtr = ctypes.CFUNCTYPE(ctypes.c_void_p).__bases__[0]
|
||||
|
||||
ffi = None
|
||||
|
||||
class CData(object):
|
||||
pass
|
||||
|
||||
def _import_cffi():
|
||||
global ffi, CData
|
||||
|
||||
if ffi is not None:
|
||||
return
|
||||
|
||||
try:
|
||||
import cffi
|
||||
ffi = cffi.FFI()
|
||||
CData = ffi.CData
|
||||
except ImportError:
|
||||
ffi = False
|
||||
|
||||
|
||||
class LowLevelCallable(tuple):
|
||||
"""
|
||||
Low-level callback function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
function : {PyCapsule, ctypes function pointer, cffi function pointer}
|
||||
Low-level callback function.
|
||||
user_data : {PyCapsule, ctypes void pointer, cffi void pointer}
|
||||
User data to pass on to the callback function.
|
||||
signature : str, optional
|
||||
Signature of the function. If omitted, determined from *function*,
|
||||
if possible.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
function
|
||||
Callback function given
|
||||
user_data
|
||||
User data given
|
||||
signature
|
||||
Signature of the function.
|
||||
|
||||
Methods
|
||||
-------
|
||||
from_cython
|
||||
Class method for constructing callables from Cython C-exported
|
||||
functions.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The argument ``function`` can be one of:
|
||||
|
||||
- PyCapsule, whose name contains the C function signature
|
||||
- ctypes function pointer
|
||||
- cffi function pointer
|
||||
|
||||
The signature of the low-level callback must match one of those expected
|
||||
by the routine it is passed to.
|
||||
|
||||
If constructing low-level functions from a PyCapsule, the name of the
|
||||
capsule must be the corresponding signature, in the format::
|
||||
|
||||
return_type (arg1_type, arg2_type, ...)
|
||||
|
||||
For example::
|
||||
|
||||
"void (double)"
|
||||
"double (double, int *, void *)"
|
||||
|
||||
The context of a PyCapsule passed in as ``function`` is used as ``user_data``,
|
||||
if an explicit value for `user_data` was not given.
|
||||
|
||||
"""
|
||||
|
||||
# Make the class immutable
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, function, user_data=None, signature=None):
|
||||
# We need to hold a reference to the function & user data,
|
||||
# to prevent them going out of scope
|
||||
item = cls._parse_callback(function, user_data, signature)
|
||||
return tuple.__new__(cls, (item, function, user_data))
|
||||
|
||||
def __repr__(self):
|
||||
return "LowLevelCallable({!r}, {!r})".format(self.function, self.user_data)
|
||||
|
||||
@property
|
||||
def function(self):
|
||||
return tuple.__getitem__(self, 1)
|
||||
|
||||
@property
|
||||
def user_data(self):
|
||||
return tuple.__getitem__(self, 2)
|
||||
|
||||
@property
|
||||
def signature(self):
|
||||
return _ccallback_c.get_capsule_signature(tuple.__getitem__(self, 0))
|
||||
|
||||
def __getitem__(self, idx):
|
||||
raise ValueError()
|
||||
|
||||
@classmethod
|
||||
def from_cython(cls, module, name, user_data=None, signature=None):
|
||||
"""
|
||||
Create a low-level callback function from an exported Cython function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
module : module
|
||||
Cython module where the exported function resides
|
||||
name : str
|
||||
Name of the exported function
|
||||
user_data : {PyCapsule, ctypes void pointer, cffi void pointer}, optional
|
||||
User data to pass on to the callback function.
|
||||
signature : str, optional
|
||||
Signature of the function. If omitted, determined from *function*.
|
||||
|
||||
"""
|
||||
try:
|
||||
function = module.__pyx_capi__[name]
|
||||
except AttributeError:
|
||||
raise ValueError("Given module is not a Cython module with __pyx_capi__ attribute")
|
||||
except KeyError:
|
||||
raise ValueError("No function {!r} found in __pyx_capi__ of the module".format(name))
|
||||
return cls(function, user_data, signature)
|
||||
|
||||
@classmethod
|
||||
def _parse_callback(cls, obj, user_data=None, signature=None):
|
||||
_import_cffi()
|
||||
|
||||
if isinstance(obj, LowLevelCallable):
|
||||
func = tuple.__getitem__(obj, 0)
|
||||
elif isinstance(obj, PyCFuncPtr):
|
||||
func, signature = _get_ctypes_func(obj, signature)
|
||||
elif isinstance(obj, CData):
|
||||
func, signature = _get_cffi_func(obj, signature)
|
||||
elif _ccallback_c.check_capsule(obj):
|
||||
func = obj
|
||||
else:
|
||||
raise ValueError("Given input is not a callable or a low-level callable (pycapsule/ctypes/cffi)")
|
||||
|
||||
if isinstance(user_data, ctypes.c_void_p):
|
||||
context = _get_ctypes_data(user_data)
|
||||
elif isinstance(user_data, CData):
|
||||
context = _get_cffi_data(user_data)
|
||||
elif user_data is None:
|
||||
context = 0
|
||||
elif _ccallback_c.check_capsule(user_data):
|
||||
context = user_data
|
||||
else:
|
||||
raise ValueError("Given user data is not a valid low-level void* pointer (pycapsule/ctypes/cffi)")
|
||||
|
||||
return _ccallback_c.get_raw_capsule(func, signature, context)
|
||||
|
||||
|
||||
#
|
||||
# ctypes helpers
|
||||
#
|
||||
|
||||
def _get_ctypes_func(func, signature=None):
|
||||
# Get function pointer
|
||||
func_ptr = ctypes.cast(func, ctypes.c_void_p).value
|
||||
|
||||
# Construct function signature
|
||||
if signature is None:
|
||||
signature = _typename_from_ctypes(func.restype) + " ("
|
||||
for j, arg in enumerate(func.argtypes):
|
||||
if j == 0:
|
||||
signature += _typename_from_ctypes(arg)
|
||||
else:
|
||||
signature += ", " + _typename_from_ctypes(arg)
|
||||
signature += ")"
|
||||
|
||||
return func_ptr, signature
|
||||
|
||||
|
||||
def _typename_from_ctypes(item):
|
||||
if item is None:
|
||||
return "void"
|
||||
elif item is ctypes.c_void_p:
|
||||
return "void *"
|
||||
|
||||
name = item.__name__
|
||||
|
||||
pointer_level = 0
|
||||
while name.startswith("LP_"):
|
||||
pointer_level += 1
|
||||
name = name[3:]
|
||||
|
||||
if name.startswith('c_'):
|
||||
name = name[2:]
|
||||
|
||||
if pointer_level > 0:
|
||||
name += " " + "*"*pointer_level
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def _get_ctypes_data(data):
|
||||
# Get voidp pointer
|
||||
return ctypes.cast(data, ctypes.c_void_p).value
|
||||
|
||||
|
||||
#
|
||||
# CFFI helpers
|
||||
#
|
||||
|
||||
def _get_cffi_func(func, signature=None):
|
||||
# Get function pointer
|
||||
func_ptr = ffi.cast('uintptr_t', func)
|
||||
|
||||
# Get signature
|
||||
if signature is None:
|
||||
signature = ffi.getctype(ffi.typeof(func)).replace('(*)', ' ')
|
||||
|
||||
return func_ptr, signature
|
||||
|
||||
|
||||
def _get_cffi_data(data):
|
||||
# Get pointer
|
||||
return ffi.cast('uintptr_t', data)
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,105 +0,0 @@
|
||||
"""
|
||||
Module for testing automatic garbage collection of objects
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
|
||||
set_gc_state - enable or disable garbage collection
|
||||
gc_state - context manager for given state of garbage collector
|
||||
assert_deallocated - context manager to check for circular references on object
|
||||
|
||||
"""
|
||||
import weakref
|
||||
import gc
|
||||
import sys
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
__all__ = ['set_gc_state', 'gc_state', 'assert_deallocated']
|
||||
|
||||
|
||||
IS_PYPY = '__pypy__' in sys.modules
|
||||
|
||||
|
||||
class ReferenceError(AssertionError):
|
||||
pass
|
||||
|
||||
|
||||
def set_gc_state(state):
|
||||
""" Set status of garbage collector """
|
||||
if gc.isenabled() == state:
|
||||
return
|
||||
if state:
|
||||
gc.enable()
|
||||
else:
|
||||
gc.disable()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def gc_state(state):
|
||||
""" Context manager to set state of garbage collector to `state`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
state : bool
|
||||
True for gc enabled, False for disabled
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> with gc_state(False):
|
||||
... assert not gc.isenabled()
|
||||
>>> with gc_state(True):
|
||||
... assert gc.isenabled()
|
||||
"""
|
||||
orig_state = gc.isenabled()
|
||||
set_gc_state(state)
|
||||
yield
|
||||
set_gc_state(orig_state)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def assert_deallocated(func, *args, **kwargs):
|
||||
"""Context manager to check that object is deallocated
|
||||
|
||||
This is useful for checking that an object can be freed directly by
|
||||
reference counting, without requiring gc to break reference cycles.
|
||||
GC is disabled inside the context manager.
|
||||
|
||||
This check is not available on PyPy.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable
|
||||
Callable to create object to check
|
||||
\\*args : sequence
|
||||
positional arguments to `func` in order to create object to check
|
||||
\\*\\*kwargs : dict
|
||||
keyword arguments to `func` in order to create object to check
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> class C(object): pass
|
||||
>>> with assert_deallocated(C) as c:
|
||||
... # do something
|
||||
... del c
|
||||
|
||||
>>> class C(object):
|
||||
... def __init__(self):
|
||||
... self._circular = self # Make circular reference
|
||||
>>> with assert_deallocated(C) as c: #doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
... # do something
|
||||
... del c
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ReferenceError: Remaining reference(s) to object
|
||||
"""
|
||||
if IS_PYPY:
|
||||
raise RuntimeError("assert_deallocated is unavailable on PyPy")
|
||||
|
||||
with gc_state(False):
|
||||
obj = func(*args, **kwargs)
|
||||
ref = weakref.ref(obj)
|
||||
yield obj
|
||||
del obj
|
||||
if ref() is not None:
|
||||
raise ReferenceError("Remaining reference(s) to object")
|
||||
@@ -1,781 +0,0 @@
|
||||
"""Functions copypasted from newer versions of numpy.
|
||||
|
||||
"""
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import warnings
|
||||
from warnings import WarningMessage
|
||||
import re
|
||||
from functools import wraps
|
||||
import numpy as np
|
||||
|
||||
from scipy._lib._version import NumpyVersion
|
||||
|
||||
|
||||
if NumpyVersion(np.__version__) > '1.7.0.dev':
|
||||
_assert_warns = np.testing.assert_warns
|
||||
else:
|
||||
def _assert_warns(warning_class, func, *args, **kw):
|
||||
r"""
|
||||
Fail unless the given callable throws the specified warning.
|
||||
|
||||
This definition is copypasted from numpy 1.9.0.dev.
|
||||
The version in earlier numpy returns None.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
warning_class : class
|
||||
The class defining the warning that `func` is expected to throw.
|
||||
func : callable
|
||||
The callable to test.
|
||||
*args : Arguments
|
||||
Arguments passed to `func`.
|
||||
**kwargs : Kwargs
|
||||
Keyword arguments passed to `func`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The value returned by `func`.
|
||||
|
||||
"""
|
||||
with warnings.catch_warnings(record=True) as l:
|
||||
warnings.simplefilter('always')
|
||||
result = func(*args, **kw)
|
||||
if not len(l) > 0:
|
||||
raise AssertionError("No warning raised when calling %s"
|
||||
% func.__name__)
|
||||
if not l[0].category is warning_class:
|
||||
raise AssertionError("First warning for %s is not a "
|
||||
"%s( is %s)" % (func.__name__, warning_class, l[0]))
|
||||
return result
|
||||
|
||||
|
||||
if NumpyVersion(np.__version__) >= '1.10.0':
|
||||
from numpy import broadcast_to
|
||||
else:
|
||||
# Definition of `broadcast_to` from numpy 1.10.0.
|
||||
|
||||
def _maybe_view_as_subclass(original_array, new_array):
|
||||
if type(original_array) is not type(new_array):
|
||||
# if input was an ndarray subclass and subclasses were OK,
|
||||
# then view the result as that subclass.
|
||||
new_array = new_array.view(type=type(original_array))
|
||||
# Since we have done something akin to a view from original_array, we
|
||||
# should let the subclass finalize (if it has it implemented, i.e., is
|
||||
# not None).
|
||||
if new_array.__array_finalize__:
|
||||
new_array.__array_finalize__(original_array)
|
||||
return new_array
|
||||
|
||||
def _broadcast_to(array, shape, subok, readonly):
|
||||
shape = tuple(shape) if np.iterable(shape) else (shape,)
|
||||
array = np.array(array, copy=False, subok=subok)
|
||||
if not shape and array.shape:
|
||||
raise ValueError('cannot broadcast a non-scalar to a scalar array')
|
||||
if any(size < 0 for size in shape):
|
||||
raise ValueError('all elements of broadcast shape must be non-'
|
||||
'negative')
|
||||
broadcast = np.nditer(
|
||||
(array,), flags=['multi_index', 'refs_ok', 'zerosize_ok'],
|
||||
op_flags=['readonly'], itershape=shape, order='C').itviews[0]
|
||||
result = _maybe_view_as_subclass(array, broadcast)
|
||||
if not readonly and array.flags.writeable:
|
||||
result.flags.writeable = True
|
||||
return result
|
||||
|
||||
def broadcast_to(array, shape, subok=False):
|
||||
return _broadcast_to(array, shape, subok=subok, readonly=True)
|
||||
|
||||
|
||||
if NumpyVersion(np.__version__) >= '1.11.0':
|
||||
def get_randint(random_state):
|
||||
return random_state.randint
|
||||
else:
|
||||
# In NumPy versions previous to 1.11.0 the randint funtion and the randint
|
||||
# method of RandomState does only work with int32 values.
|
||||
def get_randint(random_state):
|
||||
def randint_patched(low, high, size, dtype=np.int32):
|
||||
low = max(low, np.iinfo(dtype).min, np.iinfo(np.int32).min)
|
||||
high = min(high, np.iinfo(dtype).max, np.iinfo(np.int32).max)
|
||||
integers = random_state.randint(low, high=high, size=size)
|
||||
return integers.astype(dtype, copy=False)
|
||||
return randint_patched
|
||||
|
||||
|
||||
if NumpyVersion(np.__version__) >= '1.9.0':
|
||||
from numpy import unique
|
||||
else:
|
||||
# the return_counts keyword was added in 1.9.0
|
||||
def unique(ar, return_index=False, return_inverse=False, return_counts=False):
|
||||
"""
|
||||
Find the unique elements of an array.
|
||||
|
||||
Returns the sorted unique elements of an array. There are three optional
|
||||
outputs in addition to the unique elements: the indices of the input array
|
||||
that give the unique values, the indices of the unique array that
|
||||
reconstruct the input array, and the number of times each unique value
|
||||
comes up in the input array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ar : array_like
|
||||
Input array. This will be flattened if it is not already 1-D.
|
||||
return_index : bool, optional
|
||||
If True, also return the indices of `ar` that result in the unique
|
||||
array.
|
||||
return_inverse : bool, optional
|
||||
If True, also return the indices of the unique array that can be used
|
||||
to reconstruct `ar`.
|
||||
return_counts : bool, optional
|
||||
If True, also return the number of times each unique value comes up
|
||||
in `ar`.
|
||||
|
||||
.. versionadded:: 1.9.0
|
||||
|
||||
Returns
|
||||
-------
|
||||
unique : ndarray
|
||||
The sorted unique values.
|
||||
unique_indices : ndarray, optional
|
||||
The indices of the first occurrences of the unique values in the
|
||||
(flattened) original array. Only provided if `return_index` is True.
|
||||
unique_inverse : ndarray, optional
|
||||
The indices to reconstruct the (flattened) original array from the
|
||||
unique array. Only provided if `return_inverse` is True.
|
||||
unique_counts : ndarray, optional
|
||||
The number of times each of the unique values comes up in the
|
||||
original array. Only provided if `return_counts` is True.
|
||||
|
||||
.. versionadded:: 1.9.0
|
||||
|
||||
Notes
|
||||
-----
|
||||
Taken over from numpy 1.12.0-dev (c8408bf9c). Omitted examples,
|
||||
see numpy documentation for those.
|
||||
|
||||
"""
|
||||
ar = np.asanyarray(ar).flatten()
|
||||
|
||||
optional_indices = return_index or return_inverse
|
||||
optional_returns = optional_indices or return_counts
|
||||
|
||||
if ar.size == 0:
|
||||
if not optional_returns:
|
||||
ret = ar
|
||||
else:
|
||||
ret = (ar,)
|
||||
if return_index:
|
||||
ret += (np.empty(0, np.bool),)
|
||||
if return_inverse:
|
||||
ret += (np.empty(0, np.bool),)
|
||||
if return_counts:
|
||||
ret += (np.empty(0, np.intp),)
|
||||
return ret
|
||||
|
||||
if optional_indices:
|
||||
perm = ar.argsort(kind='mergesort' if return_index else 'quicksort')
|
||||
aux = ar[perm]
|
||||
else:
|
||||
ar.sort()
|
||||
aux = ar
|
||||
flag = np.concatenate(([True], aux[1:] != aux[:-1]))
|
||||
|
||||
if not optional_returns:
|
||||
ret = aux[flag]
|
||||
else:
|
||||
ret = (aux[flag],)
|
||||
if return_index:
|
||||
ret += (perm[flag],)
|
||||
if return_inverse:
|
||||
iflag = np.cumsum(flag) - 1
|
||||
inv_idx = np.empty(ar.shape, dtype=np.intp)
|
||||
inv_idx[perm] = iflag
|
||||
ret += (inv_idx,)
|
||||
if return_counts:
|
||||
idx = np.concatenate(np.nonzero(flag) + ([ar.size],))
|
||||
ret += (np.diff(idx),)
|
||||
return ret
|
||||
|
||||
|
||||
if NumpyVersion(np.__version__) > '1.12.0.dev':
|
||||
polyvalfromroots = np.polynomial.polynomial.polyvalfromroots
|
||||
else:
|
||||
def polyvalfromroots(x, r, tensor=True):
|
||||
r"""
|
||||
Evaluate a polynomial specified by its roots at points x.
|
||||
|
||||
This function is copypasted from numpy 1.12.0.dev.
|
||||
|
||||
If `r` is of length `N`, this function returns the value
|
||||
|
||||
.. math:: p(x) = \prod_{n=1}^{N} (x - r_n)
|
||||
|
||||
The parameter `x` is converted to an array only if it is a tuple or a
|
||||
list, otherwise it is treated as a scalar. In either case, either `x`
|
||||
or its elements must support multiplication and addition both with
|
||||
themselves and with the elements of `r`.
|
||||
|
||||
If `r` is a 1-D array, then `p(x)` will have the same shape as `x`. If
|
||||
`r` is multidimensional, then the shape of the result depends on the
|
||||
value of `tensor`. If `tensor is ``True`` the shape will be r.shape[1:]
|
||||
+ x.shape; that is, each polynomial is evaluated at every value of `x`.
|
||||
If `tensor` is ``False``, the shape will be r.shape[1:]; that is, each
|
||||
polynomial is evaluated only for the corresponding broadcast value of
|
||||
`x`. Note that scalars have shape (,).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x : array_like, compatible object
|
||||
If `x` is a list or tuple, it is converted to an ndarray, otherwise
|
||||
it is left unchanged and treated as a scalar. In either case, `x`
|
||||
or its elements must support addition and multiplication with with
|
||||
themselves and with the elements of `r`.
|
||||
r : array_like
|
||||
Array of roots. If `r` is multidimensional the first index is the
|
||||
root index, while the remaining indices enumerate multiple
|
||||
polynomials. For instance, in the two dimensional case the roots of
|
||||
each polynomial may be thought of as stored in the columns of `r`.
|
||||
tensor : boolean, optional
|
||||
If True, the shape of the roots array is extended with ones on the
|
||||
right, one for each dimension of `x`. Scalars have dimension 0 for
|
||||
this action. The result is that every column of coefficients in `r`
|
||||
is evaluated for every element of `x`. If False, `x` is broadcast
|
||||
over the columns of `r` for the evaluation. This keyword is useful
|
||||
when `r` is multidimensional. The default value is True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
values : ndarray, compatible object
|
||||
The shape of the returned array is described above.
|
||||
|
||||
See Also
|
||||
--------
|
||||
polyroots, polyfromroots, polyval
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from numpy.polynomial.polynomial import polyvalfromroots
|
||||
>>> polyvalfromroots(1, [1,2,3])
|
||||
0.0
|
||||
>>> a = np.arange(4).reshape(2,2)
|
||||
>>> a
|
||||
array([[0, 1],
|
||||
[2, 3]])
|
||||
>>> polyvalfromroots(a, [-1, 0, 1])
|
||||
array([[ -0., 0.],
|
||||
[ 6., 24.]])
|
||||
>>> r = np.arange(-2, 2).reshape(2,2) # multidimensional coefficients
|
||||
>>> r # each column of r defines one polynomial
|
||||
array([[-2, -1],
|
||||
[ 0, 1]])
|
||||
>>> b = [-2, 1]
|
||||
>>> polyvalfromroots(b, r, tensor=True)
|
||||
array([[-0., 3.],
|
||||
[ 3., 0.]])
|
||||
>>> polyvalfromroots(b, r, tensor=False)
|
||||
array([-0., 0.])
|
||||
"""
|
||||
r = np.array(r, ndmin=1, copy=0)
|
||||
if r.dtype.char in '?bBhHiIlLqQpP':
|
||||
r = r.astype(np.double)
|
||||
if isinstance(x, (tuple, list)):
|
||||
x = np.asarray(x)
|
||||
if isinstance(x, np.ndarray):
|
||||
if tensor:
|
||||
r = r.reshape(r.shape + (1,)*x.ndim)
|
||||
elif x.ndim >= r.ndim:
|
||||
raise ValueError("x.ndim must be < r.ndim when tensor == "
|
||||
"False")
|
||||
return np.prod(x - r, axis=0)
|
||||
|
||||
|
||||
try:
|
||||
from numpy.testing import suppress_warnings
|
||||
except ImportError:
|
||||
class suppress_warnings(object):
|
||||
"""
|
||||
Context manager and decorator doing much the same as
|
||||
``warnings.catch_warnings``.
|
||||
|
||||
However, it also provides a filter mechanism to work around
|
||||
https://bugs.python.org/issue4180.
|
||||
|
||||
This bug causes Python before 3.4 to not reliably show warnings again
|
||||
after they have been ignored once (even within catch_warnings). It
|
||||
means that no "ignore" filter can be used easily, since following
|
||||
tests might need to see the warning. Additionally it allows easier
|
||||
specificity for testing warnings and can be nested.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
forwarding_rule : str, optional
|
||||
One of "always", "once", "module", or "location". Analogous to
|
||||
the usual warnings module filter mode, it is useful to reduce
|
||||
noise mostly on the outmost level. Unsuppressed and unrecorded
|
||||
warnings will be forwarded based on this rule. Defaults to "always".
|
||||
"location" is equivalent to the warnings "default", match by exact
|
||||
location the warning warning originated from.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Filters added inside the context manager will be discarded again
|
||||
when leaving it. Upon entering all filters defined outside a
|
||||
context will be applied automatically.
|
||||
|
||||
When a recording filter is added, matching warnings are stored in the
|
||||
``log`` attribute as well as in the list returned by ``record``.
|
||||
|
||||
If filters are added and the ``module`` keyword is given, the
|
||||
warning registry of this module will additionally be cleared when
|
||||
applying it, entering the context, or exiting it. This could cause
|
||||
warnings to appear a second time after leaving the context if they
|
||||
were configured to be printed once (default) and were already
|
||||
printed before the context was entered.
|
||||
|
||||
Nesting this context manager will work as expected when the
|
||||
forwarding rule is "always" (default). Unfiltered and unrecorded
|
||||
warnings will be passed out and be matched by the outer level.
|
||||
On the outmost level they will be printed (or caught by another
|
||||
warnings context). The forwarding rule argument can modify this
|
||||
behaviour.
|
||||
|
||||
Like ``catch_warnings`` this context manager is not threadsafe.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> with suppress_warnings() as sup:
|
||||
... sup.filter(DeprecationWarning, "Some text")
|
||||
... sup.filter(module=np.ma.core)
|
||||
... log = sup.record(FutureWarning, "Does this occur?")
|
||||
... command_giving_warnings()
|
||||
... # The FutureWarning was given once, the filtered warnings were
|
||||
... # ignored. All other warnings abide outside settings (may be
|
||||
... # printed/error)
|
||||
... assert_(len(log) == 1)
|
||||
... assert_(len(sup.log) == 1) # also stored in log attribute
|
||||
|
||||
Or as a decorator:
|
||||
|
||||
>>> sup = suppress_warnings()
|
||||
>>> sup.filter(module=np.ma.core) # module must match exact
|
||||
>>> @sup
|
||||
>>> def some_function():
|
||||
... # do something which causes a warning in np.ma.core
|
||||
... pass
|
||||
"""
|
||||
def __init__(self, forwarding_rule="always"):
|
||||
self._entered = False
|
||||
|
||||
# Suppressions are either instance or defined inside one with block:
|
||||
self._suppressions = []
|
||||
|
||||
if forwarding_rule not in {"always", "module", "once", "location"}:
|
||||
raise ValueError("unsupported forwarding rule.")
|
||||
self._forwarding_rule = forwarding_rule
|
||||
|
||||
def _clear_registries(self):
|
||||
if hasattr(warnings, "_filters_mutated"):
|
||||
# clearing the registry should not be necessary on new pythons,
|
||||
# instead the filters should be mutated.
|
||||
warnings._filters_mutated()
|
||||
return
|
||||
# Simply clear the registry, this should normally be harmless,
|
||||
# note that on new pythons it would be invalidated anyway.
|
||||
for module in self._tmp_modules:
|
||||
if hasattr(module, "__warningregistry__"):
|
||||
module.__warningregistry__.clear()
|
||||
|
||||
def _filter(self, category=Warning, message="", module=None, record=False):
|
||||
if record:
|
||||
record = [] # The log where to store warnings
|
||||
else:
|
||||
record = None
|
||||
if self._entered:
|
||||
if module is None:
|
||||
warnings.filterwarnings(
|
||||
"always", category=category, message=message)
|
||||
else:
|
||||
module_regex = module.__name__.replace('.', r'\.') + '$'
|
||||
warnings.filterwarnings(
|
||||
"always", category=category, message=message,
|
||||
module=module_regex)
|
||||
self._tmp_modules.add(module)
|
||||
self._clear_registries()
|
||||
|
||||
self._tmp_suppressions.append(
|
||||
(category, message, re.compile(message, re.I), module, record))
|
||||
else:
|
||||
self._suppressions.append(
|
||||
(category, message, re.compile(message, re.I), module, record))
|
||||
|
||||
return record
|
||||
|
||||
def filter(self, category=Warning, message="", module=None):
|
||||
"""
|
||||
Add a new suppressing filter or apply it if the state is entered.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
category : class, optional
|
||||
Warning class to filter
|
||||
message : string, optional
|
||||
Regular expression matching the warning message.
|
||||
module : module, optional
|
||||
Module to filter for. Note that the module (and its file)
|
||||
must match exactly and cannot be a submodule. This may make
|
||||
it unreliable for external modules.
|
||||
|
||||
Notes
|
||||
-----
|
||||
When added within a context, filters are only added inside
|
||||
the context and will be forgotten when the context is exited.
|
||||
"""
|
||||
self._filter(category=category, message=message, module=module,
|
||||
record=False)
|
||||
|
||||
def record(self, category=Warning, message="", module=None):
|
||||
"""
|
||||
Append a new recording filter or apply it if the state is entered.
|
||||
|
||||
All warnings matching will be appended to the ``log`` attribute.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
category : class, optional
|
||||
Warning class to filter
|
||||
message : string, optional
|
||||
Regular expression matching the warning message.
|
||||
module : module, optional
|
||||
Module to filter for. Note that the module (and its file)
|
||||
must match exactly and cannot be a submodule. This may make
|
||||
it unreliable for external modules.
|
||||
|
||||
Returns
|
||||
-------
|
||||
log : list
|
||||
A list which will be filled with all matched warnings.
|
||||
|
||||
Notes
|
||||
-----
|
||||
When added within a context, filters are only added inside
|
||||
the context and will be forgotten when the context is exited.
|
||||
"""
|
||||
return self._filter(category=category, message=message, module=module,
|
||||
record=True)
|
||||
|
||||
def __enter__(self):
|
||||
if self._entered:
|
||||
raise RuntimeError("cannot enter suppress_warnings twice.")
|
||||
|
||||
self._orig_show = warnings.showwarning
|
||||
self._filters = warnings.filters
|
||||
warnings.filters = self._filters[:]
|
||||
|
||||
self._entered = True
|
||||
self._tmp_suppressions = []
|
||||
self._tmp_modules = set()
|
||||
self._forwarded = set()
|
||||
|
||||
self.log = [] # reset global log (no need to keep same list)
|
||||
|
||||
for cat, mess, _, mod, log in self._suppressions:
|
||||
if log is not None:
|
||||
del log[:] # clear the log
|
||||
if mod is None:
|
||||
warnings.filterwarnings(
|
||||
"always", category=cat, message=mess)
|
||||
else:
|
||||
module_regex = mod.__name__.replace('.', r'\.') + '$'
|
||||
warnings.filterwarnings(
|
||||
"always", category=cat, message=mess,
|
||||
module=module_regex)
|
||||
self._tmp_modules.add(mod)
|
||||
warnings.showwarning = self._showwarning
|
||||
self._clear_registries()
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
warnings.showwarning = self._orig_show
|
||||
warnings.filters = self._filters
|
||||
self._clear_registries()
|
||||
self._entered = False
|
||||
del self._orig_show
|
||||
del self._filters
|
||||
|
||||
def _showwarning(self, message, category, filename, lineno,
|
||||
*args, **kwargs):
|
||||
use_warnmsg = kwargs.pop("use_warnmsg", None)
|
||||
for cat, _, pattern, mod, rec in (
|
||||
self._suppressions + self._tmp_suppressions)[::-1]:
|
||||
if (issubclass(category, cat) and
|
||||
pattern.match(message.args[0]) is not None):
|
||||
if mod is None:
|
||||
# Message and category match, either recorded or ignored
|
||||
if rec is not None:
|
||||
msg = WarningMessage(message, category, filename,
|
||||
lineno, **kwargs)
|
||||
self.log.append(msg)
|
||||
rec.append(msg)
|
||||
return
|
||||
# Use startswith, because warnings strips the c or o from
|
||||
# .pyc/.pyo files.
|
||||
elif mod.__file__.startswith(filename):
|
||||
# The message and module (filename) match
|
||||
if rec is not None:
|
||||
msg = WarningMessage(message, category, filename,
|
||||
lineno, **kwargs)
|
||||
self.log.append(msg)
|
||||
rec.append(msg)
|
||||
return
|
||||
|
||||
# There is no filter in place, so pass to the outside handler
|
||||
# unless we should only pass it once
|
||||
if self._forwarding_rule == "always":
|
||||
if use_warnmsg is None:
|
||||
self._orig_show(message, category, filename, lineno,
|
||||
*args, **kwargs)
|
||||
else:
|
||||
self._orig_showmsg(use_warnmsg)
|
||||
return
|
||||
|
||||
if self._forwarding_rule == "once":
|
||||
signature = (message.args, category)
|
||||
elif self._forwarding_rule == "module":
|
||||
signature = (message.args, category, filename)
|
||||
elif self._forwarding_rule == "location":
|
||||
signature = (message.args, category, filename, lineno)
|
||||
|
||||
if signature in self._forwarded:
|
||||
return
|
||||
self._forwarded.add(signature)
|
||||
if use_warnmsg is None:
|
||||
self._orig_show(message, category, filename, lineno, *args,
|
||||
**kwargs)
|
||||
else:
|
||||
self._orig_showmsg(use_warnmsg)
|
||||
|
||||
def __call__(self, func):
|
||||
"""
|
||||
Function decorator to apply certain suppressions to a whole
|
||||
function.
|
||||
"""
|
||||
@wraps(func)
|
||||
def new_func(*args, **kwargs):
|
||||
with self:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return new_func
|
||||
|
||||
if NumpyVersion(np.__version__) >= '1.10.0':
|
||||
from numpy import cov
|
||||
else:
|
||||
from numpy import array, average, dot
|
||||
|
||||
def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None,
|
||||
aweights=None):
|
||||
"""
|
||||
Estimate a covariance matrix, given data and weights.
|
||||
|
||||
Covariance indicates the level to which two variables vary together.
|
||||
If we examine N-dimensional samples, :math:`X = [x_1, x_2, ... x_N]^T`,
|
||||
then the covariance matrix element :math:`C_{ij}` is the covariance of
|
||||
:math:`x_i` and :math:`x_j`. The element :math:`C_{ii}` is the variance
|
||||
of :math:`x_i`.
|
||||
|
||||
See the notes for an outline of the algorithm.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
m : array_like
|
||||
A 1-D or 2-D array containing multiple variables and observations.
|
||||
Each row of `m` represents a variable, and each column a single
|
||||
observation of all those variables. Also see `rowvar` below.
|
||||
y : array_like, optional
|
||||
An additional set of variables and observations. `y` has the same form
|
||||
as that of `m`.
|
||||
rowvar : bool, optional
|
||||
If `rowvar` is True (default), then each row represents a
|
||||
variable, with observations in the columns. Otherwise, the relationship
|
||||
is transposed: each column represents a variable, while the rows
|
||||
contain observations.
|
||||
bias : bool, optional
|
||||
Default normalization (False) is by ``(N - 1)``, where ``N`` is the
|
||||
number of observations given (unbiased estimate). If `bias` is True,
|
||||
then normalization is by ``N``. These values can be overridden by using
|
||||
the keyword ``ddof`` in numpy versions >= 1.5.
|
||||
ddof : int, optional
|
||||
If not ``None`` the default value implied by `bias` is overridden.
|
||||
Note that ``ddof=1`` will return the unbiased estimate, even if both
|
||||
`fweights` and `aweights` are specified, and ``ddof=0`` will return
|
||||
the simple average. See the notes for the details. The default value
|
||||
is ``None``.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
fweights : array_like, int, optional
|
||||
1-D array of integer freguency weights; the number of times each
|
||||
observation vector should be repeated.
|
||||
|
||||
.. versionadded:: 1.10
|
||||
aweights : array_like, optional
|
||||
1-D array of observation vector weights. These relative weights are
|
||||
typically large for observations considered "important" and smaller for
|
||||
observations considered less "important". If ``ddof=0`` the array of
|
||||
weights can be used to assign probabilities to observation vectors.
|
||||
|
||||
.. versionadded:: 1.10
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The covariance matrix of the variables.
|
||||
|
||||
See Also
|
||||
--------
|
||||
corrcoef : Normalized covariance matrix
|
||||
|
||||
Notes
|
||||
-----
|
||||
Assume that the observations are in the columns of the observation
|
||||
array `m` and let ``f = fweights`` and ``a = aweights`` for brevity. The
|
||||
steps to compute the weighted covariance are as follows::
|
||||
|
||||
>>> w = f * a
|
||||
>>> v1 = np.sum(w)
|
||||
>>> v2 = np.sum(w * a)
|
||||
>>> m -= np.sum(m * w, axis=1, keepdims=True) / v1
|
||||
>>> cov = np.dot(m * w, m.T) * v1 / (v1**2 - ddof * v2)
|
||||
|
||||
Note that when ``a == 1``, the normalization factor
|
||||
``v1 / (v1**2 - ddof * v2)`` goes over to ``1 / (np.sum(f) - ddof)``
|
||||
as it should.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Consider two variables, :math:`x_0` and :math:`x_1`, which
|
||||
correlate perfectly, but in opposite directions:
|
||||
|
||||
>>> x = np.array([[0, 2], [1, 1], [2, 0]]).T
|
||||
>>> x
|
||||
array([[0, 1, 2],
|
||||
[2, 1, 0]])
|
||||
|
||||
Note how :math:`x_0` increases while :math:`x_1` decreases. The covariance
|
||||
matrix shows this clearly:
|
||||
|
||||
>>> np.cov(x)
|
||||
array([[ 1., -1.],
|
||||
[-1., 1.]])
|
||||
|
||||
Note that element :math:`C_{0,1}`, which shows the correlation between
|
||||
:math:`x_0` and :math:`x_1`, is negative.
|
||||
|
||||
Further, note how `x` and `y` are combined:
|
||||
|
||||
>>> x = [-2.1, -1, 4.3]
|
||||
>>> y = [3, 1.1, 0.12]
|
||||
>>> X = np.stack((x, y), axis=0)
|
||||
>>> print(np.cov(X))
|
||||
[[ 11.71 -4.286 ]
|
||||
[ -4.286 2.14413333]]
|
||||
>>> print(np.cov(x, y))
|
||||
[[ 11.71 -4.286 ]
|
||||
[ -4.286 2.14413333]]
|
||||
>>> print(np.cov(x))
|
||||
11.71
|
||||
|
||||
"""
|
||||
# Check inputs
|
||||
if ddof is not None and ddof != int(ddof):
|
||||
raise ValueError(
|
||||
"ddof must be integer")
|
||||
|
||||
# Handles complex arrays too
|
||||
m = np.asarray(m)
|
||||
if m.ndim > 2:
|
||||
raise ValueError("m has more than 2 dimensions")
|
||||
|
||||
if y is None:
|
||||
dtype = np.result_type(m, np.float64)
|
||||
else:
|
||||
y = np.asarray(y)
|
||||
if y.ndim > 2:
|
||||
raise ValueError("y has more than 2 dimensions")
|
||||
dtype = np.result_type(m, y, np.float64)
|
||||
|
||||
X = array(m, ndmin=2, dtype=dtype)
|
||||
if not rowvar and X.shape[0] != 1:
|
||||
X = X.T
|
||||
if X.shape[0] == 0:
|
||||
return np.array([]).reshape(0, 0)
|
||||
if y is not None:
|
||||
y = array(y, copy=False, ndmin=2, dtype=dtype)
|
||||
if not rowvar and y.shape[0] != 1:
|
||||
y = y.T
|
||||
X = np.concatenate((X, y), axis=0)
|
||||
|
||||
if ddof is None:
|
||||
if bias == 0:
|
||||
ddof = 1
|
||||
else:
|
||||
ddof = 0
|
||||
|
||||
# Get the product of frequencies and weights
|
||||
w = None
|
||||
if fweights is not None:
|
||||
fweights = np.asarray(fweights, dtype=float)
|
||||
if not np.all(fweights == np.around(fweights)):
|
||||
raise TypeError(
|
||||
"fweights must be integer")
|
||||
if fweights.ndim > 1:
|
||||
raise RuntimeError(
|
||||
"cannot handle multidimensional fweights")
|
||||
if fweights.shape[0] != X.shape[1]:
|
||||
raise RuntimeError(
|
||||
"incompatible numbers of samples and fweights")
|
||||
if any(fweights < 0):
|
||||
raise ValueError(
|
||||
"fweights cannot be negative")
|
||||
w = fweights
|
||||
if aweights is not None:
|
||||
aweights = np.asarray(aweights, dtype=float)
|
||||
if aweights.ndim > 1:
|
||||
raise RuntimeError(
|
||||
"cannot handle multidimensional aweights")
|
||||
if aweights.shape[0] != X.shape[1]:
|
||||
raise RuntimeError(
|
||||
"incompatible numbers of samples and aweights")
|
||||
if any(aweights < 0):
|
||||
raise ValueError(
|
||||
"aweights cannot be negative")
|
||||
if w is None:
|
||||
w = aweights
|
||||
else:
|
||||
w *= aweights
|
||||
|
||||
avg, w_sum = average(X, axis=1, weights=w, returned=True)
|
||||
w_sum = w_sum[0]
|
||||
|
||||
# Determine the normalization
|
||||
if w is None:
|
||||
fact = X.shape[1] - ddof
|
||||
elif ddof == 0:
|
||||
fact = w_sum
|
||||
elif aweights is None:
|
||||
fact = w_sum - ddof
|
||||
else:
|
||||
fact = w_sum - ddof*sum(w*aweights)/w_sum
|
||||
|
||||
if fact <= 0:
|
||||
warnings.warn("Degrees of freedom <= 0 for slice",
|
||||
RuntimeWarning, stacklevel=2)
|
||||
fact = 0.0
|
||||
|
||||
X -= avg[:, None]
|
||||
if w is None:
|
||||
X_T = X.T
|
||||
else:
|
||||
X_T = (X*w).T
|
||||
c = dot(X, X_T.conj())
|
||||
c *= 1. / np.float64(fact)
|
||||
return c.squeeze()
|
||||
BIN
Binary file not shown.
@@ -1,128 +0,0 @@
|
||||
"""
|
||||
Generic test utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
__all__ = ['PytestTester', 'check_free_memory']
|
||||
|
||||
|
||||
class FPUModeChangeWarning(RuntimeWarning):
|
||||
"""Warning about FPU mode change"""
|
||||
pass
|
||||
|
||||
|
||||
class PytestTester(object):
|
||||
"""
|
||||
Pytest test runner entry point.
|
||||
"""
|
||||
|
||||
def __init__(self, module_name):
|
||||
self.module_name = module_name
|
||||
|
||||
def __call__(self, label="fast", verbose=1, extra_argv=None, doctests=False,
|
||||
coverage=False, tests=None):
|
||||
import pytest
|
||||
|
||||
module = sys.modules[self.module_name]
|
||||
module_path = os.path.abspath(module.__path__[0])
|
||||
|
||||
pytest_args = ['-l']
|
||||
|
||||
if doctests:
|
||||
raise ValueError("Doctests not supported")
|
||||
|
||||
if extra_argv:
|
||||
pytest_args += list(extra_argv)
|
||||
|
||||
if verbose and int(verbose) > 1:
|
||||
pytest_args += ["-" + "v"*(int(verbose)-1)]
|
||||
|
||||
if coverage:
|
||||
pytest_args += ["--cov=" + module_path]
|
||||
|
||||
if label == "fast":
|
||||
pytest_args += ["-m", "not slow"]
|
||||
elif label != "full":
|
||||
pytest_args += ["-m", label]
|
||||
|
||||
if tests is None:
|
||||
tests = [self.module_name]
|
||||
|
||||
pytest_args += ['--pyargs'] + list(tests)
|
||||
|
||||
try:
|
||||
code = pytest.main(pytest_args)
|
||||
except SystemExit as exc:
|
||||
code = exc.code
|
||||
|
||||
return (code == 0)
|
||||
|
||||
|
||||
def check_free_memory(free_mb):
|
||||
"""
|
||||
Check *free_mb* of memory is available, otherwise do pytest.skip
|
||||
"""
|
||||
import pytest
|
||||
|
||||
try:
|
||||
mem_free = _parse_size(os.environ['SCIPY_AVAILABLE_MEM'])
|
||||
msg = '{0} MB memory required, but environment SCIPY_AVAILABLE_MEM={1}'.format(
|
||||
free_mb, os.environ['SCIPY_AVAILABLE_MEM'])
|
||||
except KeyError:
|
||||
mem_free = _get_mem_available()
|
||||
if mem_free is None:
|
||||
pytest.skip("Could not determine available memory; set SCIPY_AVAILABLE_MEM "
|
||||
"variable to free memory in MB to run the test.")
|
||||
msg = '{0} MB memory required, but {1} MB available'.format(
|
||||
free_mb, mem_free/1e6)
|
||||
|
||||
if mem_free < free_mb * 1e6:
|
||||
pytest.skip(msg)
|
||||
|
||||
|
||||
def _parse_size(size_str):
|
||||
suffixes = {'': 1e6,
|
||||
'b': 1.0,
|
||||
'k': 1e3, 'M': 1e6, 'G': 1e9, 'T': 1e12,
|
||||
'kb': 1e3, 'Mb': 1e6, 'Gb': 1e9, 'Tb': 1e12,
|
||||
'kib': 1024.0, 'Mib': 1024.0**2, 'Gib': 1024.0**3, 'Tib': 1024.0**4}
|
||||
m = re.match(r'^\s*(\d+)\s*({0})\s*$'.format('|'.join(suffixes.keys())),
|
||||
size_str,
|
||||
re.I)
|
||||
if not m or m.group(2) not in suffixes:
|
||||
raise ValueError("Invalid size string")
|
||||
|
||||
return float(m.group(1)) * suffixes[m.group(2)]
|
||||
|
||||
|
||||
def _get_mem_available():
|
||||
"""
|
||||
Get information about memory available, not counting swap.
|
||||
"""
|
||||
try:
|
||||
import psutil
|
||||
return psutil.virtual_memory().available
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
if sys.platform.startswith('linux'):
|
||||
info = {}
|
||||
with open('/proc/meminfo', 'r') as f:
|
||||
for line in f:
|
||||
p = line.split()
|
||||
info[p[0].strip(':').lower()] = float(p[1]) * 1e3
|
||||
|
||||
if 'memavailable' in info:
|
||||
# Linux >= 3.14
|
||||
return info['memavailable']
|
||||
else:
|
||||
return info['memfree'] + info['cached']
|
||||
|
||||
return None
|
||||
@@ -1,60 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import threading
|
||||
|
||||
import scipy._lib.decorator
|
||||
|
||||
|
||||
__all__ = ['ReentrancyError', 'ReentrancyLock', 'non_reentrant']
|
||||
|
||||
|
||||
class ReentrancyError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class ReentrancyLock(object):
|
||||
"""
|
||||
Threading lock that raises an exception for reentrant calls.
|
||||
|
||||
Calls from different threads are serialized, and nested calls from the
|
||||
same thread result to an error.
|
||||
|
||||
The object can be used as a context manager, or to decorate functions
|
||||
via the decorate() method.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, err_msg):
|
||||
self._rlock = threading.RLock()
|
||||
self._entered = False
|
||||
self._err_msg = err_msg
|
||||
|
||||
def __enter__(self):
|
||||
self._rlock.acquire()
|
||||
if self._entered:
|
||||
self._rlock.release()
|
||||
raise ReentrancyError(self._err_msg)
|
||||
self._entered = True
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self._entered = False
|
||||
self._rlock.release()
|
||||
|
||||
def decorate(self, func):
|
||||
def caller(func, *a, **kw):
|
||||
with self:
|
||||
return func(*a, **kw)
|
||||
return scipy._lib.decorator.decorate(func, caller)
|
||||
|
||||
|
||||
def non_reentrant(err_msg=None):
|
||||
"""
|
||||
Decorate a function with a threading lock and prevent reentrant calls.
|
||||
"""
|
||||
def decorator(func):
|
||||
msg = err_msg
|
||||
if msg is None:
|
||||
msg = "%s is not re-entrant" % func.__name__
|
||||
lock = ReentrancyLock(msg)
|
||||
return lock.decorate(func)
|
||||
return decorator
|
||||
@@ -1,87 +0,0 @@
|
||||
''' Contexts for *with* statement providing temporary directories
|
||||
'''
|
||||
from __future__ import division, print_function, absolute_import
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from shutil import rmtree
|
||||
from tempfile import mkdtemp
|
||||
|
||||
|
||||
@contextmanager
|
||||
def tempdir():
|
||||
"""Create and return a temporary directory. This has the same
|
||||
behavior as mkdtemp but can be used as a context manager.
|
||||
|
||||
Upon exiting the context, the directory and everything contained
|
||||
in it are removed.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import os
|
||||
>>> with tempdir() as tmpdir:
|
||||
... fname = os.path.join(tmpdir, 'example_file.txt')
|
||||
... with open(fname, 'wt') as fobj:
|
||||
... _ = fobj.write('a string\\n')
|
||||
>>> os.path.exists(tmpdir)
|
||||
False
|
||||
"""
|
||||
d = mkdtemp()
|
||||
yield d
|
||||
rmtree(d)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def in_tempdir():
|
||||
''' Create, return, and change directory to a temporary directory
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import os
|
||||
>>> my_cwd = os.getcwd()
|
||||
>>> with in_tempdir() as tmpdir:
|
||||
... _ = open('test.txt', 'wt').write('some text')
|
||||
... assert os.path.isfile('test.txt')
|
||||
... assert os.path.isfile(os.path.join(tmpdir, 'test.txt'))
|
||||
>>> os.path.exists(tmpdir)
|
||||
False
|
||||
>>> os.getcwd() == my_cwd
|
||||
True
|
||||
'''
|
||||
pwd = os.getcwd()
|
||||
d = mkdtemp()
|
||||
os.chdir(d)
|
||||
yield d
|
||||
os.chdir(pwd)
|
||||
rmtree(d)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def in_dir(dir=None):
|
||||
""" Change directory to given directory for duration of ``with`` block
|
||||
|
||||
Useful when you want to use `in_tempdir` for the final test, but
|
||||
you are still debugging. For example, you may want to do this in the end:
|
||||
|
||||
>>> with in_tempdir() as tmpdir:
|
||||
... # do something complicated which might break
|
||||
... pass
|
||||
|
||||
But indeed the complicated thing does break, and meanwhile the
|
||||
``in_tempdir`` context manager wiped out the directory with the
|
||||
temporary files that you wanted for debugging. So, while debugging, you
|
||||
replace with something like:
|
||||
|
||||
>>> with in_dir() as tmpdir: # Use working directory by default
|
||||
... # do something complicated which might break
|
||||
... pass
|
||||
|
||||
You can then look at the temporary file outputs to debug what is happening,
|
||||
fix, and finally replace ``in_dir`` with ``in_tempdir`` again.
|
||||
"""
|
||||
cwd = os.getcwd()
|
||||
if dir is None:
|
||||
yield cwd
|
||||
return
|
||||
os.chdir(dir)
|
||||
yield dir
|
||||
os.chdir(cwd)
|
||||
@@ -1,418 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import functools
|
||||
import operator
|
||||
import sys
|
||||
import warnings
|
||||
import numbers
|
||||
from collections import namedtuple
|
||||
from multiprocessing import Pool
|
||||
import inspect
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def _valarray(shape, value=np.nan, typecode=None):
|
||||
"""Return an array of all value.
|
||||
"""
|
||||
|
||||
out = np.ones(shape, dtype=bool) * value
|
||||
if typecode is not None:
|
||||
out = out.astype(typecode)
|
||||
if not isinstance(out, np.ndarray):
|
||||
out = np.asarray(out)
|
||||
return out
|
||||
|
||||
|
||||
def _lazywhere(cond, arrays, f, fillvalue=None, f2=None):
|
||||
"""
|
||||
np.where(cond, x, fillvalue) always evaluates x even where cond is False.
|
||||
This one only evaluates f(arr1[cond], arr2[cond], ...).
|
||||
For example,
|
||||
>>> a, b = np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8])
|
||||
>>> def f(a, b):
|
||||
return a*b
|
||||
>>> _lazywhere(a > 2, (a, b), f, np.nan)
|
||||
array([ nan, nan, 21., 32.])
|
||||
|
||||
Notice it assumes that all `arrays` are of the same shape, or can be
|
||||
broadcasted together.
|
||||
|
||||
"""
|
||||
if fillvalue is None:
|
||||
if f2 is None:
|
||||
raise ValueError("One of (fillvalue, f2) must be given.")
|
||||
else:
|
||||
fillvalue = np.nan
|
||||
else:
|
||||
if f2 is not None:
|
||||
raise ValueError("Only one of (fillvalue, f2) can be given.")
|
||||
|
||||
arrays = np.broadcast_arrays(*arrays)
|
||||
temp = tuple(np.extract(cond, arr) for arr in arrays)
|
||||
tcode = np.mintypecode([a.dtype.char for a in arrays])
|
||||
out = _valarray(np.shape(arrays[0]), value=fillvalue, typecode=tcode)
|
||||
np.place(out, cond, f(*temp))
|
||||
if f2 is not None:
|
||||
temp = tuple(np.extract(~cond, arr) for arr in arrays)
|
||||
np.place(out, ~cond, f2(*temp))
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def _lazyselect(condlist, choicelist, arrays, default=0):
|
||||
"""
|
||||
Mimic `np.select(condlist, choicelist)`.
|
||||
|
||||
Notice it assumes that all `arrays` are of the same shape, or can be
|
||||
broadcasted together.
|
||||
|
||||
All functions in `choicelist` must accept array arguments in the order
|
||||
given in `arrays` and must return an array of the same shape as broadcasted
|
||||
`arrays`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> x = np.arange(6)
|
||||
>>> np.select([x <3, x > 3], [x**2, x**3], default=0)
|
||||
array([ 0, 1, 4, 0, 64, 125])
|
||||
|
||||
>>> _lazyselect([x < 3, x > 3], [lambda x: x**2, lambda x: x**3], (x,))
|
||||
array([ 0., 1., 4., 0., 64., 125.])
|
||||
|
||||
>>> a = -np.ones_like(x)
|
||||
>>> _lazyselect([x < 3, x > 3],
|
||||
... [lambda x, a: x**2, lambda x, a: a * x**3],
|
||||
... (x, a), default=np.nan)
|
||||
array([ 0., 1., 4., nan, -64., -125.])
|
||||
|
||||
"""
|
||||
arrays = np.broadcast_arrays(*arrays)
|
||||
tcode = np.mintypecode([a.dtype.char for a in arrays])
|
||||
out = _valarray(np.shape(arrays[0]), value=default, typecode=tcode)
|
||||
for index in range(len(condlist)):
|
||||
func, cond = choicelist[index], condlist[index]
|
||||
if np.all(cond is False):
|
||||
continue
|
||||
cond, _ = np.broadcast_arrays(cond, arrays[0])
|
||||
temp = tuple(np.extract(cond, arr) for arr in arrays)
|
||||
np.place(out, cond, func(*temp))
|
||||
return out
|
||||
|
||||
|
||||
def _aligned_zeros(shape, dtype=float, order="C", align=None):
|
||||
"""Allocate a new ndarray with aligned memory.
|
||||
|
||||
Primary use case for this currently is working around a f2py issue
|
||||
in Numpy 1.9.1, where dtype.alignment is such that np.zeros() does
|
||||
not necessarily create arrays aligned up to it.
|
||||
|
||||
"""
|
||||
dtype = np.dtype(dtype)
|
||||
if align is None:
|
||||
align = dtype.alignment
|
||||
if not hasattr(shape, '__len__'):
|
||||
shape = (shape,)
|
||||
size = functools.reduce(operator.mul, shape) * dtype.itemsize
|
||||
buf = np.empty(size + align + 1, np.uint8)
|
||||
offset = buf.__array_interface__['data'][0] % align
|
||||
if offset != 0:
|
||||
offset = align - offset
|
||||
# Note: slices producing 0-size arrays do not necessarily change
|
||||
# data pointer --- so we use and allocate size+1
|
||||
buf = buf[offset:offset+size+1][:-1]
|
||||
data = np.ndarray(shape, dtype, buf, order=order)
|
||||
data.fill(0)
|
||||
return data
|
||||
|
||||
|
||||
def _prune_array(array):
|
||||
"""Return an array equivalent to the input array. If the input
|
||||
array is a view of a much larger array, copy its contents to a
|
||||
newly allocated array. Otherwise, return the input unchanged.
|
||||
"""
|
||||
if array.base is not None and array.size < array.base.size // 2:
|
||||
return array.copy()
|
||||
return array
|
||||
|
||||
|
||||
class DeprecatedImport(object):
|
||||
"""
|
||||
Deprecated import, with redirection + warning.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Suppose you previously had in some module::
|
||||
|
||||
from foo import spam
|
||||
|
||||
If this has to be deprecated, do::
|
||||
|
||||
spam = DeprecatedImport("foo.spam", "baz")
|
||||
|
||||
to redirect users to use "baz" module instead.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, old_module_name, new_module_name):
|
||||
self._old_name = old_module_name
|
||||
self._new_name = new_module_name
|
||||
__import__(self._new_name)
|
||||
self._mod = sys.modules[self._new_name]
|
||||
|
||||
def __dir__(self):
|
||||
return dir(self._mod)
|
||||
|
||||
def __getattr__(self, name):
|
||||
warnings.warn("Module %s is deprecated, use %s instead"
|
||||
% (self._old_name, self._new_name),
|
||||
DeprecationWarning)
|
||||
return getattr(self._mod, name)
|
||||
|
||||
|
||||
# copy-pasted from scikit-learn utils/validation.py
|
||||
def check_random_state(seed):
|
||||
"""Turn seed into a np.random.RandomState instance
|
||||
|
||||
If seed is None (or np.random), return the RandomState singleton used
|
||||
by np.random.
|
||||
If seed is an int, return a new RandomState instance seeded with seed.
|
||||
If seed is already a RandomState instance, return it.
|
||||
Otherwise raise ValueError.
|
||||
"""
|
||||
if seed is None or seed is np.random:
|
||||
return np.random.mtrand._rand
|
||||
if isinstance(seed, (numbers.Integral, np.integer)):
|
||||
return np.random.RandomState(seed)
|
||||
if isinstance(seed, np.random.RandomState):
|
||||
return seed
|
||||
raise ValueError('%r cannot be used to seed a numpy.random.RandomState'
|
||||
' instance' % seed)
|
||||
|
||||
|
||||
def _asarray_validated(a, check_finite=True,
|
||||
sparse_ok=False, objects_ok=False, mask_ok=False,
|
||||
as_inexact=False):
|
||||
"""
|
||||
Helper function for scipy argument validation.
|
||||
|
||||
Many scipy linear algebra functions do support arbitrary array-like
|
||||
input arguments. Examples of commonly unsupported inputs include
|
||||
matrices containing inf/nan, sparse matrix representations, and
|
||||
matrices with complicated elements.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : array_like
|
||||
The array-like input.
|
||||
check_finite : bool, optional
|
||||
Whether to check that the input matrices contain only finite numbers.
|
||||
Disabling may give a performance gain, but may result in problems
|
||||
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
||||
Default: True
|
||||
sparse_ok : bool, optional
|
||||
True if scipy sparse matrices are allowed.
|
||||
objects_ok : bool, optional
|
||||
True if arrays with dype('O') are allowed.
|
||||
mask_ok : bool, optional
|
||||
True if masked arrays are allowed.
|
||||
as_inexact : bool, optional
|
||||
True to convert the input array to a np.inexact dtype.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ret : ndarray
|
||||
The converted validated array.
|
||||
|
||||
"""
|
||||
if not sparse_ok:
|
||||
import scipy.sparse
|
||||
if scipy.sparse.issparse(a):
|
||||
msg = ('Sparse matrices are not supported by this function. '
|
||||
'Perhaps one of the scipy.sparse.linalg functions '
|
||||
'would work instead.')
|
||||
raise ValueError(msg)
|
||||
if not mask_ok:
|
||||
if np.ma.isMaskedArray(a):
|
||||
raise ValueError('masked arrays are not supported')
|
||||
toarray = np.asarray_chkfinite if check_finite else np.asarray
|
||||
a = toarray(a)
|
||||
if not objects_ok:
|
||||
if a.dtype is np.dtype('O'):
|
||||
raise ValueError('object arrays are not supported')
|
||||
if as_inexact:
|
||||
if not np.issubdtype(a.dtype, np.inexact):
|
||||
a = toarray(a, dtype=np.float_)
|
||||
return a
|
||||
|
||||
|
||||
# Add a replacement for inspect.getargspec() which is deprecated in python 3.5
|
||||
# The version below is borrowed from Django,
|
||||
# https://github.com/django/django/pull/4846
|
||||
|
||||
# Note an inconsistency between inspect.getargspec(func) and
|
||||
# inspect.signature(func). If `func` is a bound method, the latter does *not*
|
||||
# list `self` as a first argument, while the former *does*.
|
||||
# Hence cook up a common ground replacement: `getargspec_no_self` which
|
||||
# mimics `inspect.getargspec` but does not list `self`.
|
||||
#
|
||||
# This way, the caller code does not need to know whether it uses a legacy
|
||||
# .getargspec or bright and shiny .signature.
|
||||
|
||||
try:
|
||||
# is it python 3.3 or higher?
|
||||
inspect.signature
|
||||
|
||||
# Apparently, yes. Wrap inspect.signature
|
||||
|
||||
ArgSpec = namedtuple('ArgSpec', ['args', 'varargs', 'keywords', 'defaults'])
|
||||
|
||||
def getargspec_no_self(func):
|
||||
"""inspect.getargspec replacement using inspect.signature.
|
||||
|
||||
inspect.getargspec is deprecated in python 3. This is a replacement
|
||||
based on the (new in python 3.3) `inspect.signature`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable
|
||||
A callable to inspect
|
||||
|
||||
Returns
|
||||
-------
|
||||
argspec : ArgSpec(args, varargs, varkw, defaults)
|
||||
This is similar to the result of inspect.getargspec(func) under
|
||||
python 2.x.
|
||||
NOTE: if the first argument of `func` is self, it is *not*, I repeat
|
||||
*not* included in argspec.args.
|
||||
This is done for consistency between inspect.getargspec() under
|
||||
python 2.x, and inspect.signature() under python 3.x.
|
||||
"""
|
||||
sig = inspect.signature(func)
|
||||
args = [
|
||||
p.name for p in sig.parameters.values()
|
||||
if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
|
||||
]
|
||||
varargs = [
|
||||
p.name for p in sig.parameters.values()
|
||||
if p.kind == inspect.Parameter.VAR_POSITIONAL
|
||||
]
|
||||
varargs = varargs[0] if varargs else None
|
||||
varkw = [
|
||||
p.name for p in sig.parameters.values()
|
||||
if p.kind == inspect.Parameter.VAR_KEYWORD
|
||||
]
|
||||
varkw = varkw[0] if varkw else None
|
||||
defaults = [
|
||||
p.default for p in sig.parameters.values()
|
||||
if (p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD and
|
||||
p.default is not p.empty)
|
||||
] or None
|
||||
return ArgSpec(args, varargs, varkw, defaults)
|
||||
|
||||
except AttributeError:
|
||||
# python 2.x
|
||||
def getargspec_no_self(func):
|
||||
"""inspect.getargspec replacement for compatibility with python 3.x.
|
||||
|
||||
inspect.getargspec is deprecated in python 3. This wraps it, and
|
||||
*removes* `self` from the argument list of `func`, if present.
|
||||
This is done for forward compatibility with python 3.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable
|
||||
A callable to inspect
|
||||
|
||||
Returns
|
||||
-------
|
||||
argspec : ArgSpec(args, varargs, varkw, defaults)
|
||||
This is similar to the result of inspect.getargspec(func) under
|
||||
python 2.x.
|
||||
NOTE: if the first argument of `func` is self, it is *not*, I repeat
|
||||
*not* included in argspec.args.
|
||||
This is done for consistency between inspect.getargspec() under
|
||||
python 2.x, and inspect.signature() under python 3.x.
|
||||
"""
|
||||
argspec = inspect.getargspec(func)
|
||||
if argspec.args[0] == 'self':
|
||||
argspec.args.pop(0)
|
||||
return argspec
|
||||
|
||||
|
||||
class MapWrapper(object):
|
||||
"""
|
||||
Parallelisation wrapper for working with map-like callables, such as
|
||||
`multiprocessing.Pool.map`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pool : int or map-like callable
|
||||
If `pool` is an integer, then it specifies the number of threads to
|
||||
use for parallelization. If ``int(pool) == 1``, then no parallel
|
||||
processing is used and the map builtin is used.
|
||||
If ``pool == -1``, then the pool will utilise all available CPUs.
|
||||
If `pool` is a map-like callable that follows the same
|
||||
calling sequence as the built-in map function, then this callable is
|
||||
used for parallelisation.
|
||||
"""
|
||||
def __init__(self, pool=1):
|
||||
self.pool = None
|
||||
self._mapfunc = map
|
||||
self._own_pool = False
|
||||
|
||||
if callable(pool):
|
||||
self.pool = pool
|
||||
self._mapfunc = self.pool
|
||||
else:
|
||||
# user supplies a number
|
||||
if int(pool) == -1:
|
||||
# use as many processors as possible
|
||||
self.pool = Pool()
|
||||
self._mapfunc = self.pool.map
|
||||
self._own_pool = True
|
||||
elif int(pool) == 1:
|
||||
pass
|
||||
elif int(pool) > 1:
|
||||
# use the number of processors requested
|
||||
self.pool = Pool(processes=int(pool))
|
||||
self._mapfunc = self.pool.map
|
||||
self._own_pool = True
|
||||
else:
|
||||
raise RuntimeError("Number of workers specified must be -1,"
|
||||
" an int >= 1, or an object with a 'map' method")
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def terminate(self):
|
||||
if self._own_pool:
|
||||
self.pool.terminate()
|
||||
|
||||
def join(self):
|
||||
if self._own_pool:
|
||||
self.pool.join()
|
||||
|
||||
def close(self):
|
||||
if self._own_pool:
|
||||
self.pool.close()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if self._own_pool:
|
||||
if exc_type is None:
|
||||
self.pool.close()
|
||||
self.pool.join()
|
||||
else:
|
||||
self.pool.terminate()
|
||||
|
||||
def __call__(self, func, iterable):
|
||||
# only accept one iterable because that's all Pool.map accepts
|
||||
try:
|
||||
return self._mapfunc(func, iterable)
|
||||
except TypeError:
|
||||
# wrong number of arguments
|
||||
raise TypeError("The map-like callable must be of the"
|
||||
" form f(func, iterable)")
|
||||
@@ -1,155 +0,0 @@
|
||||
"""Utility to compare (Numpy) version strings.
|
||||
|
||||
The NumpyVersion class allows properly comparing numpy version strings.
|
||||
The LooseVersion and StrictVersion classes that distutils provides don't
|
||||
work; they don't recognize anything like alpha/beta/rc/dev versions.
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from scipy._lib.six import string_types
|
||||
|
||||
|
||||
__all__ = ['NumpyVersion']
|
||||
|
||||
|
||||
class NumpyVersion():
|
||||
"""Parse and compare numpy version strings.
|
||||
|
||||
Numpy has the following versioning scheme (numbers given are examples; they
|
||||
can be >9) in principle):
|
||||
|
||||
- Released version: '1.8.0', '1.8.1', etc.
|
||||
- Alpha: '1.8.0a1', '1.8.0a2', etc.
|
||||
- Beta: '1.8.0b1', '1.8.0b2', etc.
|
||||
- Release candidates: '1.8.0rc1', '1.8.0rc2', etc.
|
||||
- Development versions: '1.8.0.dev-f1234afa' (git commit hash appended)
|
||||
- Development versions after a1: '1.8.0a1.dev-f1234afa',
|
||||
'1.8.0b2.dev-f1234afa',
|
||||
'1.8.1rc1.dev-f1234afa', etc.
|
||||
- Development versions (no git hash available): '1.8.0.dev-Unknown'
|
||||
|
||||
Comparing needs to be done against a valid version string or other
|
||||
`NumpyVersion` instance.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vstring : str
|
||||
Numpy version string (``np.__version__``).
|
||||
|
||||
Notes
|
||||
-----
|
||||
All dev versions of the same (pre-)release compare equal.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy._lib._version import NumpyVersion
|
||||
>>> if NumpyVersion(np.__version__) < '1.7.0':
|
||||
... print('skip')
|
||||
skip
|
||||
|
||||
>>> NumpyVersion('1.7') # raises ValueError, add ".0"
|
||||
|
||||
"""
|
||||
def __init__(self, vstring):
|
||||
self.vstring = vstring
|
||||
ver_main = re.match(r'\d[.]\d+[.]\d+', vstring)
|
||||
if not ver_main:
|
||||
raise ValueError("Not a valid numpy version string")
|
||||
|
||||
self.version = ver_main.group()
|
||||
self.major, self.minor, self.bugfix = [int(x) for x in
|
||||
self.version.split('.')]
|
||||
if len(vstring) == ver_main.end():
|
||||
self.pre_release = 'final'
|
||||
else:
|
||||
alpha = re.match(r'a\d', vstring[ver_main.end():])
|
||||
beta = re.match(r'b\d', vstring[ver_main.end():])
|
||||
rc = re.match(r'rc\d', vstring[ver_main.end():])
|
||||
pre_rel = [m for m in [alpha, beta, rc] if m is not None]
|
||||
if pre_rel:
|
||||
self.pre_release = pre_rel[0].group()
|
||||
else:
|
||||
self.pre_release = ''
|
||||
|
||||
self.is_devversion = bool(re.search(r'.dev', vstring))
|
||||
|
||||
def _compare_version(self, other):
|
||||
"""Compare major.minor.bugfix"""
|
||||
if self.major == other.major:
|
||||
if self.minor == other.minor:
|
||||
if self.bugfix == other.bugfix:
|
||||
vercmp = 0
|
||||
elif self.bugfix > other.bugfix:
|
||||
vercmp = 1
|
||||
else:
|
||||
vercmp = -1
|
||||
elif self.minor > other.minor:
|
||||
vercmp = 1
|
||||
else:
|
||||
vercmp = -1
|
||||
elif self.major > other.major:
|
||||
vercmp = 1
|
||||
else:
|
||||
vercmp = -1
|
||||
|
||||
return vercmp
|
||||
|
||||
def _compare_pre_release(self, other):
|
||||
"""Compare alpha/beta/rc/final."""
|
||||
if self.pre_release == other.pre_release:
|
||||
vercmp = 0
|
||||
elif self.pre_release == 'final':
|
||||
vercmp = 1
|
||||
elif other.pre_release == 'final':
|
||||
vercmp = -1
|
||||
elif self.pre_release > other.pre_release:
|
||||
vercmp = 1
|
||||
else:
|
||||
vercmp = -1
|
||||
|
||||
return vercmp
|
||||
|
||||
def _compare(self, other):
|
||||
if not isinstance(other, (string_types, NumpyVersion)):
|
||||
raise ValueError("Invalid object to compare with NumpyVersion.")
|
||||
|
||||
if isinstance(other, string_types):
|
||||
other = NumpyVersion(other)
|
||||
|
||||
vercmp = self._compare_version(other)
|
||||
if vercmp == 0:
|
||||
# Same x.y.z version, check for alpha/beta/rc
|
||||
vercmp = self._compare_pre_release(other)
|
||||
if vercmp == 0:
|
||||
# Same version and same pre-release, check if dev version
|
||||
if self.is_devversion is other.is_devversion:
|
||||
vercmp = 0
|
||||
elif self.is_devversion:
|
||||
vercmp = -1
|
||||
else:
|
||||
vercmp = 1
|
||||
|
||||
return vercmp
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._compare(other) < 0
|
||||
|
||||
def __le__(self, other):
|
||||
return self._compare(other) <= 0
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._compare(other) == 0
|
||||
|
||||
def __ne__(self, other):
|
||||
return self._compare(other) != 0
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._compare(other) > 0
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._compare(other) >= 0
|
||||
|
||||
def __repr__(self):
|
||||
return "NumpyVersion(%s)" % self.vstring
|
||||
@@ -1,425 +0,0 @@
|
||||
# ######################### LICENSE ############################ #
|
||||
|
||||
# Copyright (c) 2005-2015, Michele Simionato
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
|
||||
# Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# Redistributions in bytecode form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
# DAMAGE.
|
||||
|
||||
"""
|
||||
Decorator module, see https://pypi.python.org/pypi/decorator
|
||||
for the documentation.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import re
|
||||
import sys
|
||||
import inspect
|
||||
import operator
|
||||
import itertools
|
||||
import collections
|
||||
|
||||
__version__ = '4.0.5'
|
||||
|
||||
if sys.version >= '3':
|
||||
from inspect import getfullargspec
|
||||
|
||||
def get_init(cls):
|
||||
return cls.__init__
|
||||
else:
|
||||
class getfullargspec(object):
|
||||
"A quick and dirty replacement for getfullargspec for Python 2.X"
|
||||
def __init__(self, f):
|
||||
self.args, self.varargs, self.varkw, self.defaults = \
|
||||
inspect.getargspec(f)
|
||||
self.kwonlyargs = []
|
||||
self.kwonlydefaults = None
|
||||
|
||||
def __iter__(self):
|
||||
yield self.args
|
||||
yield self.varargs
|
||||
yield self.varkw
|
||||
yield self.defaults
|
||||
|
||||
getargspec = inspect.getargspec
|
||||
|
||||
def get_init(cls):
|
||||
return cls.__init__.__func__
|
||||
|
||||
# getargspec has been deprecated in Python 3.5
|
||||
ArgSpec = collections.namedtuple(
|
||||
'ArgSpec', 'args varargs varkw defaults')
|
||||
|
||||
|
||||
def getargspec(f):
|
||||
"""A replacement for inspect.getargspec"""
|
||||
spec = getfullargspec(f)
|
||||
return ArgSpec(spec.args, spec.varargs, spec.varkw, spec.defaults)
|
||||
|
||||
|
||||
DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(')
|
||||
|
||||
|
||||
# basic functionality
|
||||
class FunctionMaker(object):
|
||||
"""
|
||||
An object with the ability to create functions with a given signature.
|
||||
It has attributes name, doc, module, signature, defaults, dict and
|
||||
methods update and make.
|
||||
"""
|
||||
|
||||
# Atomic get-and-increment provided by the GIL
|
||||
_compile_count = itertools.count()
|
||||
|
||||
def __init__(self, func=None, name=None, signature=None,
|
||||
defaults=None, doc=None, module=None, funcdict=None):
|
||||
self.shortsignature = signature
|
||||
if func:
|
||||
# func can be a class or a callable, but not an instance method
|
||||
self.name = func.__name__
|
||||
if self.name == '<lambda>': # small hack for lambda functions
|
||||
self.name = '_lambda_'
|
||||
self.doc = func.__doc__
|
||||
self.module = func.__module__
|
||||
if inspect.isfunction(func):
|
||||
argspec = getfullargspec(func)
|
||||
self.annotations = getattr(func, '__annotations__', {})
|
||||
for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs',
|
||||
'kwonlydefaults'):
|
||||
setattr(self, a, getattr(argspec, a))
|
||||
for i, arg in enumerate(self.args):
|
||||
setattr(self, 'arg%d' % i, arg)
|
||||
if sys.version < '3': # easy way
|
||||
self.shortsignature = self.signature = (
|
||||
inspect.formatargspec(
|
||||
formatvalue=lambda val: "", *argspec)[1:-1])
|
||||
else: # Python 3 way
|
||||
allargs = list(self.args)
|
||||
allshortargs = list(self.args)
|
||||
if self.varargs:
|
||||
allargs.append('*' + self.varargs)
|
||||
allshortargs.append('*' + self.varargs)
|
||||
elif self.kwonlyargs:
|
||||
allargs.append('*') # single star syntax
|
||||
for a in self.kwonlyargs:
|
||||
allargs.append('%s=None' % a)
|
||||
allshortargs.append('%s=%s' % (a, a))
|
||||
if self.varkw:
|
||||
allargs.append('**' + self.varkw)
|
||||
allshortargs.append('**' + self.varkw)
|
||||
self.signature = ', '.join(allargs)
|
||||
self.shortsignature = ', '.join(allshortargs)
|
||||
self.dict = func.__dict__.copy()
|
||||
# func=None happens when decorating a caller
|
||||
if name:
|
||||
self.name = name
|
||||
if signature is not None:
|
||||
self.signature = signature
|
||||
if defaults:
|
||||
self.defaults = defaults
|
||||
if doc:
|
||||
self.doc = doc
|
||||
if module:
|
||||
self.module = module
|
||||
if funcdict:
|
||||
self.dict = funcdict
|
||||
# check existence required attributes
|
||||
assert hasattr(self, 'name')
|
||||
if not hasattr(self, 'signature'):
|
||||
raise TypeError('You are decorating a non function: %s' % func)
|
||||
|
||||
def update(self, func, **kw):
|
||||
"Update the signature of func with the data in self"
|
||||
func.__name__ = self.name
|
||||
func.__doc__ = getattr(self, 'doc', None)
|
||||
func.__dict__ = getattr(self, 'dict', {})
|
||||
func.__defaults__ = getattr(self, 'defaults', ())
|
||||
func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None)
|
||||
func.__annotations__ = getattr(self, 'annotations', None)
|
||||
try:
|
||||
frame = sys._getframe(3)
|
||||
except AttributeError: # for IronPython and similar implementations
|
||||
callermodule = '?'
|
||||
else:
|
||||
callermodule = frame.f_globals.get('__name__', '?')
|
||||
func.__module__ = getattr(self, 'module', callermodule)
|
||||
func.__dict__.update(kw)
|
||||
|
||||
def make(self, src_templ, evaldict=None, addsource=False, **attrs):
|
||||
"Make a new function from a given template and update the signature"
|
||||
src = src_templ % vars(self) # expand name and signature
|
||||
evaldict = evaldict or {}
|
||||
mo = DEF.match(src)
|
||||
if mo is None:
|
||||
raise SyntaxError('not a valid function template\n%s' % src)
|
||||
name = mo.group(1) # extract the function name
|
||||
names = set([name] + [arg.strip(' *') for arg in
|
||||
self.shortsignature.split(',')])
|
||||
for n in names:
|
||||
if n in ('_func_', '_call_'):
|
||||
raise NameError('%s is overridden in\n%s' % (n, src))
|
||||
if not src.endswith('\n'): # add a newline just for safety
|
||||
src += '\n' # this is needed in old versions of Python
|
||||
|
||||
# Ensure each generated function has a unique filename for profilers
|
||||
# (such as cProfile) that depend on the tuple of (<filename>,
|
||||
# <definition line>, <function name>) being unique.
|
||||
filename = '<decorator-gen-%d>' % (next(self._compile_count),)
|
||||
try:
|
||||
code = compile(src, filename, 'single')
|
||||
exec(code, evaldict)
|
||||
except: # noqa: E722
|
||||
print('Error in generated code:', file=sys.stderr)
|
||||
print(src, file=sys.stderr)
|
||||
raise
|
||||
func = evaldict[name]
|
||||
if addsource:
|
||||
attrs['__source__'] = src
|
||||
self.update(func, **attrs)
|
||||
return func
|
||||
|
||||
@classmethod
|
||||
def create(cls, obj, body, evaldict, defaults=None,
|
||||
doc=None, module=None, addsource=True, **attrs):
|
||||
"""
|
||||
Create a function from the strings name, signature and body.
|
||||
evaldict is the evaluation dictionary. If addsource is true an
|
||||
attribute __source__ is added to the result. The attributes attrs
|
||||
are added, if any.
|
||||
"""
|
||||
if isinstance(obj, str): # "name(signature)"
|
||||
name, rest = obj.strip().split('(', 1)
|
||||
signature = rest[:-1] # strip a right parens
|
||||
func = None
|
||||
else: # a function
|
||||
name = None
|
||||
signature = None
|
||||
func = obj
|
||||
self = cls(func, name, signature, defaults, doc, module)
|
||||
ibody = '\n'.join(' ' + line for line in body.splitlines())
|
||||
return self.make('def %(name)s(%(signature)s):\n' + ibody,
|
||||
evaldict, addsource, **attrs)
|
||||
|
||||
|
||||
def decorate(func, caller):
|
||||
"""
|
||||
decorate(func, caller) decorates a function using a caller.
|
||||
"""
|
||||
evaldict = func.__globals__.copy()
|
||||
evaldict['_call_'] = caller
|
||||
evaldict['_func_'] = func
|
||||
fun = FunctionMaker.create(
|
||||
func, "return _call_(_func_, %(shortsignature)s)",
|
||||
evaldict, __wrapped__=func)
|
||||
if hasattr(func, '__qualname__'):
|
||||
fun.__qualname__ = func.__qualname__
|
||||
return fun
|
||||
|
||||
|
||||
def decorator(caller, _func=None):
|
||||
"""decorator(caller) converts a caller function into a decorator"""
|
||||
if _func is not None: # return a decorated function
|
||||
# this is obsolete behavior; you should use decorate instead
|
||||
return decorate(_func, caller)
|
||||
# else return a decorator function
|
||||
if inspect.isclass(caller):
|
||||
name = caller.__name__.lower()
|
||||
callerfunc = get_init(caller)
|
||||
doc = 'decorator(%s) converts functions/generators into ' \
|
||||
'factories of %s objects' % (caller.__name__, caller.__name__)
|
||||
elif inspect.isfunction(caller):
|
||||
if caller.__name__ == '<lambda>':
|
||||
name = '_lambda_'
|
||||
else:
|
||||
name = caller.__name__
|
||||
callerfunc = caller
|
||||
doc = caller.__doc__
|
||||
else: # assume caller is an object with a __call__ method
|
||||
name = caller.__class__.__name__.lower()
|
||||
callerfunc = caller.__call__.__func__
|
||||
doc = caller.__call__.__doc__
|
||||
evaldict = callerfunc.__globals__.copy()
|
||||
evaldict['_call_'] = caller
|
||||
evaldict['_decorate_'] = decorate
|
||||
return FunctionMaker.create(
|
||||
'%s(func)' % name, 'return _decorate_(func, _call_)',
|
||||
evaldict, doc=doc, module=caller.__module__,
|
||||
__wrapped__=caller)
|
||||
|
||||
|
||||
# ####################### contextmanager ####################### #
|
||||
|
||||
try: # Python >= 3.2
|
||||
from contextlib import _GeneratorContextManager
|
||||
except ImportError: # Python >= 2.5
|
||||
from contextlib import GeneratorContextManager as _GeneratorContextManager
|
||||
|
||||
|
||||
class ContextManager(_GeneratorContextManager):
|
||||
def __call__(self, func):
|
||||
"""Context manager decorator"""
|
||||
return FunctionMaker.create(
|
||||
func, "with _self_: return _func_(%(shortsignature)s)",
|
||||
dict(_self_=self, _func_=func), __wrapped__=func)
|
||||
|
||||
|
||||
init = getfullargspec(_GeneratorContextManager.__init__)
|
||||
n_args = len(init.args)
|
||||
if n_args == 2 and not init.varargs: # (self, genobj) Python 2.7
|
||||
def __init__(self, g, *a, **k):
|
||||
return _GeneratorContextManager.__init__(self, g(*a, **k))
|
||||
ContextManager.__init__ = __init__
|
||||
elif n_args == 2 and init.varargs: # (self, gen, *a, **k) Python 3.4
|
||||
pass
|
||||
elif n_args == 4: # (self, gen, args, kwds) Python 3.5
|
||||
def __init__(self, g, *a, **k):
|
||||
return _GeneratorContextManager.__init__(self, g, a, k)
|
||||
ContextManager.__init__ = __init__
|
||||
|
||||
contextmanager = decorator(ContextManager)
|
||||
|
||||
|
||||
# ############################ dispatch_on ############################ #
|
||||
|
||||
def append(a, vancestors):
|
||||
"""
|
||||
Append ``a`` to the list of the virtual ancestors, unless it is already
|
||||
included.
|
||||
"""
|
||||
add = True
|
||||
for j, va in enumerate(vancestors):
|
||||
if issubclass(va, a):
|
||||
add = False
|
||||
break
|
||||
if issubclass(a, va):
|
||||
vancestors[j] = a
|
||||
add = False
|
||||
if add:
|
||||
vancestors.append(a)
|
||||
|
||||
|
||||
# inspired from simplegeneric by P.J. Eby and functools.singledispatch
|
||||
def dispatch_on(*dispatch_args):
|
||||
"""
|
||||
Factory of decorators turning a function into a generic function
|
||||
dispatching on the given arguments.
|
||||
"""
|
||||
assert dispatch_args, 'No dispatch args passed'
|
||||
dispatch_str = '(%s,)' % ', '.join(dispatch_args)
|
||||
|
||||
def check(arguments, wrong=operator.ne, msg=''):
|
||||
"""Make sure one passes the expected number of arguments"""
|
||||
if wrong(len(arguments), len(dispatch_args)):
|
||||
raise TypeError('Expected %d arguments, got %d%s' %
|
||||
(len(dispatch_args), len(arguments), msg))
|
||||
|
||||
def gen_func_dec(func):
|
||||
"""Decorator turning a function into a generic function"""
|
||||
|
||||
# first check the dispatch arguments
|
||||
argset = set(getfullargspec(func).args)
|
||||
if not set(dispatch_args) <= argset:
|
||||
raise NameError('Unknown dispatch arguments %s' % dispatch_str)
|
||||
|
||||
typemap = {}
|
||||
|
||||
def vancestors(*types):
|
||||
"""
|
||||
Get a list of sets of virtual ancestors for the given types
|
||||
"""
|
||||
check(types)
|
||||
ras = [[] for _ in range(len(dispatch_args))]
|
||||
for types_ in typemap:
|
||||
for t, type_, ra in zip(types, types_, ras):
|
||||
if issubclass(t, type_) and type_ not in t.__mro__:
|
||||
append(type_, ra)
|
||||
return [set(ra) for ra in ras]
|
||||
|
||||
def ancestors(*types):
|
||||
"""
|
||||
Get a list of virtual MROs, one for each type
|
||||
"""
|
||||
check(types)
|
||||
lists = []
|
||||
for t, vas in zip(types, vancestors(*types)):
|
||||
n_vas = len(vas)
|
||||
if n_vas > 1:
|
||||
raise RuntimeError(
|
||||
'Ambiguous dispatch for %s: %s' % (t, vas))
|
||||
elif n_vas == 1:
|
||||
va, = vas
|
||||
mro = type('t', (t, va), {}).__mro__[1:]
|
||||
else:
|
||||
mro = t.__mro__
|
||||
lists.append(mro[:-1]) # discard t and object
|
||||
return lists
|
||||
|
||||
def register(*types):
|
||||
"""
|
||||
Decorator to register an implementation for the given types
|
||||
"""
|
||||
check(types)
|
||||
|
||||
def dec(f):
|
||||
check(getfullargspec(f).args, operator.lt, ' in ' + f.__name__)
|
||||
typemap[types] = f
|
||||
return f
|
||||
return dec
|
||||
|
||||
def dispatch_info(*types):
|
||||
"""
|
||||
An utility to introspect the dispatch algorithm
|
||||
"""
|
||||
check(types)
|
||||
lst = []
|
||||
for anc in itertools.product(*ancestors(*types)):
|
||||
lst.append(tuple(a.__name__ for a in anc))
|
||||
return lst
|
||||
|
||||
def _dispatch(dispatch_args, *args, **kw):
|
||||
types = tuple(type(arg) for arg in dispatch_args)
|
||||
try: # fast path
|
||||
f = typemap[types]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
return f(*args, **kw)
|
||||
combinations = itertools.product(*ancestors(*types))
|
||||
next(combinations) # the first one has been already tried
|
||||
for types_ in combinations:
|
||||
f = typemap.get(types_)
|
||||
if f is not None:
|
||||
return f(*args, **kw)
|
||||
|
||||
# else call the default implementation
|
||||
return func(*args, **kw)
|
||||
|
||||
return FunctionMaker.create(
|
||||
func, 'return _f_(%s, %%(shortsignature)s)' % dispatch_str,
|
||||
dict(_f_=_dispatch), register=register, default=func,
|
||||
typemap=typemap, vancestors=vancestors, ancestors=ancestors,
|
||||
dispatch_info=dispatch_info, __wrapped__=func)
|
||||
|
||||
gen_func_dec.__name__ = 'dispatch_on' + dispatch_str
|
||||
return gen_func_dec
|
||||
BIN
Binary file not shown.
@@ -1,52 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def configuration(parent_package='',top_path=None):
|
||||
from numpy.distutils.misc_util import Configuration
|
||||
|
||||
config = Configuration('_lib', parent_package, top_path)
|
||||
config.add_data_files('tests/*.py')
|
||||
|
||||
include_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
|
||||
depends = [os.path.join(include_dir, 'ccallback.h')]
|
||||
|
||||
config.add_extension("_ccallback_c",
|
||||
sources=["_ccallback_c.c"],
|
||||
depends=depends,
|
||||
include_dirs=[include_dir])
|
||||
|
||||
config.add_extension("_test_ccallback",
|
||||
sources=["src/_test_ccallback.c"],
|
||||
depends=depends,
|
||||
include_dirs=[include_dir])
|
||||
|
||||
config.add_extension("_fpumode",
|
||||
sources=["_fpumode.c"])
|
||||
|
||||
def get_messagestream_config(ext, build_dir):
|
||||
# Generate a header file containing defines
|
||||
config_cmd = config.get_config_cmd()
|
||||
defines = []
|
||||
if config_cmd.check_func('open_memstream', decl=True, call=True):
|
||||
defines.append(('HAVE_OPEN_MEMSTREAM', '1'))
|
||||
target = os.path.join(os.path.dirname(__file__), 'src',
|
||||
'messagestream_config.h')
|
||||
with open(target, 'w') as f:
|
||||
for name, value in defines:
|
||||
f.write('#define {0} {1}\n'.format(name, value))
|
||||
|
||||
depends = [os.path.join(include_dir, 'messagestream.h')]
|
||||
config.add_extension("messagestream",
|
||||
sources=["messagestream.c"] + [get_messagestream_config],
|
||||
depends=depends,
|
||||
include_dirs=[include_dir])
|
||||
|
||||
return config
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from numpy.distutils.core import setup
|
||||
|
||||
setup(**configuration(top_path='').todict())
|
||||
@@ -1,276 +0,0 @@
|
||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||
|
||||
# Copyright (c) 2010-2012 Benjamin Peterson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import operator
|
||||
import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.2.0"
|
||||
|
||||
|
||||
# True if we are running on Python 3.
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
integer_types = int,
|
||||
class_types = type,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
|
||||
MAXSIZE = sys.maxsize
|
||||
else:
|
||||
string_types = basestring,
|
||||
integer_types = (int, long)
|
||||
class_types = (type, types.ClassType)
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
|
||||
if sys.platform.startswith("java"):
|
||||
# Jython always uses 32 bits.
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||
class X(object):
|
||||
def __len__(self):
|
||||
return 1 << 31
|
||||
try:
|
||||
len(X())
|
||||
except OverflowError:
|
||||
# 32-bit
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# 64-bit
|
||||
MAXSIZE = int((1 << 63) - 1)
|
||||
del X
|
||||
|
||||
|
||||
def _add_doc(func, doc):
|
||||
"""Add documentation to a function."""
|
||||
func.__doc__ = doc
|
||||
|
||||
|
||||
def _import_module(name):
|
||||
"""Import module, returning the module after the last dot."""
|
||||
__import__(name)
|
||||
return sys.modules[name]
|
||||
|
||||
|
||||
# Replacement for lazy loading stuff in upstream six. See gh-2764
|
||||
if PY3:
|
||||
import builtins
|
||||
import functools
|
||||
reduce = functools.reduce
|
||||
zip = builtins.zip
|
||||
xrange = builtins.range
|
||||
else:
|
||||
import __builtin__
|
||||
import itertools
|
||||
builtins = __builtin__
|
||||
reduce = __builtin__.reduce
|
||||
zip = itertools.izip
|
||||
xrange = __builtin__.xrange
|
||||
|
||||
|
||||
if PY3:
|
||||
_meth_func = "__func__"
|
||||
_meth_self = "__self__"
|
||||
|
||||
_func_code = "__code__"
|
||||
_func_defaults = "__defaults__"
|
||||
|
||||
_iterkeys = "keys"
|
||||
_itervalues = "values"
|
||||
_iteritems = "items"
|
||||
else:
|
||||
_meth_func = "im_func"
|
||||
_meth_self = "im_self"
|
||||
|
||||
_func_code = "func_code"
|
||||
_func_defaults = "func_defaults"
|
||||
|
||||
_iterkeys = "iterkeys"
|
||||
_itervalues = "itervalues"
|
||||
_iteritems = "iteritems"
|
||||
|
||||
|
||||
try:
|
||||
advance_iterator = next
|
||||
except NameError:
|
||||
def advance_iterator(it):
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
|
||||
if PY3:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound
|
||||
|
||||
Iterator = object
|
||||
|
||||
def callable(obj):
|
||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||
else:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound.im_func
|
||||
|
||||
class Iterator(object):
|
||||
|
||||
def next(self):
|
||||
return type(self).__next__(self)
|
||||
|
||||
callable = callable
|
||||
_add_doc(get_unbound_function,
|
||||
"""Get the function out of a possibly unbound function""")
|
||||
|
||||
|
||||
get_method_function = operator.attrgetter(_meth_func)
|
||||
get_method_self = operator.attrgetter(_meth_self)
|
||||
get_function_code = operator.attrgetter(_func_code)
|
||||
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||
|
||||
|
||||
def iterkeys(d):
|
||||
"""Return an iterator over the keys of a dictionary."""
|
||||
return iter(getattr(d, _iterkeys)())
|
||||
|
||||
|
||||
def itervalues(d):
|
||||
"""Return an iterator over the values of a dictionary."""
|
||||
return iter(getattr(d, _itervalues)())
|
||||
|
||||
|
||||
def iteritems(d):
|
||||
"""Return an iterator over the (key, value) pairs of a dictionary."""
|
||||
return iter(getattr(d, _iteritems)())
|
||||
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
def u(s):
|
||||
return s
|
||||
|
||||
if sys.version_info[1] <= 1:
|
||||
def int2byte(i):
|
||||
return bytes((i,))
|
||||
else:
|
||||
# This is about 2x faster than the implementation above on 3.2+
|
||||
int2byte = operator.methodcaller("to_bytes", 1, "big")
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
BytesIO = io.BytesIO
|
||||
else:
|
||||
def b(s):
|
||||
return s
|
||||
|
||||
def u(s):
|
||||
return unicode(s, "unicode_escape")
|
||||
int2byte = chr
|
||||
import StringIO
|
||||
StringIO = BytesIO = StringIO.StringIO
|
||||
_add_doc(b, """Byte literal""")
|
||||
_add_doc(u, """Text literal""")
|
||||
|
||||
|
||||
if PY3:
|
||||
import builtins
|
||||
exec_ = getattr(builtins, "exec")
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
print_ = getattr(builtins, "print")
|
||||
del builtins
|
||||
|
||||
else:
|
||||
def exec_(code, globs=None, locs=None):
|
||||
"""Execute code in a namespace."""
|
||||
if globs is None:
|
||||
frame = sys._getframe(1)
|
||||
globs = frame.f_globals
|
||||
if locs is None:
|
||||
locs = frame.f_locals
|
||||
del frame
|
||||
elif locs is None:
|
||||
locs = globs
|
||||
exec("""exec code in globs, locs""")
|
||||
|
||||
exec_("""def reraise(tp, value, tb=None):
|
||||
raise tp, value, tb
|
||||
""")
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
"""The new-style print function."""
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
|
||||
def write(data):
|
||||
if not isinstance(data, basestring):
|
||||
data = str(data)
|
||||
fp.write(data)
|
||||
want_unicode = False
|
||||
sep = kwargs.pop("sep", None)
|
||||
if sep is not None:
|
||||
if isinstance(sep, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(sep, str):
|
||||
raise TypeError("sep must be None or a string")
|
||||
end = kwargs.pop("end", None)
|
||||
if end is not None:
|
||||
if isinstance(end, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(end, str):
|
||||
raise TypeError("end must be None or a string")
|
||||
if kwargs:
|
||||
raise TypeError("invalid keyword arguments to print()")
|
||||
if not want_unicode:
|
||||
for arg in args:
|
||||
if isinstance(arg, unicode):
|
||||
want_unicode = True
|
||||
break
|
||||
if want_unicode:
|
||||
newline = unicode("\n")
|
||||
space = unicode(" ")
|
||||
else:
|
||||
newline = "\n"
|
||||
space = " "
|
||||
if sep is None:
|
||||
sep = space
|
||||
if end is None:
|
||||
end = newline
|
||||
for i, arg in enumerate(args):
|
||||
if i:
|
||||
write(sep)
|
||||
write(arg)
|
||||
write(end)
|
||||
|
||||
_add_doc(reraise, """Reraise an exception.""")
|
||||
|
||||
|
||||
def with_metaclass(meta, base=object):
|
||||
"""Create a base class with a metaclass."""
|
||||
return meta("NewBase", (base,), {})
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,99 +0,0 @@
|
||||
""" Test for assert_deallocated context manager and gc utilities
|
||||
"""
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import gc
|
||||
|
||||
from scipy._lib._gcutils import (set_gc_state, gc_state, assert_deallocated,
|
||||
ReferenceError, IS_PYPY)
|
||||
|
||||
from numpy.testing import assert_equal
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_set_gc_state():
|
||||
gc_status = gc.isenabled()
|
||||
try:
|
||||
for state in (True, False):
|
||||
gc.enable()
|
||||
set_gc_state(state)
|
||||
assert_equal(gc.isenabled(), state)
|
||||
gc.disable()
|
||||
set_gc_state(state)
|
||||
assert_equal(gc.isenabled(), state)
|
||||
finally:
|
||||
if gc_status:
|
||||
gc.enable()
|
||||
|
||||
|
||||
def test_gc_state():
|
||||
# Test gc_state context manager
|
||||
gc_status = gc.isenabled()
|
||||
try:
|
||||
for pre_state in (True, False):
|
||||
set_gc_state(pre_state)
|
||||
for with_state in (True, False):
|
||||
# Check the gc state is with_state in with block
|
||||
with gc_state(with_state):
|
||||
assert_equal(gc.isenabled(), with_state)
|
||||
# And returns to previous state outside block
|
||||
assert_equal(gc.isenabled(), pre_state)
|
||||
# Even if the gc state is set explicitly within the block
|
||||
with gc_state(with_state):
|
||||
assert_equal(gc.isenabled(), with_state)
|
||||
set_gc_state(not with_state)
|
||||
assert_equal(gc.isenabled(), pre_state)
|
||||
finally:
|
||||
if gc_status:
|
||||
gc.enable()
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
||||
def test_assert_deallocated():
|
||||
# Ordinary use
|
||||
class C(object):
|
||||
def __init__(self, arg0, arg1, name='myname'):
|
||||
self.name = name
|
||||
for gc_current in (True, False):
|
||||
with gc_state(gc_current):
|
||||
# We are deleting from with-block context, so that's OK
|
||||
with assert_deallocated(C, 0, 2, 'another name') as c:
|
||||
assert_equal(c.name, 'another name')
|
||||
del c
|
||||
# Or not using the thing in with-block context, also OK
|
||||
with assert_deallocated(C, 0, 2, name='third name'):
|
||||
pass
|
||||
assert_equal(gc.isenabled(), gc_current)
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
||||
def test_assert_deallocated_nodel():
|
||||
class C(object):
|
||||
pass
|
||||
with pytest.raises(ReferenceError):
|
||||
# Need to delete after using if in with-block context
|
||||
with assert_deallocated(C) as c:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
||||
def test_assert_deallocated_circular():
|
||||
class C(object):
|
||||
def __init__(self):
|
||||
self._circular = self
|
||||
with pytest.raises(ReferenceError):
|
||||
# Circular reference, no automatic garbage collection
|
||||
with assert_deallocated(C) as c:
|
||||
del c
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy")
|
||||
def test_assert_deallocated_circular2():
|
||||
class C(object):
|
||||
def __init__(self):
|
||||
self._circular = self
|
||||
with pytest.raises(ReferenceError):
|
||||
# Still circular reference, no automatic garbage collection
|
||||
with assert_deallocated(C):
|
||||
pass
|
||||
@@ -1,34 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import sys
|
||||
from scipy._lib._testutils import _parse_size, _get_mem_available
|
||||
import pytest
|
||||
|
||||
|
||||
def test__parse_size():
|
||||
expected = {
|
||||
'12': 12e6,
|
||||
'12 b': 12,
|
||||
'12k': 12e3,
|
||||
' 12 M ': 12e6,
|
||||
' 12 G ': 12e9,
|
||||
' 12Tb ': 12e12,
|
||||
'12 Mib ': 12 * 1024.0**2,
|
||||
'12Tib': 12 * 1024.0**4,
|
||||
}
|
||||
|
||||
for inp, outp in sorted(expected.items()):
|
||||
if outp is None:
|
||||
with pytest.raises(ValueError):
|
||||
_parse_size(inp)
|
||||
else:
|
||||
assert _parse_size(inp) == outp
|
||||
|
||||
|
||||
def test__mem_available():
|
||||
# May return None on non-Linux platforms
|
||||
available = _get_mem_available()
|
||||
if sys.platform.startswith('linux'):
|
||||
assert available >= 0
|
||||
else:
|
||||
assert available is None or available >= 0
|
||||
@@ -1,53 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from numpy.testing import assert_
|
||||
from pytest import raises as assert_raises
|
||||
|
||||
from scipy._lib._threadsafety import ReentrancyLock, non_reentrant, ReentrancyError
|
||||
|
||||
|
||||
def test_parallel_threads():
|
||||
# Check that ReentrancyLock serializes work in parallel threads.
|
||||
#
|
||||
# The test is not fully deterministic, and may succeed falsely if
|
||||
# the timings go wrong.
|
||||
|
||||
lock = ReentrancyLock("failure")
|
||||
|
||||
failflag = [False]
|
||||
exceptions_raised = []
|
||||
|
||||
def worker(k):
|
||||
try:
|
||||
with lock:
|
||||
assert_(not failflag[0])
|
||||
failflag[0] = True
|
||||
time.sleep(0.1 * k)
|
||||
assert_(failflag[0])
|
||||
failflag[0] = False
|
||||
except Exception:
|
||||
exceptions_raised.append(traceback.format_exc(2))
|
||||
|
||||
threads = [threading.Thread(target=lambda k=k: worker(k))
|
||||
for k in range(3)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
exceptions_raised = "\n".join(exceptions_raised)
|
||||
assert_(not exceptions_raised, exceptions_raised)
|
||||
|
||||
|
||||
def test_reentering():
|
||||
# Check that ReentrancyLock prevents re-entering from the same thread.
|
||||
|
||||
@non_reentrant()
|
||||
def func(x):
|
||||
return func(x)
|
||||
|
||||
assert_raises(ReentrancyError, func, 0)
|
||||
@@ -1,109 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
from multiprocessing import Pool
|
||||
from multiprocessing.pool import Pool as PWL
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_
|
||||
from pytest import raises as assert_raises
|
||||
|
||||
from scipy._lib._util import _aligned_zeros, check_random_state, MapWrapper
|
||||
|
||||
|
||||
def test__aligned_zeros():
|
||||
niter = 10
|
||||
|
||||
def check(shape, dtype, order, align):
|
||||
err_msg = repr((shape, dtype, order, align))
|
||||
x = _aligned_zeros(shape, dtype, order, align=align)
|
||||
if align is None:
|
||||
align = np.dtype(dtype).alignment
|
||||
assert_equal(x.__array_interface__['data'][0] % align, 0)
|
||||
if hasattr(shape, '__len__'):
|
||||
assert_equal(x.shape, shape, err_msg)
|
||||
else:
|
||||
assert_equal(x.shape, (shape,), err_msg)
|
||||
assert_equal(x.dtype, dtype)
|
||||
if order == "C":
|
||||
assert_(x.flags.c_contiguous, err_msg)
|
||||
elif order == "F":
|
||||
if x.size > 0:
|
||||
# Size-0 arrays get invalid flags on Numpy 1.5
|
||||
assert_(x.flags.f_contiguous, err_msg)
|
||||
elif order is None:
|
||||
assert_(x.flags.c_contiguous, err_msg)
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
# try various alignments
|
||||
for align in [1, 2, 3, 4, 8, 16, 32, 64, None]:
|
||||
for n in [0, 1, 3, 11]:
|
||||
for order in ["C", "F", None]:
|
||||
for dtype in [np.uint8, np.float64]:
|
||||
for shape in [n, (1, 2, 3, n)]:
|
||||
for j in range(niter):
|
||||
check(shape, dtype, order, align)
|
||||
|
||||
|
||||
def test_check_random_state():
|
||||
# If seed is None, return the RandomState singleton used by np.random.
|
||||
# If seed is an int, return a new RandomState instance seeded with seed.
|
||||
# If seed is already a RandomState instance, return it.
|
||||
# Otherwise raise ValueError.
|
||||
rsi = check_random_state(1)
|
||||
assert_equal(type(rsi), np.random.RandomState)
|
||||
rsi = check_random_state(rsi)
|
||||
assert_equal(type(rsi), np.random.RandomState)
|
||||
rsi = check_random_state(None)
|
||||
assert_equal(type(rsi), np.random.RandomState)
|
||||
assert_raises(ValueError, check_random_state, 'a')
|
||||
|
||||
|
||||
class TestMapWrapper(object):
|
||||
|
||||
def setup_method(self):
|
||||
self.input = np.arange(10.)
|
||||
self.output = np.sin(self.input)
|
||||
|
||||
def test_serial(self):
|
||||
p = MapWrapper(1)
|
||||
assert_(p._mapfunc is map)
|
||||
assert_(p.pool is None)
|
||||
assert_(p._own_pool is False)
|
||||
out = list(p(np.sin, self.input))
|
||||
assert_equal(out, self.output)
|
||||
|
||||
with assert_raises(RuntimeError):
|
||||
p = MapWrapper(0)
|
||||
|
||||
def test_parallel(self):
|
||||
with MapWrapper(2) as p:
|
||||
out = p(np.sin, self.input)
|
||||
assert_equal(list(out), self.output)
|
||||
|
||||
assert_(p._own_pool is True)
|
||||
assert_(isinstance(p.pool, PWL))
|
||||
assert_(p._mapfunc is not None)
|
||||
|
||||
# the context manager should've closed the internal pool
|
||||
# check that it has by asking it to calculate again.
|
||||
with assert_raises(Exception) as excinfo:
|
||||
p(np.sin, self.input)
|
||||
|
||||
# on py27 an AssertionError is raised, on >py27 it's a ValueError
|
||||
err_type = excinfo.type
|
||||
assert_((err_type is ValueError) or (err_type is AssertionError))
|
||||
|
||||
# can also set a PoolWrapper up with a map-like callable instance
|
||||
try:
|
||||
p = Pool(2)
|
||||
q = MapWrapper(p.map)
|
||||
|
||||
assert_(q._own_pool is False)
|
||||
q.close()
|
||||
|
||||
# closing the PoolWrapper shouldn't close the internal pool
|
||||
# because it didn't create it
|
||||
out = p.map(np.sin, self.input)
|
||||
assert_equal(list(out), self.output)
|
||||
finally:
|
||||
p.close()
|
||||
@@ -1,65 +0,0 @@
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
from numpy.testing import assert_
|
||||
from pytest import raises as assert_raises
|
||||
from scipy._lib._version import NumpyVersion
|
||||
|
||||
|
||||
def test_main_versions():
|
||||
assert_(NumpyVersion('1.8.0') == '1.8.0')
|
||||
for ver in ['1.9.0', '2.0.0', '1.8.1']:
|
||||
assert_(NumpyVersion('1.8.0') < ver)
|
||||
|
||||
for ver in ['1.7.0', '1.7.1', '0.9.9']:
|
||||
assert_(NumpyVersion('1.8.0') > ver)
|
||||
|
||||
|
||||
def test_version_1_point_10():
|
||||
# regression test for gh-2998.
|
||||
assert_(NumpyVersion('1.9.0') < '1.10.0')
|
||||
assert_(NumpyVersion('1.11.0') < '1.11.1')
|
||||
assert_(NumpyVersion('1.11.0') == '1.11.0')
|
||||
assert_(NumpyVersion('1.99.11') < '1.99.12')
|
||||
|
||||
|
||||
def test_alpha_beta_rc():
|
||||
assert_(NumpyVersion('1.8.0rc1') == '1.8.0rc1')
|
||||
for ver in ['1.8.0', '1.8.0rc2']:
|
||||
assert_(NumpyVersion('1.8.0rc1') < ver)
|
||||
|
||||
for ver in ['1.8.0a2', '1.8.0b3', '1.7.2rc4']:
|
||||
assert_(NumpyVersion('1.8.0rc1') > ver)
|
||||
|
||||
assert_(NumpyVersion('1.8.0b1') > '1.8.0a2')
|
||||
|
||||
|
||||
def test_dev_version():
|
||||
assert_(NumpyVersion('1.9.0.dev-Unknown') < '1.9.0')
|
||||
for ver in ['1.9.0', '1.9.0a1', '1.9.0b2', '1.9.0b2.dev-ffffffff']:
|
||||
assert_(NumpyVersion('1.9.0.dev-f16acvda') < ver)
|
||||
|
||||
assert_(NumpyVersion('1.9.0.dev-f16acvda') == '1.9.0.dev-11111111')
|
||||
|
||||
|
||||
def test_dev_a_b_rc_mixed():
|
||||
assert_(NumpyVersion('1.9.0a2.dev-f16acvda') == '1.9.0a2.dev-11111111')
|
||||
assert_(NumpyVersion('1.9.0a2.dev-6acvda54') < '1.9.0a2')
|
||||
|
||||
|
||||
def test_dev0_version():
|
||||
assert_(NumpyVersion('1.9.0.dev0+Unknown') < '1.9.0')
|
||||
for ver in ['1.9.0', '1.9.0a1', '1.9.0b2', '1.9.0b2.dev0+ffffffff']:
|
||||
assert_(NumpyVersion('1.9.0.dev0+f16acvda') < ver)
|
||||
|
||||
assert_(NumpyVersion('1.9.0.dev0+f16acvda') == '1.9.0.dev0+11111111')
|
||||
|
||||
|
||||
def test_dev0_a_b_rc_mixed():
|
||||
assert_(NumpyVersion('1.9.0a2.dev0+f16acvda') == '1.9.0a2.dev0+11111111')
|
||||
assert_(NumpyVersion('1.9.0a2.dev0+6acvda54') < '1.9.0a2')
|
||||
|
||||
|
||||
def test_raises():
|
||||
for ver in ['1.9', '1,9.0', '1.7.x']:
|
||||
assert_raises(ValueError, NumpyVersion, ver)
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
from numpy.testing import assert_equal, assert_
|
||||
from pytest import raises as assert_raises
|
||||
|
||||
import time
|
||||
import pytest
|
||||
import ctypes
|
||||
import threading
|
||||
from scipy._lib import _ccallback_c as _test_ccallback_cython
|
||||
from scipy._lib import _test_ccallback
|
||||
from scipy._lib._ccallback import LowLevelCallable
|
||||
|
||||
try:
|
||||
import cffi
|
||||
HAVE_CFFI = True
|
||||
except ImportError:
|
||||
HAVE_CFFI = False
|
||||
|
||||
|
||||
ERROR_VALUE = 2.0
|
||||
|
||||
|
||||
def callback_python(a, user_data=None):
|
||||
if a == ERROR_VALUE:
|
||||
raise ValueError("bad value")
|
||||
|
||||
if user_data is None:
|
||||
return a + 1
|
||||
else:
|
||||
return a + user_data
|
||||
|
||||
def _get_cffi_func(base, signature):
|
||||
if not HAVE_CFFI:
|
||||
pytest.skip("cffi not installed")
|
||||
|
||||
# Get function address
|
||||
voidp = ctypes.cast(base, ctypes.c_void_p)
|
||||
address = voidp.value
|
||||
|
||||
# Create corresponding cffi handle
|
||||
ffi = cffi.FFI()
|
||||
func = ffi.cast(signature, address)
|
||||
return func
|
||||
|
||||
|
||||
def _get_ctypes_data():
|
||||
value = ctypes.c_double(2.0)
|
||||
return ctypes.cast(ctypes.pointer(value), ctypes.c_voidp)
|
||||
|
||||
|
||||
def _get_cffi_data():
|
||||
if not HAVE_CFFI:
|
||||
pytest.skip("cffi not installed")
|
||||
ffi = cffi.FFI()
|
||||
return ffi.new('double *', 2.0)
|
||||
|
||||
|
||||
CALLERS = {
|
||||
'simple': _test_ccallback.test_call_simple,
|
||||
'nodata': _test_ccallback.test_call_nodata,
|
||||
'nonlocal': _test_ccallback.test_call_nonlocal,
|
||||
'cython': _test_ccallback_cython.test_call_cython,
|
||||
}
|
||||
|
||||
# These functions have signatures known to the callers
|
||||
FUNCS = {
|
||||
'python': lambda: callback_python,
|
||||
'capsule': lambda: _test_ccallback.test_get_plus1_capsule(),
|
||||
'cython': lambda: LowLevelCallable.from_cython(_test_ccallback_cython, "plus1_cython"),
|
||||
'ctypes': lambda: _test_ccallback_cython.plus1_ctypes,
|
||||
'cffi': lambda: _get_cffi_func(_test_ccallback_cython.plus1_ctypes,
|
||||
'double (*)(double, int *, void *)'),
|
||||
'capsule_b': lambda: _test_ccallback.test_get_plus1b_capsule(),
|
||||
'cython_b': lambda: LowLevelCallable.from_cython(_test_ccallback_cython, "plus1b_cython"),
|
||||
'ctypes_b': lambda: _test_ccallback_cython.plus1b_ctypes,
|
||||
'cffi_b': lambda: _get_cffi_func(_test_ccallback_cython.plus1b_ctypes,
|
||||
'double (*)(double, double, int *, void *)'),
|
||||
}
|
||||
|
||||
# These functions have signatures the callers don't know
|
||||
BAD_FUNCS = {
|
||||
'capsule_bc': lambda: _test_ccallback.test_get_plus1bc_capsule(),
|
||||
'cython_bc': lambda: LowLevelCallable.from_cython(_test_ccallback_cython, "plus1bc_cython"),
|
||||
'ctypes_bc': lambda: _test_ccallback_cython.plus1bc_ctypes,
|
||||
'cffi_bc': lambda: _get_cffi_func(_test_ccallback_cython.plus1bc_ctypes,
|
||||
'double (*)(double, double, double, int *, void *)'),
|
||||
}
|
||||
|
||||
USER_DATAS = {
|
||||
'ctypes': _get_ctypes_data,
|
||||
'cffi': _get_cffi_data,
|
||||
'capsule': _test_ccallback.test_get_data_capsule,
|
||||
}
|
||||
|
||||
|
||||
def test_callbacks():
|
||||
def check(caller, func, user_data):
|
||||
caller = CALLERS[caller]
|
||||
func = FUNCS[func]()
|
||||
user_data = USER_DATAS[user_data]()
|
||||
|
||||
if func is callback_python:
|
||||
func2 = lambda x: func(x, 2.0)
|
||||
else:
|
||||
func2 = LowLevelCallable(func, user_data)
|
||||
func = LowLevelCallable(func)
|
||||
|
||||
# Test basic call
|
||||
assert_equal(caller(func, 1.0), 2.0)
|
||||
|
||||
# Test 'bad' value resulting to an error
|
||||
assert_raises(ValueError, caller, func, ERROR_VALUE)
|
||||
|
||||
# Test passing in user_data
|
||||
assert_equal(caller(func2, 1.0), 3.0)
|
||||
|
||||
for caller in sorted(CALLERS.keys()):
|
||||
for func in sorted(FUNCS.keys()):
|
||||
for user_data in sorted(USER_DATAS.keys()):
|
||||
check(caller, func, user_data)
|
||||
|
||||
|
||||
def test_bad_callbacks():
|
||||
def check(caller, func, user_data):
|
||||
caller = CALLERS[caller]
|
||||
user_data = USER_DATAS[user_data]()
|
||||
func = BAD_FUNCS[func]()
|
||||
|
||||
if func is callback_python:
|
||||
func2 = lambda x: func(x, 2.0)
|
||||
else:
|
||||
func2 = LowLevelCallable(func, user_data)
|
||||
func = LowLevelCallable(func)
|
||||
|
||||
# Test that basic call fails
|
||||
assert_raises(ValueError, caller, LowLevelCallable(func), 1.0)
|
||||
|
||||
# Test that passing in user_data also fails
|
||||
assert_raises(ValueError, caller, func2, 1.0)
|
||||
|
||||
# Test error message
|
||||
llfunc = LowLevelCallable(func)
|
||||
try:
|
||||
caller(llfunc, 1.0)
|
||||
except ValueError as err:
|
||||
msg = str(err)
|
||||
assert_(llfunc.signature in msg, msg)
|
||||
assert_('double (double, double, int *, void *)' in msg, msg)
|
||||
|
||||
for caller in sorted(CALLERS.keys()):
|
||||
for func in sorted(BAD_FUNCS.keys()):
|
||||
for user_data in sorted(USER_DATAS.keys()):
|
||||
check(caller, func, user_data)
|
||||
|
||||
|
||||
def test_signature_override():
|
||||
caller = _test_ccallback.test_call_simple
|
||||
func = _test_ccallback.test_get_plus1_capsule()
|
||||
|
||||
llcallable = LowLevelCallable(func, signature="bad signature")
|
||||
assert_equal(llcallable.signature, "bad signature")
|
||||
assert_raises(ValueError, caller, llcallable, 3)
|
||||
|
||||
llcallable = LowLevelCallable(func, signature="double (double, int *, void *)")
|
||||
assert_equal(llcallable.signature, "double (double, int *, void *)")
|
||||
assert_equal(caller(llcallable, 3), 4)
|
||||
|
||||
|
||||
def test_threadsafety():
|
||||
def callback(a, caller):
|
||||
if a <= 0:
|
||||
return 1
|
||||
else:
|
||||
res = caller(lambda x: callback(x, caller), a - 1)
|
||||
return 2*res
|
||||
|
||||
def check(caller):
|
||||
caller = CALLERS[caller]
|
||||
|
||||
results = []
|
||||
|
||||
count = 10
|
||||
|
||||
def run():
|
||||
time.sleep(0.01)
|
||||
r = caller(lambda x: callback(x, caller), count)
|
||||
results.append(r)
|
||||
|
||||
threads = [threading.Thread(target=run) for j in range(20)]
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
assert_equal(results, [2.0**count]*len(threads))
|
||||
|
||||
for caller in CALLERS.keys():
|
||||
check(caller)
|
||||
@@ -1,52 +0,0 @@
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
|
||||
MODULES = [
|
||||
"scipy.cluster",
|
||||
"scipy.cluster.vq",
|
||||
"scipy.cluster.hierarchy",
|
||||
"scipy.constants",
|
||||
"scipy.fftpack",
|
||||
"scipy.integrate",
|
||||
"scipy.interpolate",
|
||||
"scipy.io",
|
||||
"scipy.io.arff",
|
||||
"scipy.io.harwell_boeing",
|
||||
"scipy.io.idl",
|
||||
"scipy.io.matlab",
|
||||
"scipy.io.netcdf",
|
||||
"scipy.io.wavfile",
|
||||
"scipy.linalg",
|
||||
"scipy.linalg.blas",
|
||||
"scipy.linalg.cython_blas",
|
||||
"scipy.linalg.lapack",
|
||||
"scipy.linalg.cython_lapack",
|
||||
"scipy.linalg.interpolative",
|
||||
"scipy.misc",
|
||||
"scipy.ndimage",
|
||||
"scipy.odr",
|
||||
"scipy.optimize",
|
||||
"scipy.signal",
|
||||
"scipy.signal.windows",
|
||||
"scipy.sparse",
|
||||
"scipy.sparse.linalg",
|
||||
"scipy.sparse.csgraph",
|
||||
"scipy.spatial",
|
||||
"scipy.spatial.distance",
|
||||
"scipy.special",
|
||||
"scipy.stats",
|
||||
"scipy.stats.distributions",
|
||||
"scipy.stats.mstats",
|
||||
]
|
||||
|
||||
|
||||
def test_modules_importable():
|
||||
# Check that all modules are importable in a new Python
|
||||
# process. This is not necessarily true (esp on Python 2) if there
|
||||
# are import cycles present.
|
||||
for module in MODULES:
|
||||
cmd = 'import {}'.format(module)
|
||||
subprocess.check_call([sys.executable, '-c', cmd])
|
||||
@@ -1,45 +0,0 @@
|
||||
""" Test tmpdirs module """
|
||||
from __future__ import division, print_function, absolute_import
|
||||
|
||||
from os import getcwd
|
||||
from os.path import realpath, abspath, dirname, isfile, join as pjoin, exists
|
||||
|
||||
from scipy._lib._tmpdirs import tempdir, in_tempdir, in_dir
|
||||
|
||||
from numpy.testing import assert_, assert_equal
|
||||
|
||||
MY_PATH = abspath(__file__)
|
||||
MY_DIR = dirname(MY_PATH)
|
||||
|
||||
|
||||
def test_tempdir():
|
||||
with tempdir() as tmpdir:
|
||||
fname = pjoin(tmpdir, 'example_file.txt')
|
||||
with open(fname, 'wt') as fobj:
|
||||
fobj.write('a string\\n')
|
||||
assert_(not exists(tmpdir))
|
||||
|
||||
|
||||
def test_in_tempdir():
|
||||
my_cwd = getcwd()
|
||||
with in_tempdir() as tmpdir:
|
||||
with open('test.txt', 'wt') as f:
|
||||
f.write('some text')
|
||||
assert_(isfile('test.txt'))
|
||||
assert_(isfile(pjoin(tmpdir, 'test.txt')))
|
||||
assert_(not exists(tmpdir))
|
||||
assert_equal(getcwd(), my_cwd)
|
||||
|
||||
|
||||
def test_given_directory():
|
||||
# Test InGivenDirectory
|
||||
cwd = getcwd()
|
||||
with in_dir() as tmpdir:
|
||||
assert_equal(tmpdir, abspath(cwd))
|
||||
assert_equal(tmpdir, abspath(getcwd()))
|
||||
with in_dir(MY_DIR) as tmpdir:
|
||||
assert_equal(tmpdir, MY_DIR)
|
||||
assert_equal(realpath(MY_DIR), realpath(abspath(getcwd())))
|
||||
# We were deleting the given directory! Check not so now.
|
||||
assert_(isfile(MY_PATH))
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
"""
|
||||
Tests which scan for certain occurrences in the code, they may not find
|
||||
all of these occurrences but should catch almost all. This file was adapted
|
||||
from numpy.
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import scipy
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
if sys.version_info >= (3, 4):
|
||||
from pathlib import Path
|
||||
import ast
|
||||
import tokenize
|
||||
|
||||
class ParseCall(ast.NodeVisitor):
|
||||
def __init__(self):
|
||||
self.ls = []
|
||||
|
||||
def visit_Attribute(self, node):
|
||||
ast.NodeVisitor.generic_visit(self, node)
|
||||
self.ls.append(node.attr)
|
||||
|
||||
def visit_Name(self, node):
|
||||
self.ls.append(node.id)
|
||||
|
||||
class FindFuncs(ast.NodeVisitor):
|
||||
def __init__(self, filename):
|
||||
super().__init__()
|
||||
self.__filename = filename
|
||||
self.bad_filters = []
|
||||
self.bad_stacklevels = []
|
||||
|
||||
def visit_Call(self, node):
|
||||
p = ParseCall()
|
||||
p.visit(node.func)
|
||||
ast.NodeVisitor.generic_visit(self, node)
|
||||
|
||||
if p.ls[-1] == 'simplefilter' or p.ls[-1] == 'filterwarnings':
|
||||
if node.args[0].s == "ignore":
|
||||
self.bad_filters.append(
|
||||
"{}:{}".format(self.__filename, node.lineno))
|
||||
|
||||
if p.ls[-1] == 'warn' and (
|
||||
len(p.ls) == 1 or p.ls[-2] == 'warnings'):
|
||||
|
||||
if self.__filename == "_lib/tests/test_warnings.py":
|
||||
# This file
|
||||
return
|
||||
|
||||
# See if stacklevel exists:
|
||||
if len(node.args) == 3:
|
||||
return
|
||||
args = {kw.arg for kw in node.keywords}
|
||||
if "stacklevel" not in args:
|
||||
self.bad_stacklevels.append(
|
||||
"{}:{}".format(self.__filename, node.lineno))
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def warning_calls():
|
||||
# combined "ignore" and stacklevel error
|
||||
base = Path(scipy.__file__).parent
|
||||
|
||||
bad_filters = []
|
||||
bad_stacklevels = []
|
||||
|
||||
for path in base.rglob("*.py"):
|
||||
# use tokenize to auto-detect encoding on systems where no
|
||||
# default encoding is defined (e.g. LANG='C')
|
||||
with tokenize.open(str(path)) as file:
|
||||
tree = ast.parse(file.read(), filename=str(path))
|
||||
finder = FindFuncs(path.relative_to(base))
|
||||
finder.visit(tree)
|
||||
bad_filters.extend(finder.bad_filters)
|
||||
bad_stacklevels.extend(finder.bad_stacklevels)
|
||||
|
||||
return bad_filters, bad_stacklevels
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.skipif(sys.version_info < (3, 4), reason="needs Python >= 3.4")
|
||||
def test_warning_calls_filters(warning_calls):
|
||||
bad_filters, bad_stacklevels = warning_calls
|
||||
|
||||
# There is still one simplefilter occurrence in optimize.py that could be removed.
|
||||
bad_filters = [item for item in bad_filters
|
||||
if 'optimize.py' not in item]
|
||||
# The filterwarnings call in sparse/__init__.py is needed.
|
||||
bad_filters = [item for item in bad_filters
|
||||
if os.path.join('sparse', '__init__.py') not in item]
|
||||
|
||||
if bad_filters:
|
||||
raise AssertionError(
|
||||
"warning ignore filter should not be used, instead, use\n"
|
||||
"scipy._lib._numpy_compat.suppress_warnings (in tests only);\n"
|
||||
"found in:\n {}".format(
|
||||
"\n ".join(bad_filters)))
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.skipif(sys.version_info < (3, 4), reason="needs Python >= 3.4")
|
||||
@pytest.mark.xfail(reason="stacklevels currently missing")
|
||||
def test_warning_calls_stacklevels(warning_calls):
|
||||
bad_filters, bad_stacklevels = warning_calls
|
||||
|
||||
msg = ""
|
||||
|
||||
if bad_filters:
|
||||
msg += ("warning ignore filter should not be used, instead, use\n"
|
||||
"scipy._lib._numpy_compat.suppress_warnings (in tests only);\n"
|
||||
"found in:\n {}".format("\n ".join(bad_filters)))
|
||||
msg += "\n\n"
|
||||
|
||||
if bad_stacklevels:
|
||||
msg += "warnings should have an appropriate stacklevel:\n {}".format(
|
||||
"\n ".join(bad_stacklevels))
|
||||
|
||||
if msg:
|
||||
raise AssertionError(msg)
|
||||
Reference in New Issue
Block a user