diff options
Diffstat (limited to 'silx/utils')
-rw-r--r-- | silx/utils/__init__.py | 28 | ||||
-rw-r--r-- | silx/utils/_have_openmp.pxi | 49 | ||||
-rw-r--r-- | silx/utils/array_like.py | 593 | ||||
-rw-r--r-- | silx/utils/debug.py | 102 | ||||
-rw-r--r-- | silx/utils/deprecation.py | 119 | ||||
-rw-r--r-- | silx/utils/exceptions.py | 33 | ||||
-rw-r--r-- | silx/utils/html.py | 60 | ||||
-rw-r--r-- | silx/utils/include/silx_store_openmp.h | 10 | ||||
-rw-r--r-- | silx/utils/launcher.py | 295 | ||||
-rw-r--r-- | silx/utils/number.py | 143 | ||||
-rw-r--r-- | silx/utils/property.py | 52 | ||||
-rw-r--r-- | silx/utils/proxy.py | 204 | ||||
-rw-r--r-- | silx/utils/setup.py | 43 | ||||
-rw-r--r-- | silx/utils/test/__init__.py | 51 | ||||
-rw-r--r-- | silx/utils/test/test_array_like.py | 453 | ||||
-rw-r--r-- | silx/utils/test/test_debug.py | 99 | ||||
-rw-r--r-- | silx/utils/test/test_deprecation.py | 107 | ||||
-rw-r--r-- | silx/utils/test/test_html.py | 61 | ||||
-rw-r--r-- | silx/utils/test/test_launcher.py | 204 | ||||
-rw-r--r-- | silx/utils/test/test_launcher_command.py | 47 | ||||
-rw-r--r-- | silx/utils/test/test_number.py | 181 | ||||
-rw-r--r-- | silx/utils/test/test_proxy.py | 295 | ||||
-rw-r--r-- | silx/utils/test/test_weakref.py | 330 | ||||
-rw-r--r-- | silx/utils/testutils.py | 281 | ||||
-rw-r--r-- | silx/utils/weakref.py | 361 |
25 files changed, 0 insertions, 4201 deletions
diff --git a/silx/utils/__init__.py b/silx/utils/__init__.py deleted file mode 100644 index f803a5f..0000000 --- a/silx/utils/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2018 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""This package contains a set of miscellaneous convenient features. - -See silx documentation: http://www.silx.org/doc/silx/latest/ -""" diff --git a/silx/utils/_have_openmp.pxi b/silx/utils/_have_openmp.pxi deleted file mode 100644 index 40a2857..0000000 --- a/silx/utils/_have_openmp.pxi +++ /dev/null @@ -1,49 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2018 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ - -""" -Store in a Cython module if it was compiled with OpenMP - -You have to patch the setup module like that: - -.. code-block:: python - - silx_include = os.path.join(top_path, "silx", "utils", "include") - config.add_extension('my_extension', - include_dirs=[silx_include], - ...) - -Then you can include it like that in your Cython module: - -.. code-block:: python - - include "../../utils/_have_openmp.pxi" - -""" - - -cdef extern from "silx_store_openmp.h": - int COMPILED_WITH_OPENMP -_COMPILED_WITH_OPENMP = COMPILED_WITH_OPENMP diff --git a/silx/utils/array_like.py b/silx/utils/array_like.py deleted file mode 100644 index 11f531d..0000000 --- a/silx/utils/array_like.py +++ /dev/null @@ -1,593 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2018 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Functions and classes for array-like objects, implementing common numpy -array features for datasets or nested sequences, while trying to avoid copying -data. - -Classes: - - - :class:`DatasetView`: Similar to a numpy view, to access - a h5py dataset as if it was transposed, without casting it into a - numpy array (this lets h5py handle reading the data from the - file into memory, as needed). - - :class:`ListOfImages`: Similar to a numpy view, to access - a list of 2D numpy arrays as if it was a 3D array (possibly transposed), - without casting it into a numpy array. - -Functions: - - - :func:`is_array` - - :func:`is_list_of_arrays` - - :func:`is_nested_sequence` - - :func:`get_shape` - - :func:`get_dtype` - - :func:`get_concatenated_dtype` - -""" - -from __future__ import absolute_import, print_function, division -import numpy -import sys -from silx.third_party import six - -__authors__ = ["P. Knobel"] -__license__ = "MIT" -__date__ = "26/04/2017" - - -def is_array(obj): - """Return True if object implements necessary attributes to be - considered similar to a numpy array. - - Attributes needed are "shape", "dtype", "__getitem__" - and "__array__". - - :param obj: Array-like object (numpy array, h5py dataset...) - :return: boolean - """ - # add more required attribute if necessary - for attr in ("shape", "dtype", "__array__", "__getitem__"): - if not hasattr(obj, attr): - return False - return True - - -def is_list_of_arrays(obj): - """Return True if object is a sequence of numpy arrays, - e.g. a list of images as 2D arrays. - - :param obj: list of arrays - :return: boolean""" - # object must not be a numpy array - if is_array(obj): - return False - - # object must have a __len__ method - if not hasattr(obj, "__len__"): - return False - - # all elements in sequence must be arrays - for arr in obj: - if not is_array(arr): - return False - - return True - - -def is_nested_sequence(obj): - """Return True if object is a nested sequence. - - A simple 1D sequence is considered to be a nested sequence. - - Numpy arrays and h5py datasets are not considered to be nested sequences. - - To test if an object is a nested sequence in a more general sense, - including arrays and datasets, use:: - - is_nested_sequence(obj) or is_array(obj) - - :param obj: nested sequence (numpy array, h5py dataset...) - :return: boolean""" - # object must not be a numpy array - if is_array(obj): - return False - - if not hasattr(obj, "__len__"): - return False - - # obj must not be a list of (lists of) numpy arrays - subsequence = obj - while hasattr(subsequence, "__len__"): - if is_array(subsequence): - return False - # strings cause infinite loops - if isinstance(subsequence, six.string_types + (six.binary_type, )): - return True - subsequence = subsequence[0] - - # object has __len__ and is not an array - return True - - -def get_shape(array_like): - """Return shape of an array like object. - - In case the object is a nested sequence but not an array or dataset - (list of lists, tuples...), the size of each dimension is assumed to be - uniform, and is deduced from the length of the first sequence. - - :param array_like: Array like object: numpy array, hdf5 dataset, - multi-dimensional sequence - :return: Shape of array, as a tuple of integers - """ - if hasattr(array_like, "shape"): - return array_like.shape - - shape = [] - subsequence = array_like - while hasattr(subsequence, "__len__"): - shape.append(len(subsequence)) - # strings cause infinite loops - if isinstance(subsequence, six.string_types + (six.binary_type, )): - break - subsequence = subsequence[0] - - return tuple(shape) - - -def get_dtype(array_like): - """Return dtype of an array like object. - - In the case of a nested sequence, the type of the first value - is inspected. - - :param array_like: Array like object: numpy array, hdf5 dataset, - multi-dimensional nested sequence - :return: numpy dtype of object - """ - if hasattr(array_like, "dtype"): - return array_like.dtype - - subsequence = array_like - while hasattr(subsequence, "__len__"): - # strings cause infinite loops - if isinstance(subsequence, six.string_types + (six.binary_type, )): - break - subsequence = subsequence[0] - - return numpy.dtype(type(subsequence)) - - -def get_concatenated_dtype(arrays): - """Return dtype of array resulting of concatenation - of a list of arrays (without actually concatenating - them). - - :param arrays: list of numpy arrays - :return: resulting dtype after concatenating arrays - """ - dtypes = {a.dtype for a in arrays} - dummy = [] - for dt in dtypes: - dummy.append(numpy.zeros((1, 1), dtype=dt)) - return numpy.array(dummy).dtype - - -class ListOfImages(object): - """This class provides a way to access values and slices in a stack of - images stored as a list of 2D numpy arrays, without creating a 3D numpy - array first. - - A transposition can be specified, as a 3-tuple of dimensions in the wanted - order. For example, to transpose from ``xyz`` ``(0, 1, 2)`` into ``yzx``, - the transposition tuple is ``(1, 2, 0)`` - - All the 2D arrays in the list must have the same shape. - - The global dtype of the stack of images is the one that would be obtained - by casting the list of 2D arrays into a 3D numpy array. - - :param images: list of 2D numpy arrays, or :class:`ListOfImages` object - :param transposition: Tuple of dimension numbers in the wanted order - """ - def __init__(self, images, transposition=None): - """ - - """ - super(ListOfImages, self).__init__() - - # if images is a ListOfImages instance, get the underlying data - # as a list of 2D arrays - if isinstance(images, ListOfImages): - images = images.images - - # test stack of images is as expected - assert is_list_of_arrays(images), \ - "Image stack must be a list of arrays" - image0_shape = images[0].shape - for image in images: - assert image.ndim == 2, \ - "Images must be 2D numpy arrays" - assert image.shape == image0_shape, \ - "All images must have the same shape" - - self.images = images - """List of images""" - - self.shape = (len(images), ) + image0_shape - """Tuple of array dimensions""" - self.dtype = get_concatenated_dtype(images) - """Data-type of the global array""" - self.ndim = 3 - """Number of array dimensions""" - - self.size = len(images) * image0_shape[0] * image0_shape[1] - """Number of elements in the array.""" - - self.transposition = list(range(self.ndim)) - """List of dimension indices, in an order depending on the - specified transposition. By default this is simply - [0, ..., self.ndim], but it can be changed by specifying a different - ``transposition`` parameter at initialization. - - Use :meth:`transpose`, to create a new :class:`ListOfImages` - with a different :attr:`transposition`. - """ - - if transposition is not None: - assert len(transposition) == self.ndim - assert set(transposition) == set(list(range(self.ndim))), \ - "Transposition must be a sequence containing all dimensions" - self.transposition = transposition - self.__sort_shape() - - def __sort_shape(self): - """Sort shape in the order defined in :attr:`transposition` - """ - new_shape = tuple(self.shape[dim] for dim in self.transposition) - self.shape = new_shape - - def __sort_indices(self, indices): - """Return array indices sorted in the order needed - to access data in the original non-transposed images. - - :param indices: Tuple of ndim indices, in the order needed - to access the transposed view - :return: Sorted tuple of indices, to access original data - """ - assert len(indices) == self.ndim - sorted_indices = tuple(idx for (_, idx) in - sorted(zip(self.transposition, indices))) - return sorted_indices - - def __array__(self, dtype=None): - """Cast the images into a numpy array, and return it. - - If a transposition has been done on this images, return - a transposed view of a numpy array.""" - return numpy.transpose(numpy.array(self.images, dtype=dtype), - self.transposition) - - def __len__(self): - return self.shape[0] - - def transpose(self, transposition=None): - """Return a re-ordered (dimensions permutated) - :class:`ListOfImages`. - - The returned object refers to - the same images but with a different :attr:`transposition`. - - :param List[int] transposition: List/tuple of dimension numbers in the - wanted order. - If ``None`` (default), reverse the dimensions. - :return: new :class:`ListOfImages` object - """ - # by default, reverse the dimensions - if transposition is None: - transposition = list(reversed(self.transposition)) - - # If this ListOfImages is already transposed, sort new transposition - # relative to old transposition - elif list(self.transposition) != list(range(self.ndim)): - transposition = [self.transposition[i] for i in transposition] - - return ListOfImages(self.images, - transposition) - - @property - def T(self): - """ - Same as self.transpose() - - :return: DatasetView with dimensions reversed.""" - return self.transpose() - - def __getitem__(self, item): - """Handle a subset of numpy indexing with regards to the dimension - order as specified in :attr:`transposition` - - Following features are **not supported**: - - - fancy indexing using numpy arrays - - using ellipsis objects - - :param item: Index - :return: value or slice as a numpy array - """ - # 1-D slicing -> n-D slicing (n=1) - if not hasattr(item, "__len__"): - # first dimension index is given - item = [item] - # following dimensions are indexed with : (all elements) - item += [slice(None) for _i in range(self.ndim - 1)] - - # n-dimensional slicing - if len(item) != self.ndim: - raise IndexError( - "N-dim slicing requires a tuple of N indices/slices. " + - "Needed dimensions: %d" % self.ndim) - - # get list of indices sorted in the original images order - sorted_indices = self.__sort_indices(item) - list_idx, array_idx = sorted_indices[0], sorted_indices[1:] - - images_selection = self.images[list_idx] - - # now we must transpose the output data - output_dimensions = [] - frozen_dimensions = [] - for i, idx in enumerate(item): - # slices and sequences - if not isinstance(idx, int): - output_dimensions.append(self.transposition[i]) - # regular integer index - else: - # whenever a dimension is fixed (indexed by an integer) - # the number of output dimension is reduced - frozen_dimensions.append(self.transposition[i]) - - # decrement output dimensions that are above frozen dimensions - for frozen_dim in reversed(sorted(frozen_dimensions)): - for i, out_dim in enumerate(output_dimensions): - if out_dim > frozen_dim: - output_dimensions[i] -= 1 - - assert (len(output_dimensions) + len(frozen_dimensions)) == self.ndim - assert set(output_dimensions) == set(range(len(output_dimensions))) - - # single list elements selected - if isinstance(images_selection, numpy.ndarray): - return numpy.transpose(images_selection[array_idx], - axes=output_dimensions) - # muliple list elements selected - else: - # apply selection first - output_stack = [] - for img in images_selection: - output_stack.append(img[array_idx]) - # then cast into a numpy array, and transpose - return numpy.transpose(numpy.array(output_stack), - axes=output_dimensions) - - def min(self): - """ - :return: Global minimum value - """ - min_value = self.images[0].min() - if len(self.images) > 1: - for img in self.images[1:]: - min_value = min(min_value, img.min()) - return min_value - - def max(self): - """ - :return: Global maximum value - """ - max_value = self.images[0].max() - if len(self.images) > 1: - for img in self.images[1:]: - max_value = max(max_value, img.max()) - return max_value - - -class DatasetView(object): - """This class provides a way to transpose a dataset without - casting it into a numpy array. This way, the dataset in a file need not - necessarily be integrally read into memory to view it in a different - transposition. - - .. note:: - The performances depend a lot on the way the dataset was written - to file. Depending on the chunking strategy, reading a complete 2D slice - in an unfavorable direction may still require the entire dataset to - be read from disk. - - :param dataset: h5py dataset - :param transposition: List of dimensions sorted in the order of - transposition (relative to the original h5py dataset) - """ - def __init__(self, dataset, transposition=None): - """ - - """ - super(DatasetView, self).__init__() - self.dataset = dataset - """original dataset""" - - self.shape = dataset.shape - """Tuple of array dimensions""" - self.dtype = dataset.dtype - """Data-type of the array’s element""" - self.ndim = len(dataset.shape) - """Number of array dimensions""" - - size = 0 - if self.ndim: - size = 1 - for dimsize in self.shape: - size *= dimsize - self.size = size - """Number of elements in the array.""" - - self.transposition = list(range(self.ndim)) - """List of dimension indices, in an order depending on the - specified transposition. By default this is simply - [0, ..., self.ndim], but it can be changed by specifying a different - `transposition` parameter at initialization. - - Use :meth:`transpose`, to create a new :class:`DatasetView` - with a different :attr:`transposition`. - """ - - if transposition is not None: - assert len(transposition) == self.ndim - assert set(transposition) == set(list(range(self.ndim))), \ - "Transposition must be a list containing all dimensions" - self.transposition = transposition - self.__sort_shape() - - def __sort_shape(self): - """Sort shape in the order defined in :attr:`transposition` - """ - new_shape = tuple(self.shape[dim] for dim in self.transposition) - self.shape = new_shape - - def __sort_indices(self, indices): - """Return array indices sorted in the order needed - to access data in the original non-transposed dataset. - - :param indices: Tuple of ndim indices, in the order needed - to access the view - :return: Sorted tuple of indices, to access original data - """ - assert len(indices) == self.ndim - sorted_indices = tuple(idx for (_, idx) in - sorted(zip(self.transposition, indices))) - return sorted_indices - - def __getitem__(self, item): - """Handle fancy indexing with regards to the dimension order as - specified in :attr:`transposition` - - The supported fancy-indexing syntax is explained at - http://docs.h5py.org/en/latest/high/dataset.html#fancy-indexing. - - Additional restrictions exist if the data has been transposed: - - - numpy boolean array indexing is not supported - - ellipsis objects are not supported - - :param item: Index, possibly fancy index (must be supported by h5py) - :return: Sliced numpy array or numpy scalar - """ - # no transposition, let the original dataset handle indexing - if self.transposition == list(range(self.ndim)): - return self.dataset[item] - - # 1-D slicing: create a list of indices to switch to n-D slicing - if not hasattr(item, "__len__"): - # first dimension index (list index) is given - item = [item] - # following dimensions are indexed with slices representing all elements - item += [slice(None) for _i in range(self.ndim - 1)] - - # n-dimensional slicing - if len(item) != self.ndim: - raise IndexError( - "N-dim slicing requires a tuple of N indices/slices. " + - "Needed dimensions: %d" % self.ndim) - - # get list of indices sorted in the original dataset order - sorted_indices = self.__sort_indices(item) - - output_data_not_transposed = self.dataset[sorted_indices] - - # now we must transpose the output data - output_dimensions = [] - frozen_dimensions = [] - for i, idx in enumerate(item): - # slices and sequences - if not isinstance(idx, int): - output_dimensions.append(self.transposition[i]) - # regular integer index - else: - # whenever a dimension is fixed (indexed by an integer) - # the number of output dimension is reduced - frozen_dimensions.append(self.transposition[i]) - - # decrement output dimensions that are above frozen dimensions - for frozen_dim in reversed(sorted(frozen_dimensions)): - for i, out_dim in enumerate(output_dimensions): - if out_dim > frozen_dim: - output_dimensions[i] -= 1 - - assert (len(output_dimensions) + len(frozen_dimensions)) == self.ndim - assert set(output_dimensions) == set(range(len(output_dimensions))) - - return numpy.transpose(output_data_not_transposed, - axes=output_dimensions) - - def __array__(self, dtype=None): - """Cast the dataset into a numpy array, and return it. - - If a transposition has been done on this dataset, return - a transposed view of a numpy array.""" - return numpy.transpose(numpy.array(self.dataset, dtype=dtype), - self.transposition) - - def __len__(self): - return self.shape[0] - - def transpose(self, transposition=None): - """Return a re-ordered (dimensions permutated) - :class:`DatasetView`. - - The returned object refers to - the same dataset but with a different :attr:`transposition`. - - :param List[int] transposition: List of dimension numbers in the wanted order. - If ``None`` (default), reverse the dimensions. - :return: Transposed DatasetView - """ - # by default, reverse the dimensions - if transposition is None: - transposition = list(reversed(self.transposition)) - - # If this DatasetView is already transposed, sort new transposition - # relative to old transposition - elif list(self.transposition) != list(range(self.ndim)): - transposition = [self.transposition[i] for i in transposition] - - return DatasetView(self.dataset, - transposition) - - @property - def T(self): - """ - Same as self.transpose() - - :return: DatasetView with dimensions reversed.""" - return self.transpose() diff --git a/silx/utils/debug.py b/silx/utils/debug.py deleted file mode 100644 index d6ca328..0000000 --- a/silx/utils/debug.py +++ /dev/null @@ -1,102 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2018 European Synchrotron Radiation Facility -# -# 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 inspect -import types -import logging -from silx.third_party import six - - -debug_logger = logging.getLogger("silx.DEBUG") - -_indent = 0 - - -def log_method(func, class_name=None): - """Decorator to inject a warning log before an after any function/method. - - .. code-block:: python - - @log_method - def foo(): - return None - - :param callable func: The function to patch - :param str class_name: In case a method, provide the class name - """ - def wrapper(*args, **kwargs): - global _indent - - indent = " " * _indent - func_name = func.func_name if six.PY2 else func.__name__ - if class_name is not None: - name = "%s.%s" % (class_name, func_name) - else: - name = "%s" % (func_name) - - debug_logger.warning("%s%s" % (indent, name)) - _indent += 1 - result = func(*args, **kwargs) - _indent -= 1 - debug_logger.warning("%sreturn (%s)" % (indent, name)) - return result - return wrapper - - -def log_all_methods(base_class): - """Decorator to inject a warning log before an after any method provided by - a class. - - .. code-block:: python - - @log_all_methods - class Foo(object): - - def a(self): - return None - - def b(self): - return self.a() - - Here is the output when calling the `b` method. - - .. code-block:: - - WARNING:silx.DEBUG:_Foobar.b - WARNING:silx.DEBUG: _Foobar.a - WARNING:silx.DEBUG: return (_Foobar.a) - WARNING:silx.DEBUG:return (_Foobar.b) - - :param class base_class: The class to patch - """ - methodTypes = (types.MethodType, types.FunctionType, types.BuiltinFunctionType, types.BuiltinMethodType) - for name, func in inspect.getmembers(base_class): - if isinstance(func, methodTypes): - if func.__name__ not in ["__subclasshook__", "__new__"]: - # patching __new__ in Python2 break the object, then we skip it - setattr(base_class, name, log_method(func, base_class.__name__)) - - return base_class diff --git a/silx/utils/deprecation.py b/silx/utils/deprecation.py deleted file mode 100644 index f9ba017..0000000 --- a/silx/utils/deprecation.py +++ /dev/null @@ -1,119 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Bunch of useful decorators""" - -from __future__ import absolute_import, print_function, division - -__authors__ = ["Jerome Kieffer", "H. Payno", "P. Knobel"] -__license__ = "MIT" -__date__ = "26/02/2018" - -import sys -import logging -import functools -import traceback - -depreclog = logging.getLogger("silx.DEPRECATION") - -deprecache = set([]) - - -def deprecated(func=None, reason=None, replacement=None, since_version=None, only_once=True, skip_backtrace_count=1): - """ - Decorator that deprecates the use of a function - - :param str reason: Reason for deprecating this function - (e.g. "feature no longer provided", - :param str replacement: Name of replacement function (if the reason for - deprecating was to rename the function) - :param str since_version: First *silx* version for which the function was - deprecated (e.g. "0.5.0"). - :param bool only_once: If true, the deprecation warning will only be - generated one time. Default is true. - :param int skip_backtrace_count: Amount of last backtrace to ignore when - logging the backtrace - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - name = func.func_name if sys.version_info[0] < 3 else func.__name__ - - deprecated_warning(type_='Function', - name=name, - reason=reason, - replacement=replacement, - since_version=since_version, - only_once=only_once, - skip_backtrace_count=skip_backtrace_count) - return func(*args, **kwargs) - return wrapper - if func is not None: - return decorator(func) - return decorator - - -def deprecated_warning(type_, name, reason=None, replacement=None, - since_version=None, only_once=True, - skip_backtrace_count=0): - """ - Function to log a deprecation warning - - :param str type_: Nature of the object to be deprecated: - "Module", "Function", "Class" ... - :param name: Object name. - :param str reason: Reason for deprecating this function - (e.g. "feature no longer provided", - :param str replacement: Name of replacement function (if the reason for - deprecating was to rename the function) - :param str since_version: First *silx* version for which the function was - deprecated (e.g. "0.5.0"). - :param bool only_once: If true, the deprecation warning will only be - generated one time for each different call locations. Default is true. - :param int skip_backtrace_count: Amount of last backtrace to ignore when - logging the backtrace - """ - if not depreclog.isEnabledFor(logging.WARNING): - # Avoid computation when it is not logged - return - - msg = "%s %s is deprecated" - if since_version is not None: - msg += " since silx version %s" % since_version - msg += "." - if reason is not None: - msg += " Reason: %s." % reason - if replacement is not None: - msg += " Use '%s' instead." % replacement - msg += "\n%s" - limit = 2 + skip_backtrace_count - backtrace = "".join(traceback.format_stack(limit=limit)[0]) - backtrace = backtrace.rstrip() - if only_once: - data = (msg, type_, name, backtrace) - if data in deprecache: - return - else: - deprecache.add(data) - depreclog.warning(msg, type_, name, backtrace) diff --git a/silx/utils/exceptions.py b/silx/utils/exceptions.py deleted file mode 100644 index addba89..0000000 --- a/silx/utils/exceptions.py +++ /dev/null @@ -1,33 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Bunch of useful exceptions""" - -__authors__ = ["H. Payno"] -__license__ = "MIT" -__date__ = "17/01/2018" - - -class NotEditableError(Exception): - """Exception emitted when try to access to a non editable attribute""" diff --git a/silx/utils/html.py b/silx/utils/html.py deleted file mode 100644 index aab25f2..0000000 --- a/silx/utils/html.py +++ /dev/null @@ -1,60 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Utils function relative to HTML -""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "19/09/2016" - - -def escape(string, quote=True): - """Returns a string where HTML metacharacters are properly escaped. - - Compatibility layer to avoid incompatibilities between Python versions, - Qt versions and Qt bindings. - - >>> import silx.utils.html - >>> silx.utils.html.escape("<html>") - >>> "<html>" - - .. note:: Since Python 3.3 you can use the `html` module. For previous - version, it is provided by `sgi` module. - .. note:: Qt4 provides it with `Qt.escape` while Qt5 provide it with - `QString.toHtmlEscaped`. But `QString` is not exposed by `PyQt` or - `PySide`. - - :param str string: Human readable string. - :param bool quote: Escape quote and double quotes (default behaviour). - :returns: Valid HTML syntax to display the input string. - :rtype: str - """ - string = string.replace("&", "&") # must be done first - string = string.replace("<", "<") - string = string.replace(">", ">") - if quote: - string = string.replace("'", "'") - string = string.replace("\"", """) - return string diff --git a/silx/utils/include/silx_store_openmp.h b/silx/utils/include/silx_store_openmp.h deleted file mode 100644 index f04f630..0000000 --- a/silx/utils/include/silx_store_openmp.h +++ /dev/null @@ -1,10 +0,0 @@ - - - -/** Flag the C module with a variable to know if it was compiled with OpenMP - */ -#ifdef _OPENMP -static const int COMPILED_WITH_OPENMP = 1; -#else -static const int COMPILED_WITH_OPENMP = 0; -#endif diff --git a/silx/utils/launcher.py b/silx/utils/launcher.py deleted file mode 100644 index c46256a..0000000 --- a/silx/utils/launcher.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-2017 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""This module define silx application available throug the silx launcher. -""" - -__authors__ = ["P. Knobel", "V. Valls"] -__license__ = "MIT" -__date__ = "03/04/2017" - - -import sys -import importlib -import contextlib -import argparse -import logging - - -_logger = logging.getLogger(__name__) - - -class LauncherCommand(object): - """Description of a command""" - - def __init__(self, name, description=None, module_name=None, function=None): - """ - Constructor - - :param str name: Name of the command - :param str description: Description of the command - :param str module_name: Module name to execute - :param callable function: Python function to execute - """ - self.name = name - self.module_name = module_name - if description is None: - description = "A command" - self.description = description - self.function = function - - def get_module(self): - """Returns the python module to execute. If any. - - :rtype: module - """ - try: - module = importlib.import_module(self.module_name) - return module - except ImportError: - msg = "Error while reaching module '%s'" - _logger.error(msg, self.module_name, exc_info=True) - return None - - def get_function(self): - """Returns the main function to execute. - - :rtype: callable - """ - if self.function is not None: - return self.function - else: - module = self.get_module() - if module is None: - _logger.error("Impossible to load module name '%s'" % self.module_name) - return None - - # reach the 'main' function - if not hasattr(module, "main"): - raise TypeError("Module expect to have a 'main' function") - else: - main = getattr(module, "main") - return main - - @contextlib.contextmanager - def get_env(self, argv): - """Fix the environement before and after executing the command. - - :param list argv: The list of arguments (the first one is the name of - the application and command) - :rtype: int - """ - - # fix the context - old_argv = sys.argv - sys.argv = argv - - try: - yield - finally: - # clean up the context - sys.argv = old_argv - - def execute(self, argv): - """Execute the command. - - :param list[str] argv: The list of arguments (the first one is the - name of the application and command) - :rtype: int - :returns: The execution status - """ - with self.get_env(argv): - func = self.get_function() - if func is None: - _logger.error("Imposible to execute the command '%s'" % self.name) - return -1 - try: - status = func(argv) - except SystemExit as e: - # ArgumentParser likes to finish with a sys.exit - status = e.args[0] - return status - - -class Launcher(object): - """ - Manage launch of module. - - Provides an API to describe available commands and feature to display help - and execute the commands. - """ - - def __init__(self, - prog=None, - usage=None, - description=None, - epilog=None, - version=None): - """ - :param str prog: Name of the program. If it is not defined it uses the - first argument of `sys.argv` - :param str usage: Custom the string explaining the usage. Else it is - autogenerated. - :param str description: Description of the application displayed after the - usage. - :param str epilog: Custom the string displayed at the end of the help. - :param str version: Define the version of the application. - """ - if prog is None: - prog = sys.argv[0] - self.prog = prog - self.usage = usage - self.description = description - self.epilog = epilog - self.version = version - self._commands = {} - - help_command = LauncherCommand( - "help", - description="Show help of the following command", - function=self.execute_help) - self.add_command(command=help_command) - - def add_command(self, name=None, module_name=None, description=None, command=None): - """ - Add a command to the launcher. - - See also `LauncherCommand`. - - :param str name: Name of the command - :param str module_name: Module to execute - :param str description: Description of the command - :param LauncherCommand command: A `LauncherCommand` - """ - if command is not None: - assert(name is None and module_name is None and description is None) - else: - command = LauncherCommand( - name=name, - description=description, - module_name=module_name) - self._commands[command.name] = command - - def print_help(self): - """Print the help to stdout. - """ - usage = self.usage - if usage is None: - usage = "usage: {0.prog} [--version|--help] <command> [<args>]" - description = self.description - epilog = self.epilog - if epilog is None: - epilog = "See '{0.prog} help <command>' to read about a specific subcommand" - - print(usage.format(self)) - print("") - if description is not None: - print(description) - print("") - print("The {0.prog} commands are:".format(self)) - commands = sorted(self._commands.keys()) - for command in commands: - command = self._commands[command] - print(" {:10s} {:s}".format(command.name, command.description)) - print("") - print(epilog.format(self)) - - def execute_help(self, argv): - """Execute the help command. - - :param list[str] argv: The list of arguments (the first one is the - name of the application with the help command) - :rtype: int - :returns: The execution status - """ - description = "Display help information about %s" % self.prog - parser = argparse.ArgumentParser(description=description) - parser.add_argument( - 'command', - default=None, - nargs=argparse.OPTIONAL, - help='Command in which aving help') - - try: - options = parser.parse_args(argv[1:]) - except SystemExit as e: - # ArgumentParser likes to finish with a sys.exit - return e.args[0] - - command_name = options.command - if command_name is None: - self.print_help() - return 0 - - if command_name not in self._commands: - print("Unknown command: %s", command_name) - self.print_help() - return -1 - - command = self._commands[command_name] - prog = "%s %s" % (self.prog, command.name) - return command.execute([prog, "--help"]) - - def execute(self, argv=None): - """Execute the launcher. - - :param list[str] argv: The list of arguments (the first one is the - name of the application) - :rtype: int - :returns: The execution status - """ - if argv is None: - argv = sys.argv - - if len(argv) <= 1: - self.print_help() - return 0 - - command_name = argv[1] - - if command_name == "--version": - print("%s version %s" % (self.prog, str(self.version))) - return 0 - - if command_name in ["--help", "-h"]: - # Special help command - if len(argv) == 2: - self.print_help() - return 0 - else: - command_name = argv[2] - command_argv = argv[2:] + ["--help"] - command_argv[0] = "%s %s" % (self.prog, command_argv[0]) - else: - command_argv = argv[1:] - command_argv[0] = "%s %s" % (self.prog, command_argv[0]) - - if command_name not in self._commands: - print("Unknown command: %s" % command_name) - self.print_help() - return -1 - - command = self._commands[command_name] - return command.execute(command_argv) diff --git a/silx/utils/number.py b/silx/utils/number.py deleted file mode 100644 index 92e98fe..0000000 --- a/silx/utils/number.py +++ /dev/null @@ -1,143 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2018 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Utilitary functions dealing with numbers. -""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "05/06/2018" - -import numpy -import re -import logging - - -_logger = logging.getLogger(__name__) - - -_biggest_float = None - -if hasattr(numpy, "longdouble"): - finfo = numpy.finfo(numpy.longdouble) - # The bit for sign is missing here - bits = finfo.nexp + finfo.nmant - if bits > 64: - _biggest_float = numpy.longdouble - # From bigger to smaller - _float_types = (numpy.longdouble, numpy.float64, numpy.float32, numpy.float16) -if _biggest_float is None: - _biggest_float = numpy.float64 - # From bigger to smaller - _float_types = (numpy.float64, numpy.float32, numpy.float16) - - -_parse_numeric_value = re.compile(r"^\s*[-+]?0*(\d+?)?(?:\.(\d+))?(?:[eE]([-+]?\d+))?\s*$") - - -def is_longdouble_64bits(): - """Returns true if the system uses floating-point 64bits for it's - long double type. - - .. note:: Comparing `numpy.longdouble` and `numpy.float64` on Windows is not - possible (or at least not will all the numpy version) - """ - return _biggest_float == numpy.float64 - - -def min_numerical_convertible_type(string, check_accuracy=True): - """ - Parse the string and return the smallest numerical type to use for a safe - conversion. - - :param str string: Representation of a float/integer with text - :param bool check_accuracy: If true, a warning is pushed on the logger - in case there is a loss of accuracy. - :raise ValueError: When the string is not a numerical value - :retrun: A numpy numerical type - """ - if string == "": - raise ValueError("Not a numerical value") - match = _parse_numeric_value.match(string) - if match is None: - raise ValueError("Not a numerical value") - number, decimal, exponent = match.groups() - - if decimal is None and exponent is None: - # It's an integer - # TODO: We could find the int type without converting the number - value = int(string) - return numpy.min_scalar_type(value).type - - # Try floating-point - try: - value = _biggest_float(string) - except ValueError: - raise ValueError("Not a numerical value") - - if number is None: - number = "" - if decimal is None: - decimal = "" - if exponent is None: - exponent = "0" - - nb_precision_digits = int(exponent) - len(decimal) - 1 - precision = _biggest_float(10) ** nb_precision_digits * 2.5 - previous_type = _biggest_float - for numpy_type in _float_types: - if numpy_type == _biggest_float: - # value was already casted using the bigger type - continue - reduced_value = numpy_type(value) - if not numpy.isfinite(reduced_value): - break - # numpy isclose(atol=is not accurate enough) - diff = value - reduced_value - # numpy 1.8.2 looks to do the substraction using float64... - # we lose precision here - diff = numpy.abs(diff) - if diff > precision: - break - previous_type = numpy_type - - # It's the smaller float type which fit with enougth precision - numpy_type = previous_type - - if check_accuracy and numpy_type == _biggest_float: - # Check the precision using the original string - expected = number + decimal - # This format the number without python convertion - try: - result = numpy.array2string(value, precision=len(number) + len(decimal), floatmode="fixed") - except TypeError: - # numpy 1.8.2 do not have floatmode argument - _logger.warning("Not able to check accuracy of the conversion of '%s' using %s", string, _biggest_float) - return numpy_type - - result = result.replace(".", "").replace("-", "") - if not result.startswith(expected): - _logger.warning("Not able to convert '%s' using %s without losing precision", string, _biggest_float) - - return numpy_type diff --git a/silx/utils/property.py b/silx/utils/property.py deleted file mode 100644 index 10d5d98..0000000 --- a/silx/utils/property.py +++ /dev/null @@ -1,52 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Bunch of useful decorators""" - -from __future__ import absolute_import, print_function, division - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "22/02/2018" - - -class classproperty(property): - """ - Decorator to transform an object method into a class property. - - This code are equivalent, but the second one can be decorated with - deprecation warning for example. - - .. code-block:: python - - class Foo(object): - VALUE = 10 - - class Foo2(object): - @classproperty - def VALUE(self): - return 10 - """ - def __get__(self, cls, owner): - return classmethod(self.fget).__get__(None, owner)() diff --git a/silx/utils/proxy.py b/silx/utils/proxy.py deleted file mode 100644 index 2633703..0000000 --- a/silx/utils/proxy.py +++ /dev/null @@ -1,204 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Module containing proxy objects""" - -from __future__ import absolute_import, print_function, division - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "02/10/2017" - -from silx.third_party import six - - -class Proxy(object): - """Create a proxy of an object. - - Provides default methods and property using :meth:`__getattr__` and special - method by redefining them one by one. - Special methods are defined as properties, as a result if the `obj` method - is not defined, the property code fail and the special method will not be - visible. - """ - - __slots__ = ["__obj", "__weakref__"] - - def __init__(self, obj): - object.__setattr__(self, "_Proxy__obj", obj) - - __class__ = property(lambda self: self.__obj.__class__) - - def __getattr__(self, name): - return getattr(self.__obj, name) - - __setattr__ = property(lambda self: self.__obj.__setattr__) - __delattr__ = property(lambda self: self.__obj.__delattr__) - - # binary comparator methods - - __lt__ = property(lambda self: self.__obj.__lt__) - __le__ = property(lambda self: self.__obj.__le__) - __eq__ = property(lambda self: self.__obj.__eq__) - __ne__ = property(lambda self: self.__obj.__ne__) - __gt__ = property(lambda self: self.__obj.__gt__) - __ge__ = property(lambda self: self.__obj.__ge__) - - if six.PY2: - __cmp__ = property(lambda self: self.__obj.__cmp__) - - # binary numeric methods - - __add__ = property(lambda self: self.__obj.__add__) - __radd__ = property(lambda self: self.__obj.__radd__) - __iadd__ = property(lambda self: self.__obj.__iadd__) - __sub__ = property(lambda self: self.__obj.__sub__) - __rsub__ = property(lambda self: self.__obj.__rsub__) - __isub__ = property(lambda self: self.__obj.__isub__) - __mul__ = property(lambda self: self.__obj.__mul__) - __rmul__ = property(lambda self: self.__obj.__rmul__) - __imul__ = property(lambda self: self.__obj.__imul__) - - if six.PY2: - # Only part of Python 2 - # Python 3 uses __truediv__ and __floordiv__ - __div__ = property(lambda self: self.__obj.__div__) - __rdiv__ = property(lambda self: self.__obj.__rdiv__) - __idiv__ = property(lambda self: self.__obj.__idiv__) - - __truediv__ = property(lambda self: self.__obj.__truediv__) - __rtruediv__ = property(lambda self: self.__obj.__rtruediv__) - __itruediv__ = property(lambda self: self.__obj.__itruediv__) - __floordiv__ = property(lambda self: self.__obj.__floordiv__) - __rfloordiv__ = property(lambda self: self.__obj.__rfloordiv__) - __ifloordiv__ = property(lambda self: self.__obj.__ifloordiv__) - __mod__ = property(lambda self: self.__obj.__mod__) - __rmod__ = property(lambda self: self.__obj.__rmod__) - __imod__ = property(lambda self: self.__obj.__imod__) - __divmod__ = property(lambda self: self.__obj.__divmod__) - __rdivmod__ = property(lambda self: self.__obj.__rdivmod__) - __pow__ = property(lambda self: self.__obj.__pow__) - __rpow__ = property(lambda self: self.__obj.__rpow__) - __ipow__ = property(lambda self: self.__obj.__ipow__) - __lshift__ = property(lambda self: self.__obj.__lshift__) - __rlshift__ = property(lambda self: self.__obj.__rlshift__) - __ilshift__ = property(lambda self: self.__obj.__ilshift__) - __rshift__ = property(lambda self: self.__obj.__rshift__) - __rrshift__ = property(lambda self: self.__obj.__rrshift__) - __irshift__ = property(lambda self: self.__obj.__irshift__) - - # binary logical methods - - __and__ = property(lambda self: self.__obj.__and__) - __rand__ = property(lambda self: self.__obj.__rand__) - __iand__ = property(lambda self: self.__obj.__iand__) - __xor__ = property(lambda self: self.__obj.__xor__) - __rxor__ = property(lambda self: self.__obj.__rxor__) - __ixor__ = property(lambda self: self.__obj.__ixor__) - __or__ = property(lambda self: self.__obj.__or__) - __ror__ = property(lambda self: self.__obj.__ror__) - __ior__ = property(lambda self: self.__obj.__ior__) - - # unary methods - - __neg__ = property(lambda self: self.__obj.__neg__) - __pos__ = property(lambda self: self.__obj.__pos__) - __abs__ = property(lambda self: self.__obj.__abs__) - __invert__ = property(lambda self: self.__obj.__invert__) - if six.PY3: - __floor__ = property(lambda self: self.__obj.__floor__) - __ceil__ = property(lambda self: self.__obj.__ceil__) - __round__ = property(lambda self: self.__obj.__round__) - - # cast - - __repr__ = property(lambda self: self.__obj.__repr__) - __str__ = property(lambda self: self.__obj.__str__) - __complex__ = property(lambda self: self.__obj.__complex__) - __int__ = property(lambda self: self.__obj.__int__) - __float__ = property(lambda self: self.__obj.__float__) - __hash__ = property(lambda self: self.__obj.__hash__) - if six.PY2: - __long__ = property(lambda self: self.__obj.__long__) - __oct__ = property(lambda self: self.__obj.__oct__) - __hex__ = property(lambda self: self.__obj.__hex__) - __unicode__ = property(lambda self: self.__obj.__unicode__) - __nonzero__ = property(lambda self: lambda: bool(self.__obj)) - if six.PY3: - __bytes__ = property(lambda self: self.__obj.__bytes__) - __bool__ = property(lambda self: lambda: bool(self.__obj)) - __format__ = property(lambda self: self.__obj.__format__) - - # container - - __len__ = property(lambda self: self.__obj.__len__) - if six.PY3: - __length_hint__ = property(lambda self: self.__obj.__length_hint__) - __getitem__ = property(lambda self: self.__obj.__getitem__) - __missing__ = property(lambda self: self.__obj.__missing__) - __setitem__ = property(lambda self: self.__obj.__setitem__) - __delitem__ = property(lambda self: self.__obj.__delitem__) - __iter__ = property(lambda self: self.__obj.__iter__) - __reversed__ = property(lambda self: self.__obj.__reversed__) - __contains__ = property(lambda self: self.__obj.__contains__) - - if six.PY2: - __getslice__ = property(lambda self: self.__obj.__getslice__) - __setslice__ = property(lambda self: self.__obj.__setslice__) - __delslice__ = property(lambda self: self.__obj.__delslice__) - - # pickle - - __reduce__ = property(lambda self: self.__obj.__reduce__) - __reduce_ex__ = property(lambda self: self.__obj.__reduce_ex__) - - # async - - if six.PY3: - __await__ = property(lambda self: self.__obj.__await__) - __aiter__ = property(lambda self: self.__obj.__aiter__) - __anext__ = property(lambda self: self.__obj.__anext__) - __aenter__ = property(lambda self: self.__obj.__aenter__) - __aexit__ = property(lambda self: self.__obj.__aexit__) - - # other - - __index__ = property(lambda self: self.__obj.__index__) - if six.PY2: - __coerce__ = property(lambda self: self.__obj.__coerce__) - - if six.PY3: - __next__ = property(lambda self: self.__obj.__next__) - - __enter__ = property(lambda self: self.__obj.__enter__) - __exit__ = property(lambda self: self.__obj.__exit__) - - __concat__ = property(lambda self: self.__obj.__concat__) - __iconcat__ = property(lambda self: self.__obj.__iconcat__) - - if six.PY2: - __repeat__ = property(lambda self: self.__obj.__repeat__) - __irepeat__ = property(lambda self: self.__obj.__irepeat__) - - __call__ = property(lambda self: self.__obj.__call__) diff --git a/silx/utils/setup.py b/silx/utils/setup.py deleted file mode 100644 index 1f3e09a..0000000 --- a/silx/utils/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "24/08/2016" - - -from numpy.distutils.misc_util import Configuration - - -def configuration(parent_package='', top_path=None): - config = Configuration('utils', parent_package, top_path) - config.add_subpackage('test') - - return config - - -if __name__ == "__main__": - from numpy.distutils.core import setup - - setup(configuration=configuration) diff --git a/silx/utils/test/__init__.py b/silx/utils/test/__init__.py deleted file mode 100644 index f16cbdc..0000000 --- a/silx/utils/test/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -__authors__ = ["T. Vincent", "P. Knobel"] -__license__ = "MIT" -__date__ = "24/05/2018" - - -import unittest -from . import test_weakref -from . import test_html -from . import test_array_like -from . import test_launcher -from . import test_deprecation -from . import test_proxy -from . import test_debug -from . import test_number - - -def suite(): - test_suite = unittest.TestSuite() - test_suite.addTest(test_weakref.suite()) - test_suite.addTest(test_html.suite()) - test_suite.addTest(test_array_like.suite()) - test_suite.addTest(test_launcher.suite()) - test_suite.addTest(test_deprecation.suite()) - test_suite.addTest(test_proxy.suite()) - test_suite.addTest(test_debug.suite()) - test_suite.addTest(test_number.suite()) - return test_suite diff --git a/silx/utils/test/test_array_like.py b/silx/utils/test/test_array_like.py deleted file mode 100644 index 7cd004c..0000000 --- a/silx/utils/test/test_array_like.py +++ /dev/null @@ -1,453 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for array_like module""" - -__authors__ = ["P. Knobel"] -__license__ = "MIT" -__date__ = "09/01/2017" - -try: - import h5py -except ImportError: - h5py = None - -import numpy -import os -import tempfile -import unittest - -from ..array_like import DatasetView, ListOfImages -from ..array_like import get_dtype, get_concatenated_dtype, get_shape,\ - is_array, is_nested_sequence, is_list_of_arrays - - -@unittest.skipIf(h5py is None, - "h5py is needed to test DatasetView") -class TestTransposedDatasetView(unittest.TestCase): - - def setUp(self): - # dataset attributes - self.ndim = 3 - self.original_shape = (5, 10, 20) - self.size = 1 - for dim in self.original_shape: - self.size *= dim - - self.volume = numpy.arange(self.size).reshape(self.original_shape) - - self.tempdir = tempfile.mkdtemp() - self.h5_fname = os.path.join(self.tempdir, "tempfile.h5") - with h5py.File(self.h5_fname, "w") as f: - f["volume"] = self.volume - - self.h5f = h5py.File(self.h5_fname, "r") - - self.all_permutations = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), - (2, 0, 1), (2, 1, 0)] - - def tearDown(self): - self.h5f.close() - os.unlink(self.h5_fname) - os.rmdir(self.tempdir) - - def _testSize(self, obj): - """These assertions apply to all following test cases""" - self.assertEqual(obj.ndim, self.ndim) - self.assertEqual(obj.size, self.size) - size_from_shape = 1 - for dim in obj.shape: - size_from_shape *= dim - self.assertEqual(size_from_shape, self.size) - - for dim in self.original_shape: - self.assertIn(dim, obj.shape) - - def testNoTransposition(self): - """no transposition (transposition = (0, 1, 2))""" - a = DatasetView(self.h5f["volume"]) - - self.assertEqual(a.shape, self.original_shape) - self._testSize(a) - - # reversing the dimensions twice results in no change - rtrans = list(reversed(range(self.ndim))) - self.assertTrue(numpy.array_equal( - a, - a.transpose(rtrans).transpose(rtrans))) - - for i in range(a.shape[0]): - for j in range(a.shape[1]): - for k in range(a.shape[2]): - self.assertEqual(self.h5f["volume"][i, j, k], - a[i, j, k]) - - def _testTransposition(self, transposition): - """test transposed dataset - - :param tuple transposition: List of dimensions (0... n-1) sorted - in the desired order - """ - a = DatasetView(self.h5f["volume"], - transposition=transposition) - self._testSize(a) - - # sort shape of transposed object, to hopefully find the original shape - sorted_shape = tuple(dim_size for (_, dim_size) in - sorted(zip(transposition, a.shape))) - self.assertEqual(sorted_shape, self.original_shape) - - a_as_array = numpy.array(self.h5f["volume"]).transpose(transposition) - - # test the __array__ method - self.assertTrue(numpy.array_equal( - numpy.array(a), - a_as_array)) - - # test slicing - for selection in [(2, slice(None), slice(None)), - (slice(None), 1, slice(0, 8)), - (slice(0, 3), slice(None), 3), - (1, 3, slice(None)), - (slice(None), 2, 1), - (4, slice(1, 9, 2), 2)]: - self.assertIsInstance(a[selection], numpy.ndarray) - self.assertTrue(numpy.array_equal( - a[selection], - a_as_array[selection])) - - # test the DatasetView.__getitem__ for single values - # (step adjusted to test at least 3 indices in each dimension) - for i in range(0, a.shape[0], a.shape[0] // 3): - for j in range(0, a.shape[1], a.shape[1] // 3): - for k in range(0, a.shape[2], a.shape[2] // 3): - sorted_indices = tuple(idx for (_, idx) in - sorted(zip(transposition, [i, j, k]))) - viewed_value = a[i, j, k] - corresponding_original_value = self.h5f["volume"][sorted_indices] - self.assertEqual(viewed_value, - corresponding_original_value) - - # reversing the dimensions twice results in no change - rtrans = list(reversed(range(self.ndim))) - self.assertTrue(numpy.array_equal( - a, - a.transpose(rtrans).transpose(rtrans))) - - # test .T property - self.assertTrue(numpy.array_equal( - a.T, - a.transpose(rtrans))) - - def testTransposition012(self): - """transposition = (0, 1, 2) - (should be the same as testNoTransposition)""" - self._testTransposition((0, 1, 2)) - - def testTransposition021(self): - """transposition = (0, 2, 1)""" - self._testTransposition((0, 2, 1)) - - def testTransposition102(self): - """transposition = (1, 0, 2)""" - self._testTransposition((1, 0, 2)) - - def testTransposition120(self): - """transposition = (1, 2, 0)""" - self._testTransposition((1, 2, 0)) - - def testTransposition201(self): - """transposition = (2, 0, 1)""" - self._testTransposition((2, 0, 1)) - - def testTransposition210(self): - """transposition = (2, 1, 0)""" - self._testTransposition((2, 1, 0)) - - def testAllDoubleTranspositions(self): - for trans1 in self.all_permutations: - for trans2 in self.all_permutations: - self._testDoubleTransposition(trans1, trans2) - - def _testDoubleTransposition(self, transposition1, transposition2): - a = DatasetView(self.h5f["volume"], - transposition=transposition1).transpose(transposition2) - - b = self.volume.transpose(transposition1).transpose(transposition2) - - self.assertTrue(numpy.array_equal(a, b), - "failed with double transposition %s %s" % (transposition1, transposition2)) - - def test1DIndex(self): - a = DatasetView(self.h5f["volume"]) - self.assertTrue(numpy.array_equal(self.volume[1], - a[1])) - - b = DatasetView(self.h5f["volume"], transposition=(1, 0, 2)) - self.assertTrue(numpy.array_equal(self.volume[:, 1, :], - b[1])) - - -class TestTransposedListOfImages(unittest.TestCase): - def setUp(self): - # images attributes - self.ndim = 3 - self.original_shape = (5, 10, 20) - self.size = 1 - for dim in self.original_shape: - self.size *= dim - - volume = numpy.arange(self.size).reshape(self.original_shape) - - self.images = [] - for i in range(self.original_shape[0]): - self.images.append( - volume[i]) - - self.images_as_3D_array = numpy.array(self.images) - - self.all_permutations = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), - (2, 0, 1), (2, 1, 0)] - - def tearDown(self): - pass - - def _testSize(self, obj): - """These assertions apply to all following test cases""" - self.assertEqual(obj.ndim, self.ndim) - self.assertEqual(obj.size, self.size) - size_from_shape = 1 - for dim in obj.shape: - size_from_shape *= dim - self.assertEqual(size_from_shape, self.size) - - for dim in self.original_shape: - self.assertIn(dim, obj.shape) - - def testNoTransposition(self): - """no transposition (transposition = (0, 1, 2))""" - a = ListOfImages(self.images) - - self.assertEqual(a.shape, self.original_shape) - self._testSize(a) - - for i in range(a.shape[0]): - for j in range(a.shape[1]): - for k in range(a.shape[2]): - self.assertEqual(self.images[i][j, k], - a[i, j, k]) - - # reversing the dimensions twice results in no change - rtrans = list(reversed(range(self.ndim))) - self.assertTrue(numpy.array_equal( - a, - a.transpose(rtrans).transpose(rtrans))) - - # test .T property - self.assertTrue(numpy.array_equal( - a.T, - a.transpose(rtrans))) - - def _testTransposition(self, transposition): - """test transposed dataset - - :param tuple transposition: List of dimensions (0... n-1) sorted - in the desired order - """ - a = ListOfImages(self.images, - transposition=transposition) - self._testSize(a) - - # sort shape of transposed object, to hopefully find the original shape - sorted_shape = tuple(dim_size for (_, dim_size) in - sorted(zip(transposition, a.shape))) - self.assertEqual(sorted_shape, self.original_shape) - - a_as_array = numpy.array(self.images).transpose(transposition) - - # test the DatasetView.__array__ method - self.assertTrue(numpy.array_equal( - numpy.array(a), - a_as_array)) - - # test slicing - for selection in [(2, slice(None), slice(None)), - (slice(None), 1, slice(0, 8)), - (slice(0, 3), slice(None), 3), - (1, 3, slice(None)), - (slice(None), 2, 1), - (4, slice(1, 9, 2), 2)]: - self.assertIsInstance(a[selection], numpy.ndarray) - self.assertTrue(numpy.array_equal( - a[selection], - a_as_array[selection])) - - # test the DatasetView.__getitem__ for single values - # (step adjusted to test at least 3 indices in each dimension) - for i in range(0, a.shape[0], a.shape[0] // 3): - for j in range(0, a.shape[1], a.shape[1] // 3): - for k in range(0, a.shape[2], a.shape[2] // 3): - viewed_value = a[i, j, k] - sorted_indices = tuple(idx for (_, idx) in - sorted(zip(transposition, [i, j, k]))) - corresponding_original_value = self.images[sorted_indices[0]][sorted_indices[1:]] - self.assertEqual(viewed_value, - corresponding_original_value) - - # reversing the dimensions twice results in no change - rtrans = list(reversed(range(self.ndim))) - self.assertTrue(numpy.array_equal( - a, - a.transpose(rtrans).transpose(rtrans))) - - # test .T property - self.assertTrue(numpy.array_equal( - a.T, - a.transpose(rtrans))) - - def _testDoubleTransposition(self, transposition1, transposition2): - a = ListOfImages(self.images, - transposition=transposition1).transpose(transposition2) - - b = self.images_as_3D_array.transpose(transposition1).transpose(transposition2) - - self.assertTrue(numpy.array_equal(a, b), - "failed with double transposition %s %s" % (transposition1, transposition2)) - - def testTransposition012(self): - """transposition = (0, 1, 2) - (should be the same as testNoTransposition)""" - self._testTransposition((0, 1, 2)) - - def testTransposition021(self): - """transposition = (0, 2, 1)""" - self._testTransposition((0, 2, 1)) - - def testTransposition102(self): - """transposition = (1, 0, 2)""" - self._testTransposition((1, 0, 2)) - - def testTransposition120(self): - """transposition = (1, 2, 0)""" - self._testTransposition((1, 2, 0)) - - def testTransposition201(self): - """transposition = (2, 0, 1)""" - self._testTransposition((2, 0, 1)) - - def testTransposition210(self): - """transposition = (2, 1, 0)""" - self._testTransposition((2, 1, 0)) - - def testAllDoubleTranspositions(self): - for trans1 in self.all_permutations: - for trans2 in self.all_permutations: - self._testDoubleTransposition(trans1, trans2) - - def test1DIndex(self): - a = ListOfImages(self.images) - self.assertTrue(numpy.array_equal(self.images[1], - a[1])) - - b = ListOfImages(self.images, transposition=(1, 0, 2)) - self.assertTrue(numpy.array_equal(self.images_as_3D_array[:, 1, :], - b[1])) - - -class TestFunctions(unittest.TestCase): - """Test functions to guess the dtype and shape of an array_like - object""" - def testListOfLists(self): - l = [[0, 1, 2], [2, 3, 4]] - self.assertEqual(get_dtype(l), - numpy.dtype(int)) - self.assertEqual(get_shape(l), - (2, 3)) - self.assertTrue(is_nested_sequence(l)) - self.assertFalse(is_array(l)) - self.assertFalse(is_list_of_arrays(l)) - - l = [[0., 1.], [2., 3.]] - self.assertEqual(get_dtype(l), - numpy.dtype(float)) - self.assertEqual(get_shape(l), - (2, 2)) - self.assertTrue(is_nested_sequence(l)) - self.assertFalse(is_array(l)) - self.assertFalse(is_list_of_arrays(l)) - - # concatenated dtype of int and float - l = [numpy.array([[0, 1, 2], [2, 3, 4]]), - numpy.array([[0., 1., 2.], [2., 3., 4.]])] - - self.assertEqual(get_concatenated_dtype(l), - numpy.array(l).dtype) - self.assertEqual(get_shape(l), - (2, 2, 3)) - self.assertFalse(is_nested_sequence(l)) - self.assertFalse(is_array(l)) - self.assertTrue(is_list_of_arrays(l)) - - def testNumpyArray(self): - a = numpy.array([[0, 1], [2, 3]]) - self.assertEqual(get_dtype(a), - a.dtype) - self.assertFalse(is_nested_sequence(a)) - self.assertTrue(is_array(a)) - self.assertFalse(is_list_of_arrays(a)) - - @unittest.skipIf(h5py is None, - "h5py is needed for this test") - def testH5pyDataset(self): - a = numpy.array([[0, 1], [2, 3]]) - - tempdir = tempfile.mkdtemp() - h5_fname = os.path.join(tempdir, "tempfile.h5") - with h5py.File(h5_fname, "w") as h5f: - h5f["dataset"] = a - d = h5f["dataset"] - - self.assertEqual(get_dtype(d), - numpy.dtype(int)) - self.assertFalse(is_nested_sequence(d)) - self.assertTrue(is_array(d)) - self.assertFalse(is_list_of_arrays(d)) - - os.unlink(h5_fname) - os.rmdir(tempdir) - - -def suite(): - test_suite = unittest.TestSuite() - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestTransposedDatasetView)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestTransposedListOfImages)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestFunctions)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/utils/test/test_debug.py b/silx/utils/test/test_debug.py deleted file mode 100644 index da08960..0000000 --- a/silx/utils/test/test_debug.py +++ /dev/null @@ -1,99 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for debug module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "27/02/2018" - - -import unittest -from silx.utils import debug -from silx.utils import testutils - - -@debug.log_all_methods -class _Foobar(object): - - def a(self): - return None - - def b(self): - return self.a() - - def random_args(self, *args, **kwargs): - return args, kwargs - - def named_args(self, a, b): - return a + 1, b + 1 - - -class TestDebug(unittest.TestCase): - """Tests for debug module.""" - - def logB(self): - """ - Can be used to check the log output using: - `./run_tests.py silx.utils.test.test_debug.TestDebug.logB -v` - """ - print() - test = _Foobar() - test.b() - - @testutils.test_logging(debug.debug_logger.name, warning=2) - def testMethod(self): - test = _Foobar() - test.a() - - @testutils.test_logging(debug.debug_logger.name, warning=4) - def testInterleavedMethod(self): - test = _Foobar() - test.b() - - @testutils.test_logging(debug.debug_logger.name, warning=2) - def testNamedArgument(self): - # Arguments arre still provided to the patched method - test = _Foobar() - result = test.named_args(10, 11) - self.assertEqual(result, (11, 12)) - - @testutils.test_logging(debug.debug_logger.name, warning=2) - def testRandomArguments(self): - # Arguments arre still provided to the patched method - test = _Foobar() - result = test.random_args("foo", 50, a=10, b=100) - self.assertEqual(result[0], ("foo", 50)) - self.assertEqual(result[1], {"a": 10, "b": 100}) - - -def suite(): - test_suite = unittest.TestSuite() - loadTests = unittest.defaultTestLoader.loadTestsFromTestCase - test_suite.addTest(loadTests(TestDebug)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/utils/test/test_deprecation.py b/silx/utils/test/test_deprecation.py deleted file mode 100644 index 0aa06a0..0000000 --- a/silx/utils/test/test_deprecation.py +++ /dev/null @@ -1,107 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for html module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "17/01/2018" - - -import unittest -from .. import deprecation -from silx.utils import testutils - - -class TestDeprecation(unittest.TestCase): - """Tests for deprecation module.""" - - @deprecation.deprecated - def deprecatedWithoutParam(self): - pass - - @deprecation.deprecated(reason="r", replacement="r", since_version="v") - def deprecatedWithParams(self): - pass - - @deprecation.deprecated(reason="r", replacement="r", since_version="v", only_once=True) - def deprecatedOnlyOnce(self): - pass - - @deprecation.deprecated(reason="r", replacement="r", since_version="v", only_once=False) - def deprecatedEveryTime(self): - pass - - @testutils.test_logging(deprecation.depreclog.name, warning=1) - def testAnnotationWithoutParam(self): - self.deprecatedWithoutParam() - - @testutils.test_logging(deprecation.depreclog.name, warning=1) - def testAnnotationWithParams(self): - self.deprecatedWithParams() - - @testutils.test_logging(deprecation.depreclog.name, warning=3) - def testLoggedEveryTime(self): - """Logged everytime cause it is 3 different locations""" - self.deprecatedOnlyOnce() - self.deprecatedOnlyOnce() - self.deprecatedOnlyOnce() - - @testutils.test_logging(deprecation.depreclog.name, warning=1) - def testLoggedSingleTime(self): - def log(): - self.deprecatedOnlyOnce() - log() - log() - log() - - @testutils.test_logging(deprecation.depreclog.name, warning=3) - def testLoggedEveryTime2(self): - self.deprecatedEveryTime() - self.deprecatedEveryTime() - self.deprecatedEveryTime() - - @testutils.test_logging(deprecation.depreclog.name, warning=1) - def testWarning(self): - deprecation.deprecated_warning(type_="t", name="n") - - def testBacktrace(self): - testLogging = testutils.TestLogging(deprecation.depreclog.name) - with testLogging: - self.deprecatedEveryTime() - message = testLogging.records[0].getMessage() - filename = __file__.replace(".pyc", ".py") - self.assertTrue(filename in message) - self.assertTrue("testBacktrace" in message) - - -def suite(): - test_suite = unittest.TestSuite() - loadTests = unittest.defaultTestLoader.loadTestsFromTestCase - test_suite.addTest(loadTests(TestDeprecation)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/utils/test/test_html.py b/silx/utils/test/test_html.py deleted file mode 100644 index 4af8560..0000000 --- a/silx/utils/test/test_html.py +++ /dev/null @@ -1,61 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for html module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "19/09/2016" - - -import unittest -from .. import html - - -class TestHtml(unittest.TestCase): - """Tests for html module.""" - - def testLtGt(self): - result = html.escape("<html>'\"") - self.assertEqual("<html>'"", result) - - def testLtAmpGt(self): - # '&' have to be escaped first - result = html.escape("<&>") - self.assertEqual("<&>", result) - - def testNoQuotes(self): - result = html.escape("\"m&m's\"", quote=False) - self.assertEqual("\"m&m's\"", result) - - -def suite(): - test_suite = unittest.TestSuite() - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestHtml)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/utils/test/test_launcher.py b/silx/utils/test/test_launcher.py deleted file mode 100644 index c64ac9a..0000000 --- a/silx/utils/test/test_launcher.py +++ /dev/null @@ -1,204 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for html module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "17/01/2018" - - -import sys -import unittest -from silx.utils.testutils import ParametricTestCase -from .. import launcher - - -class CallbackMock(): - - def __init__(self, result=None): - self._execute_count = 0 - self._execute_argv = None - self._result = result - - def execute(self, argv): - self._execute_count = self._execute_count + 1 - self._execute_argv = argv - return self._result - - def __call__(self, argv): - return self.execute(argv) - - -class TestLauncherCommand(unittest.TestCase): - """Tests for launcher class.""" - - def testEnv(self): - command = launcher.LauncherCommand("foo") - old = sys.argv - params = ["foo", "bar"] - with command.get_env(params): - self.assertEqual(params, sys.argv) - self.assertEqual(sys.argv, old) - - def testEnvWhileException(self): - command = launcher.LauncherCommand("foo") - old = sys.argv - params = ["foo", "bar"] - try: - with command.get_env(params): - raise RuntimeError() - except RuntimeError: - pass - self.assertEqual(sys.argv, old) - - def testExecute(self): - params = ["foo", "bar"] - callback = CallbackMock(result=42) - command = launcher.LauncherCommand("foo", function=callback) - status = command.execute(params) - self.assertEqual(callback._execute_count, 1) - self.assertEqual(callback._execute_argv, params) - self.assertEqual(status, 42) - - -class TestModuleCommand(ParametricTestCase): - - def setUp(self): - module_name = "silx.utils.test.test_launcher_command" - command = launcher.LauncherCommand("foo", module_name=module_name) - self.command = command - - def testHelp(self): - status = self.command.execute(["--help"]) - self.assertEqual(status, 0) - - def testException(self): - try: - self.command.execute(["exception"]) - self.fail() - except RuntimeError: - pass - - def testCall(self): - status = self.command.execute([]) - self.assertEqual(status, 0) - - def testError(self): - status = self.command.execute(["error"]) - self.assertEqual(status, -1) - - -class TestLauncher(ParametricTestCase): - """Tests for launcher class.""" - - def testCallCommand(self): - callback = CallbackMock(result=42) - runner = launcher.Launcher(prog="prog") - command = launcher.LauncherCommand("foo", function=callback) - runner.add_command(command=command) - status = runner.execute(["prog", "foo", "param1", "param2"]) - self.assertEqual(status, 42) - self.assertEqual(callback._execute_argv, ["prog foo", "param1", "param2"]) - self.assertEqual(callback._execute_count, 1) - - def testAddCommand(self): - runner = launcher.Launcher(prog="prog") - module_name = "silx.utils.test.test_launcher_command" - runner.add_command("foo", module_name=module_name) - status = runner.execute(["prog", "foo"]) - self.assertEqual(status, 0) - - def testCallHelpOnCommand(self): - callback = CallbackMock(result=42) - runner = launcher.Launcher(prog="prog") - command = launcher.LauncherCommand("foo", function=callback) - runner.add_command(command=command) - status = runner.execute(["prog", "--help", "foo"]) - self.assertEqual(status, 42) - self.assertEqual(callback._execute_argv, ["prog foo", "--help"]) - self.assertEqual(callback._execute_count, 1) - - def testCallHelpOnCommand2(self): - callback = CallbackMock(result=42) - runner = launcher.Launcher(prog="prog") - command = launcher.LauncherCommand("foo", function=callback) - runner.add_command(command=command) - status = runner.execute(["prog", "help", "foo"]) - self.assertEqual(status, 42) - self.assertEqual(callback._execute_argv, ["prog foo", "--help"]) - self.assertEqual(callback._execute_count, 1) - - def testCallHelpOnUnknownCommand(self): - callback = CallbackMock(result=42) - runner = launcher.Launcher(prog="prog") - command = launcher.LauncherCommand("foo", function=callback) - runner.add_command(command=command) - status = runner.execute(["prog", "help", "foo2"]) - self.assertEqual(status, -1) - - def testNotAvailableCommand(self): - callback = CallbackMock(result=42) - runner = launcher.Launcher(prog="prog") - command = launcher.LauncherCommand("foo", function=callback) - runner.add_command(command=command) - status = runner.execute(["prog", "foo2", "param1", "param2"]) - self.assertEqual(status, -1) - self.assertEqual(callback._execute_count, 0) - - def testCallHelp(self): - callback = CallbackMock(result=42) - runner = launcher.Launcher(prog="prog") - command = launcher.LauncherCommand("foo", function=callback) - runner.add_command(command=command) - status = runner.execute(["prog", "help"]) - self.assertEqual(status, 0) - self.assertEqual(callback._execute_count, 0) - - def testCommonCommands(self): - runner = launcher.Launcher() - tests = [ - ["prog"], - ["prog", "--help"], - ["prog", "--version"], - ["prog", "help", "--help"], - ["prog", "help", "help"], - ] - for arguments in tests: - with self.subTest(args=tests): - status = runner.execute(arguments) - self.assertEqual(status, 0) - - -def suite(): - loader = unittest.defaultTestLoader.loadTestsFromTestCase - test_suite = unittest.TestSuite() - test_suite.addTest(loader(TestLauncherCommand)) - test_suite.addTest(loader(TestLauncher)) - test_suite.addTest(loader(TestModuleCommand)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/utils/test/test_launcher_command.py b/silx/utils/test/test_launcher_command.py deleted file mode 100644 index ccf4601..0000000 --- a/silx/utils/test/test_launcher_command.py +++ /dev/null @@ -1,47 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for html module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "03/04/2017" - - -import sys - - -def main(argv): - - if "--help" in argv: - # Common behaviour of ArgumentParser - sys.exit(0) - - if "exception" in argv: - raise RuntimeError("Simulated exception") - - if "error" in argv: - return -1 - - return 0 diff --git a/silx/utils/test/test_number.py b/silx/utils/test/test_number.py deleted file mode 100644 index c900b32..0000000 --- a/silx/utils/test/test_number.py +++ /dev/null @@ -1,181 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-2018 European Synchrotron Radiation Facility -# -# 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. -# -# ############################################################################*/ -"""Tests for silx.uitls.number module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "05/06/2018" - -import logging -import numpy -import unittest -import pkg_resources -from silx.utils import number -from silx.utils import testutils - -_logger = logging.getLogger(__name__) - - -class TestConversionTypes(testutils.ParametricTestCase): - - def testEmptyFail(self): - self.assertRaises(ValueError, number.min_numerical_convertible_type, "") - - def testStringFail(self): - self.assertRaises(ValueError, number.min_numerical_convertible_type, "a") - - def testInteger(self): - dtype = number.min_numerical_convertible_type("1456") - self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger)) - - def testTrailledInteger(self): - dtype = number.min_numerical_convertible_type(" \t\n\r1456\t\n\r") - self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger)) - - def testPositiveInteger(self): - dtype = number.min_numerical_convertible_type("+1456") - self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger)) - - def testNegativeInteger(self): - dtype = number.min_numerical_convertible_type("-1456") - self.assertTrue(numpy.issubdtype(dtype, numpy.signedinteger)) - - def testIntegerExponential(self): - dtype = number.min_numerical_convertible_type("14e10") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testIntegerPositiveExponential(self): - dtype = number.min_numerical_convertible_type("14e+10") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testIntegerNegativeExponential(self): - dtype = number.min_numerical_convertible_type("14e-10") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testNumberDecimal(self): - dtype = number.min_numerical_convertible_type("14.5") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testPositiveNumberDecimal(self): - dtype = number.min_numerical_convertible_type("+14.5") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testNegativeNumberDecimal(self): - dtype = number.min_numerical_convertible_type("-14.5") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testDecimal(self): - dtype = number.min_numerical_convertible_type(".50") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testPositiveDecimal(self): - dtype = number.min_numerical_convertible_type("+.5") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testNegativeDecimal(self): - dtype = number.min_numerical_convertible_type("-.5") - self.assertTrue(numpy.issubdtype(dtype, numpy.floating)) - - def testMantissa16(self): - dtype = number.min_numerical_convertible_type("1.50") - self.assertEqual(dtype, numpy.float16) - - def testMantissa32(self): - dtype = number.min_numerical_convertible_type("1400.50") - self.assertEqual(dtype, numpy.float32) - - def testMantissa64(self): - dtype = number.min_numerical_convertible_type("10000.000010") - self.assertEqual(dtype, numpy.float64) - - def testMantissa80(self): - self.skipIfFloat80NotSupported() - dtype = number.min_numerical_convertible_type("1000000000.00001013") - - if pkg_resources.parse_version(numpy.version.version) <= pkg_resources.parse_version("1.10.4"): - # numpy 1.8.2 -> Debian 8 - # Checking a float128 precision with numpy 1.8.2 using abs(diff) is not working. - # It looks like the difference is done using float64 (diff == 0.0) - expected = (numpy.longdouble, numpy.float64) - else: - expected = (numpy.longdouble, ) - self.assertIn(dtype, expected) - - def testExponent32(self): - dtype = number.min_numerical_convertible_type("14.0e30") - self.assertEqual(dtype, numpy.float32) - - def testExponent64(self): - dtype = number.min_numerical_convertible_type("14.0e300") - self.assertEqual(dtype, numpy.float64) - - def testExponent80(self): - self.skipIfFloat80NotSupported() - dtype = number.min_numerical_convertible_type("14.0e3000") - self.assertEqual(dtype, numpy.longdouble) - - def testFloat32ToString(self): - value = str(numpy.float32(numpy.pi)) - dtype = number.min_numerical_convertible_type(value) - self.assertIn(dtype, (numpy.float32, numpy.float64)) - - def skipIfFloat80NotSupported(self): - if number.is_longdouble_64bits(): - self.skipTest("float-80bits not supported") - - def testLosePrecisionUsingFloat80(self): - self.skipIfFloat80NotSupported() - if pkg_resources.parse_version(numpy.version.version) <= pkg_resources.parse_version("1.10.4"): - self.skipTest("numpy > 1.10.4 expected") - value = "1000000000.00001013332" - func = testutils.test_logging(number._logger.name, warning=1) - func = func(number.min_numerical_convertible_type) - dtype = func(value) - self.assertIn(dtype, (numpy.longdouble, )) - - def testMillisecondEpochTime(self): - datetimes = ['1465803236.495412', - '1465803236.999362', - '1465803237.504311', - '1465803238.009261', - '1465803238.512211', - '1465803239.016160', - '1465803239.520110', - '1465803240.026059', - '1465803240.529009'] - for datetime in datetimes: - with self.subTest(datetime=datetime): - dtype = number.min_numerical_convertible_type(datetime) - self.assertEqual(dtype, numpy.float64) - - -def suite(): - loadTests = unittest.defaultTestLoader.loadTestsFromTestCase - test_suite = unittest.TestSuite() - test_suite.addTest(loadTests(TestConversionTypes)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest="suite") diff --git a/silx/utils/test/test_proxy.py b/silx/utils/test/test_proxy.py deleted file mode 100644 index 081d3d4..0000000 --- a/silx/utils/test/test_proxy.py +++ /dev/null @@ -1,295 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for weakref module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "02/10/2017" - - -import unittest -import pickle -import numpy -from ..proxy import Proxy - - -class Thing(object): - - def __init__(self, value): - self.value = value - - def __getitem__(self, selection): - return selection + 1 - - def method(self, value): - return value + 2 - - -class InheritedProxy(Proxy): - """Inheriting the proxy allow to specialisze methods""" - - def __init__(self, obj, value): - Proxy.__init__(self, obj) - self.value = value + 2 - - def __getitem__(self, selection): - return selection + 3 - - def method(self, value): - return value + 4 - - -class TestProxy(unittest.TestCase): - """Test that the proxy behave as expected""" - - def text_init(self): - obj = Thing(10) - p = Proxy(obj) - self.assertTrue(isinstance(p, Thing)) - self.assertTrue(isinstance(p, Proxy)) - - # methods and properties - - def test_has_special_method(self): - obj = Thing(10) - p = Proxy(obj) - self.assertTrue(hasattr(p, "__getitem__")) - - def test_missing_special_method(self): - obj = Thing(10) - p = Proxy(obj) - self.assertFalse(hasattr(p, "__and__")) - - def test_method(self): - obj = Thing(10) - p = Proxy(obj) - self.assertEqual(p.method(10), obj.method(10)) - - def test_property(self): - obj = Thing(10) - p = Proxy(obj) - self.assertEqual(p.value, obj.value) - - # special functions - - def test_getitem(self): - obj = Thing(10) - p = Proxy(obj) - self.assertEqual(p[10], obj[10]) - - def test_setitem(self): - obj = numpy.array([10, 20, 30]) - p = Proxy(obj) - p[0] = 20 - self.assertEqual(obj[0], 20) - - def test_slice(self): - obj = numpy.arange(20) - p = Proxy(obj) - expected = obj[0:10:2] - result = p[0:10:2] - self.assertEqual(list(result), list(expected)) - - # binary comparator methods - - def test_lt(self): - obj = numpy.array([20]) - p = Proxy(obj) - expected = obj < obj - result = p < p - self.assertEqual(result, expected) - - # binary numeric methods - - def test_add(self): - obj = numpy.array([20]) - proxy = Proxy(obj) - expected = obj + obj - result = proxy + proxy - self.assertEqual(result, expected) - - def test_iadd(self): - expected = numpy.array([20]) - expected += 10 - obj = numpy.array([20]) - result = Proxy(obj) - result += 10 - self.assertEqual(result, expected) - - def test_radd(self): - obj = numpy.array([20]) - p = Proxy(obj) - expected = 10 + obj - result = 10 + p - self.assertEqual(result, expected) - - # binary logical methods - - def test_and(self): - obj = numpy.array([20]) - p = Proxy(obj) - expected = obj & obj - result = p & p - self.assertEqual(result, expected) - - def test_iand(self): - expected = numpy.array([20]) - expected &= 10 - obj = numpy.array([20]) - result = Proxy(obj) - result &= 10 - self.assertEqual(result, expected) - - def test_rand(self): - obj = numpy.array([20]) - p = Proxy(obj) - expected = 10 & obj - result = 10 & p - self.assertEqual(result, expected) - - # unary methods - - def test_neg(self): - obj = numpy.array([20]) - p = Proxy(obj) - expected = -obj - result = -p - self.assertEqual(result, expected) - - def test_round(self): - obj = 20.5 - p = Proxy(obj) - expected = round(obj) - result = round(p) - self.assertEqual(result, expected) - - # cast - - def test_bool(self): - obj = True - p = Proxy(obj) - if p: - pass - else: - self.fail() - - def test_str(self): - obj = Thing(10) - p = Proxy(obj) - expected = str(obj) - result = str(p) - self.assertEqual(result, expected) - - def test_repr(self): - obj = Thing(10) - p = Proxy(obj) - expected = repr(obj) - result = repr(p) - self.assertEqual(result, expected) - - def test_text_bool(self): - obj = "" - p = Proxy(obj) - if p: - self.fail() - else: - pass - - def test_text_str(self): - obj = "a" - p = Proxy(obj) - expected = str(obj) - result = str(p) - self.assertEqual(result, expected) - - def test_text_repr(self): - obj = "a" - p = Proxy(obj) - expected = repr(obj) - result = repr(p) - self.assertEqual(result, expected) - - def test_hash(self): - obj = [0, 1, 2] - p = Proxy(obj) - with self.assertRaises(TypeError): - hash(p) - obj = (0, 1, 2) - p = Proxy(obj) - hash(p) - - -class TestInheritedProxy(unittest.TestCase): - """Test that inheriting the Proxy class behave as expected""" - - # methods and properties - - def test_method(self): - obj = Thing(10) - p = InheritedProxy(obj, 11) - self.assertEqual(p.method(10), 11 + 3) - - def test_property(self): - obj = Thing(10) - p = InheritedProxy(obj, 11) - self.assertEqual(p.value, 11 + 2) - - # special functions - - def test_getitem(self): - obj = Thing(10) - p = InheritedProxy(obj, 11) - self.assertEqual(p[12], 12 + 3) - - -class TestPickle(unittest.TestCase): - - def test_dumps(self): - obj = Thing(10) - p = Proxy(obj) - expected = pickle.dumps(obj) - result = pickle.dumps(p) - self.assertEqual(result, expected) - - def test_loads(self): - obj = Thing(10) - p = Proxy(obj) - obj2 = pickle.loads(pickle.dumps(p)) - self.assertTrue(isinstance(obj2, Thing)) - self.assertFalse(isinstance(obj2, Proxy)) - self.assertEqual(obj.value, obj2.value) - - -def suite(): - loadTests = unittest.defaultTestLoader.loadTestsFromTestCase - test_suite = unittest.TestSuite() - test_suite.addTest(loadTests(TestProxy)) - test_suite.addTest(loadTests(TestPickle)) - test_suite.addTest(loadTests(TestInheritedProxy)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/utils/test/test_weakref.py b/silx/utils/test/test_weakref.py deleted file mode 100644 index 16d3cf5..0000000 --- a/silx/utils/test/test_weakref.py +++ /dev/null @@ -1,330 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Tests for weakref module""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "15/09/2016" - - -import unittest -from .. import weakref - - -class Dummy(object): - """Dummy class to use it as geanie pig""" - def inc(self, a): - return a + 1 - - def __lt__(self, other): - return True - - -def dummy_inc(a): - """Dummy function to use it as geanie pig""" - return a + 1 - - -class TestWeakMethod(unittest.TestCase): - """Tests for weakref.WeakMethod""" - - def testMethod(self): - dummy = Dummy() - callable_ = weakref.WeakMethod(dummy.inc) - self.assertEqual(callable_()(10), 11) - - def testMethodWithDeadObject(self): - dummy = Dummy() - callable_ = weakref.WeakMethod(dummy.inc) - dummy = None - self.assertIsNone(callable_()) - - def testMethodWithDeadFunction(self): - dummy = Dummy() - dummy.inc2 = lambda self, a: a + 1 - callable_ = weakref.WeakMethod(dummy.inc2) - dummy.inc2 = None - self.assertIsNone(callable_()) - - def testFunction(self): - callable_ = weakref.WeakMethod(dummy_inc) - self.assertEqual(callable_()(10), 11) - - def testDeadFunction(self): - def inc(a): - return a + 1 - callable_ = weakref.WeakMethod(inc) - inc = None - self.assertIsNone(callable_()) - - def testLambda(self): - store = lambda a: a + 1 # noqa: E731 - callable_ = weakref.WeakMethod(store) - self.assertEqual(callable_()(10), 11) - - def testDeadLambda(self): - callable_ = weakref.WeakMethod(lambda a: a + 1) - self.assertIsNone(callable_()) - - def testCallbackOnDeadObject(self): - self.__count = 0 - - def callback(ref): - self.__count += 1 - self.assertIs(callable_, ref) - dummy = Dummy() - callable_ = weakref.WeakMethod(dummy.inc, callback) - dummy = None - self.assertEqual(self.__count, 1) - - def testCallbackOnDeadMethod(self): - self.__count = 0 - - def callback(ref): - self.__count += 1 - self.assertIs(callable_, ref) - dummy = Dummy() - dummy.inc2 = lambda self, a: a + 1 - callable_ = weakref.WeakMethod(dummy.inc2, callback) - dummy.inc2 = None - self.assertEqual(self.__count, 1) - - def testCallbackOnDeadFunction(self): - self.__count = 0 - - def callback(ref): - self.__count += 1 - self.assertIs(callable_, ref) - store = lambda a: a + 1 # noqa: E731 - callable_ = weakref.WeakMethod(store, callback) - store = None - self.assertEqual(self.__count, 1) - - def testEquals(self): - dummy = Dummy() - callable1 = weakref.WeakMethod(dummy.inc) - callable2 = weakref.WeakMethod(dummy.inc) - self.assertEqual(callable1, callable2) - - def testInSet(self): - callable_set = set([]) - dummy = Dummy() - callable_set.add(weakref.WeakMethod(dummy.inc)) - callable_ = weakref.WeakMethod(dummy.inc) - self.assertIn(callable_, callable_set) - - def testInDict(self): - callable_dict = {} - dummy = Dummy() - callable_dict[weakref.WeakMethod(dummy.inc)] = 10 - callable_ = weakref.WeakMethod(dummy.inc) - self.assertEqual(callable_dict.get(callable_), 10) - - -class TestWeakMethodProxy(unittest.TestCase): - - def testMethod(self): - dummy = Dummy() - callable_ = weakref.WeakMethodProxy(dummy.inc) - self.assertEqual(callable_(10), 11) - - def testMethodWithDeadObject(self): - dummy = Dummy() - method = weakref.WeakMethodProxy(dummy.inc) - dummy = None - self.assertRaises(ReferenceError, method, 9) - - -class TestWeakList(unittest.TestCase): - """Tests for weakref.WeakList""" - - def setUp(self): - self.list = weakref.WeakList() - self.object1 = Dummy() - self.object2 = Dummy() - self.list.append(self.object1) - self.list.append(self.object2) - - def testAppend(self): - obj = Dummy() - self.list.append(obj) - self.assertEqual(len(self.list), 3) - obj = None - self.assertEqual(len(self.list), 2) - - def testRemove(self): - self.list.remove(self.object1) - self.assertEqual(len(self.list), 1) - - def testPop(self): - obj = self.list.pop(0) - self.assertIs(obj, self.object1) - self.assertEqual(len(self.list), 1) - - def testGetItem(self): - self.assertIs(self.object1, self.list[0]) - - def testGetItemSlice(self): - objects = self.list[:] - self.assertEqual(len(objects), 2) - self.assertIs(self.object1, objects[0]) - self.assertIs(self.object2, objects[1]) - - def testIter(self): - obj_list = list(self.list) - self.assertEqual(len(obj_list), 2) - self.assertIs(self.object1, obj_list[0]) - - def testLen(self): - self.assertEqual(len(self.list), 2) - - def testSetItem(self): - obj = Dummy() - self.list[0] = obj - self.assertIsNot(self.object1, self.list[0]) - obj = None - self.assertEqual(len(self.list), 1) - - def testSetItemSlice(self): - obj = Dummy() - self.list[:] = [obj, obj] - self.assertEqual(len(self.list), 2) - self.assertIs(obj, self.list[0]) - self.assertIs(obj, self.list[1]) - obj = None - self.assertEqual(len(self.list), 0) - - def testDelItem(self): - del self.list[0] - self.assertEqual(len(self.list), 1) - self.assertIs(self.object2, self.list[0]) - - def testDelItemSlice(self): - del self.list[:] - self.assertEqual(len(self.list), 0) - - def testContains(self): - self.assertIn(self.object1, self.list) - - def testAdd(self): - others = [Dummy()] - l = self.list + others - self.assertIs(l[0], self.object1) - self.assertEqual(len(l), 3) - others = None - self.assertEqual(len(l), 2) - - def testExtend(self): - others = [Dummy()] - self.list.extend(others) - self.assertIs(self.list[0], self.object1) - self.assertEqual(len(self.list), 3) - others = None - self.assertEqual(len(self.list), 2) - - def testIadd(self): - others = [Dummy()] - self.list += others - self.assertIs(self.list[0], self.object1) - self.assertEqual(len(self.list), 3) - others = None - self.assertEqual(len(self.list), 2) - - def testMul(self): - l = self.list * 2 - self.assertIs(l[0], self.object1) - self.assertEqual(len(l), 4) - self.object1 = None - self.assertEqual(len(l), 2) - self.assertIs(l[0], self.object2) - self.assertIs(l[1], self.object2) - - def testImul(self): - self.list *= 2 - self.assertIs(self.list[0], self.object1) - self.assertEqual(len(self.list), 4) - self.object1 = None - self.assertEqual(len(self.list), 2) - self.assertIs(self.list[0], self.object2) - self.assertIs(self.list[1], self.object2) - - def testCount(self): - self.list.append(self.object2) - self.assertEqual(self.list.count(self.object1), 1) - self.assertEqual(self.list.count(self.object2), 2) - - def testIndex(self): - self.assertEqual(self.list.index(self.object1), 0) - self.assertEqual(self.list.index(self.object2), 1) - - def testInsert(self): - obj = Dummy() - self.list.insert(1, obj) - self.assertEqual(len(self.list), 3) - self.assertIs(self.list[1], obj) - obj = None - self.assertEqual(len(self.list), 2) - - def testReverse(self): - self.list.reverse() - self.assertEqual(len(self.list), 2) - self.assertIs(self.list[0], self.object2) - self.assertIs(self.list[1], self.object1) - - def testReverted(self): - new_list = reversed(self.list) - self.assertEqual(len(new_list), 2) - self.assertIs(self.list[1], self.object2) - self.assertIs(self.list[0], self.object1) - self.assertIs(new_list[0], self.object2) - self.assertIs(new_list[1], self.object1) - self.object1 = None - self.assertEqual(len(new_list), 1) - - def testStr(self): - self.assertNotEquals(self.list.__str__(), "[]") - - def testRepr(self): - self.assertNotEquals(self.list.__repr__(), "[]") - - def testSort(self): - # only a coverage - self.list.sort() - self.assertEqual(len(self.list), 2) - - -def suite(): - test_suite = unittest.TestSuite() - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestWeakMethod)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestWeakMethodProxy)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestWeakList)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/utils/testutils.py b/silx/utils/testutils.py deleted file mode 100644 index 82c2ce3..0000000 --- a/silx/utils/testutils.py +++ /dev/null @@ -1,281 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Utilities for writing tests. - -- :class:`ParametricTestCase` provides a :meth:`TestCase.subTest` replacement - for Python < 3.4 -- :class:`TestLogging` with context or the :func:`test_logging` decorator - enables testing the number of logging messages of different levels. -""" - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "26/01/2018" - - -import contextlib -import functools -import logging -import sys -import unittest - -_logger = logging.getLogger(__name__) - - -if sys.hexversion >= 0x030400F0: # Python >= 3.4 - class ParametricTestCase(unittest.TestCase): - pass -else: - class ParametricTestCase(unittest.TestCase): - """TestCase with subTest support for Python < 3.4. - - Add subTest method to support parametric tests. - API is the same, but behavior differs: - If a subTest fails, the following ones are not run. - """ - - _subtest_msg = None # Class attribute to provide a default value - - @contextlib.contextmanager - def subTest(self, msg=None, **params): - """Use as unittest.TestCase.subTest method in Python >= 3.4.""" - # Format arguments as: '[msg] (key=value, ...)' - param_str = ', '.join(['%s=%s' % (k, v) for k, v in params.items()]) - self._subtest_msg = '[%s] (%s)' % (msg or '', param_str) - yield - self._subtest_msg = None - - def shortDescription(self): - short_desc = super(ParametricTestCase, self).shortDescription() - if self._subtest_msg is not None: - # Append subTest message to shortDescription - short_desc = ' '.join( - [msg for msg in (short_desc, self._subtest_msg) if msg]) - - return short_desc if short_desc else None - - -def parameterize(test_case_class, *args, **kwargs): - """Create a suite containing all tests taken from the given - subclass, passing them the parameters. - - .. code-block:: python - - class TestParameterizedCase(unittest.TestCase): - def __init__(self, methodName='runTest', foo=None): - unittest.TestCase.__init__(self, methodName) - self.foo = foo - - def suite(): - testSuite = unittest.TestSuite() - testSuite.addTest(parameterize(TestParameterizedCase, foo=10)) - testSuite.addTest(parameterize(TestParameterizedCase, foo=50)) - return testSuite - """ - test_loader = unittest.TestLoader() - test_names = test_loader.getTestCaseNames(test_case_class) - suite = unittest.TestSuite() - for name in test_names: - suite.addTest(test_case_class(name, *args, **kwargs)) - return suite - - -class TestLogging(logging.Handler): - """Context checking the number of logging messages from a specified Logger. - - It disables propagation of logging message while running. - - This is meant to be used as a with statement, for example: - - >>> with TestLogging(logger, error=2, warning=0): - >>> pass # Run tests here expecting 2 ERROR and no WARNING from logger - ... - - :param logger: Name or instance of the logger to test. - (Default: root logger) - :type logger: str or :class:`logging.Logger` - :param int critical: Expected number of CRITICAL messages. - Default: Do not check. - :param int error: Expected number of ERROR messages. - Default: Do not check. - :param int warning: Expected number of WARNING messages. - Default: Do not check. - :param int info: Expected number of INFO messages. - Default: Do not check. - :param int debug: Expected number of DEBUG messages. - Default: Do not check. - :param int notset: Expected number of NOTSET messages. - Default: Do not check. - :raises RuntimeError: If the message counts are the expected ones. - """ - - def __init__(self, logger=None, critical=None, error=None, - warning=None, info=None, debug=None, notset=None): - if logger is None: - logger = logging.getLogger() - elif not isinstance(logger, logging.Logger): - logger = logging.getLogger(logger) - self.logger = logger - - self.records = [] - - self.count_by_level = { - logging.CRITICAL: critical, - logging.ERROR: error, - logging.WARNING: warning, - logging.INFO: info, - logging.DEBUG: debug, - logging.NOTSET: notset - } - - super(TestLogging, self).__init__() - - def __enter__(self): - """Context (i.e., with) support""" - self.records = [] # Reset recorded LogRecords - self.logger.addHandler(self) - self.logger.propagate = False - # ensure no log message is ignored - self.entry_level = self.logger.level * 1 - self.logger.setLevel(logging.DEBUG) - - def __exit__(self, exc_type, exc_value, traceback): - """Context (i.e., with) support""" - self.logger.removeHandler(self) - self.logger.propagate = True - self.logger.setLevel(self.entry_level) - - for level, expected_count in self.count_by_level.items(): - if expected_count is None: - continue - - # Number of records for the specified level_str - count = len([r for r in self.records if r.levelno == level]) - if count != expected_count: # That's an error - # Resend record logs through logger as they where masked - # to help debug - for record in self.records: - self.logger.handle(record) - raise RuntimeError( - 'Expected %d %s logging messages, got %d' % ( - expected_count, logging.getLevelName(level), count)) - - def emit(self, record): - """Override :meth:`logging.Handler.emit`""" - self.records.append(record) - - -def test_logging(logger=None, critical=None, error=None, - warning=None, info=None, debug=None, notset=None): - """Decorator checking number of logging messages. - - Propagation of logging messages is disabled by this decorator. - - In case the expected number of logging messages is not found, it raises - a RuntimeError. - - >>> class Test(unittest.TestCase): - ... @test_logging('module_logger_name', error=2, warning=0) - ... def test(self): - ... pass # Test expecting 2 ERROR and 0 WARNING messages - - :param logger: Name or instance of the logger to test. - (Default: root logger) - :type logger: str or :class:`logging.Logger` - :param int critical: Expected number of CRITICAL messages. - Default: Do not check. - :param int error: Expected number of ERROR messages. - Default: Do not check. - :param int warning: Expected number of WARNING messages. - Default: Do not check. - :param int info: Expected number of INFO messages. - Default: Do not check. - :param int debug: Expected number of DEBUG messages. - Default: Do not check. - :param int notset: Expected number of NOTSET messages. - Default: Do not check. - """ - def decorator(func): - test_context = TestLogging(logger, critical, error, - warning, info, debug, notset) - - @functools.wraps(func) - def wrapper(*args, **kwargs): - with test_context: - result = func(*args, **kwargs) - return result - return wrapper - return decorator - - -# Simulate missing library context -class EnsureImportError(object): - """This context manager allows to simulate the unavailability - of a library, even if it is actually available. It ensures that - an ImportError is raised if the code inside the context tries to - import the module. - - It can be used to test that a correct fallback library is used, - or that the expected error code is returned. - - Trivial example:: - - from silx.utils.testutils import EnsureImportError - - with EnsureImportError("h5py"): - try: - import h5py - except ImportError: - print("Good") - - .. note:: - - This context manager does not remove the library from the namespace, - if it is already imported. It only ensures that any attempt to import - it again will cause an ImportError to be raised. - """ - def __init__(self, name): - """ - - :param str name: Name of module to be hidden (e.g. "h5py") - """ - self.module_name = name - - def __enter__(self): - """Simulate failed import by setting sys.modules[name]=None""" - if self.module_name not in sys.modules: - self._delete_on_exit = True - self._backup = None - else: - self._delete_on_exit = False - self._backup = sys.modules[self.module_name] - sys.modules[self.module_name] = None - - def __exit__(self, exc_type, exc_val, exc_tb): - """Restore previous state""" - if self._delete_on_exit: - del sys.modules[self.module_name] - else: - sys.modules[self.module_name] = self._backup diff --git a/silx/utils/weakref.py b/silx/utils/weakref.py deleted file mode 100644 index 06646e8..0000000 --- a/silx/utils/weakref.py +++ /dev/null @@ -1,361 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2018 European Synchrotron Radiation Facility -# -# 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. -# -# ###########################################################################*/ -"""Weakref utils for compatibility between Python 2 and Python 3 or for -extended features. -""" -from __future__ import absolute_import - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "15/09/2016" - - -import weakref -import types -import inspect - - -def ref(object, callback=None): - """Returns a weak reference to object. The original object can be retrieved - by calling the reference object if the referent is still alive. If the - referent is no longer alive, calling the reference object will cause None - to be returned. - - The signature is the same as the standard `weakref` library, but it returns - `WeakMethod` if the object is a bound method. - - :param object: An object - :param func callback: If provided, and the returned weakref object is - still alive, the callback will be called when the object is about to - be finalized. The weak reference object will be passed as the only - parameter to the callback. Then the referent will no longer be - available. - :return: A weak reference to the object - """ - if inspect.ismethod(object): - return WeakMethod(object, callback) - else: - return weakref.ref(object, callback) - - -def proxy(object, callback=None): - """Return a proxy to object which uses a weak reference. This supports use - of the proxy in most contexts instead of requiring the explicit - dereferencing used with weak reference objects. - - The signature is the same as the standard `weakref` library, but it returns - `WeakMethodProxy` if the object is a bound method. - - :param object: An object - :param func callback: If provided, and the returned weakref object is - still alive, the callback will be called when the object is about to - be finalized. The weak reference object will be passed as the only - parameter to the callback. Then the referent will no longer be - available. - :return: A proxy to a weak reference of the object - """ - if inspect.ismethod(object): - return WeakMethodProxy(object, callback) - else: - return weakref.proxy(object, callback) - - -class WeakMethod(object): - """Wraps a callable object like a function or a bound method. - Feature callback when the object is about to be finalized. - Provids the same interface as a normal weak reference. - """ - - def __init__(self, function, callback=None): - """ - Constructor - :param function: Function/method to be called - :param callback: If callback is provided and not None, - and the returned weakref object is still alive, the - callback will be called when the object is about to - be finalized; the weak reference object will be passed - as the only parameter to the callback; the referent will - no longer be available - """ - self.__callback = callback - - if inspect.ismethod(function): - # it is a bound method - self.__obj = weakref.ref(function.__self__, self.__call_callback) - self.__method = weakref.ref(function.__func__, self.__call_callback) - else: - self.__obj = None - self.__method = weakref.ref(function, self.__call_callback) - - def __call_callback(self, ref): - """Called when the object is about to be finalized""" - if not self.is_alive(): - return - self.__obj = None - self.__method = None - if self.__callback is not None: - self.__callback(self) - - def __call__(self): - """Return a callable function or None if the WeakMethod is dead.""" - if self.__obj is not None: - method = self.__method() - obj = self.__obj() - if method is None or obj is None: - return None - return types.MethodType(method, obj) - elif self.__method is not None: - return self.__method() - else: - return None - - def is_alive(self): - """True if the WeakMethod is still alive""" - return self.__method is not None - - def __eq__(self, other): - """Check it another obect is equal to this. - - :param object other: Object to compare with - """ - if isinstance(other, WeakMethod): - if not self.is_alive(): - return False - return self.__obj == other.__obj and self.__method == other.__method - return False - - def __ne__(self, other): - """Check it another obect is not equal to this. - - :param object other: Object to compare with - """ - if isinstance(other, WeakMethod): - if not self.is_alive(): - return False - return self.__obj != other.__obj or self.__method != other.__method - return True - - def __hash__(self): - """Returns the hash for the object.""" - return self.__obj.__hash__() ^ self.__method.__hash__() - - -class WeakMethodProxy(WeakMethod): - """Wraps a callable object like a function or a bound method - with a weakref proxy. - """ - def __call__(self, *args, **kwargs): - """Dereference the method and call it if the method is still alive. - Else raises an ReferenceError. - - :raises: ReferenceError, if the method is not alive - """ - fn = super(WeakMethodProxy, self).__call__() - if fn is None: - raise ReferenceError("weakly-referenced object no longer exists") - return fn(*args, **kwargs) - - -class WeakList(list): - """Manage a list of weaked references. - When an object is dead, the list is flaged as invalid. - If expected the list is cleaned up to remove dead objects. - """ - - def __init__(self, enumerator=()): - """Create a WeakList - - :param iterator enumerator: A list of object to initialize the - list - """ - list.__init__(self) - self.__list = [] - self.__is_valid = True - for obj in enumerator: - self.append(obj) - - def __invalidate(self, ref): - """Flag the list as invalidated. The list contains dead references.""" - self.__is_valid = False - - def __create_ref(self, obj): - """Create a weakref from an object. It uses the `ref` module function. - """ - return ref(obj, self.__invalidate) - - def __clean(self): - """Clean the list from dead references""" - if self.__is_valid: - return - self.__list = [ref for ref in self.__list if ref() is not None] - self.__is_valid = True - - def __iter__(self): - """Iterate over objects of the list""" - for ref in self.__list: - obj = ref() - if obj is not None: - yield obj - - def __len__(self): - """Count item on the list""" - self.__clean() - return len(self.__list) - - def __getitem__(self, key): - """Returns the object at the requested index - - :param key: Indexes to get - :type key: int or slice - """ - self.__clean() - data = self.__list[key] - if isinstance(data, list): - result = [ref() for ref in data] - else: - result = data() - return result - - def __setitem__(self, key, obj): - """Set an item at an index - - :param key: Indexes to set - :type key: int or slice - """ - self.__clean() - if isinstance(key, slice): - objs = [self.__create_ref(o) for o in obj] - self.__list[key] = objs - else: - obj_ref = self.__create_ref(obj) - self.__list[key] = obj_ref - - def __delitem__(self, key): - """Delete an Indexes item of this list - - :param key: Index to delete - :type key: int or slice - """ - self.__clean() - del self.__list[key] - - def __delslice__(self, i, j): - """Looks to be used in Python 2.7""" - self.__delitem__(slice(i, j, None)) - - def __setslice__(self, i, j, sequence): - """Looks to be used in Python 2.7""" - self.__setitem__(slice(i, j, None), sequence) - - def __getslice__(self, i, j): - """Looks to be used in Python 2.7""" - return self.__getitem__(slice(i, j, None)) - - def __reversed__(self): - """Returns a copy of the reverted list""" - reversed_list = reversed(list(self)) - return WeakList(reversed_list) - - def __contains__(self, obj): - """Returns true if the object is in the list""" - ref = self.__create_ref(obj) - return ref in self.__list - - def __add__(self, other): - """Returns a WeakList containing this list an the other""" - l = WeakList(self) - l.extend(other) - return l - - def __iadd__(self, other): - """Add objects to this list inplace""" - self.extend(other) - return self - - def __mul__(self, n): - """Returns a WeakList containing n-duplication object of this list""" - return WeakList(list(self) * n) - - def __imul__(self, n): - """N-duplication of the objects to this list inplace""" - self.__list *= n - return self - - def append(self, obj): - """Add an object at the end of the list""" - ref = self.__create_ref(obj) - self.__list.append(ref) - - def count(self, obj): - """Returns the number of occurencies of an object""" - ref = self.__create_ref(obj) - return self.__list.count(ref) - - def extend(self, other): - """Append the list with all objects from another list""" - for obj in other: - self.append(obj) - - def index(self, obj): - """Returns the index of an object""" - ref = self.__create_ref(obj) - return self.__list.index(ref) - - def insert(self, index, obj): - """Insert an object at the requested index""" - ref = self.__create_ref(obj) - self.__list.insert(index, ref) - - def pop(self, index=-1): - """Remove and return an object at the requested index""" - self.__clean() - obj = self.__list.pop(index)() - return obj - - def remove(self, obj): - """Remove an object from the list""" - ref = self.__create_ref(obj) - self.__list.remove(ref) - - def reverse(self): - """Reverse the list inplace""" - self.__list.reverse() - - def sort(self, key=None, reverse=False): - """Sort the list inplace. - Not very efficient. - """ - sorted_list = list(self) - sorted_list.sort(key=key, reverse=reverse) - self.__list = [] - self.extend(sorted_list) - - def __str__(self): - unref_list = list(self) - return "WeakList(%s)" % str(unref_list) - - def __repr__(self): - unref_list = list(self) - return "WeakList(%s)" % repr(unref_list) |