summaryrefslogtreecommitdiff
path: root/silx/utils
diff options
context:
space:
mode:
Diffstat (limited to 'silx/utils')
-rw-r--r--silx/utils/ExternalResources.py320
-rw-r--r--silx/utils/__init__.py28
-rw-r--r--silx/utils/_have_openmp.pxd49
-rw-r--r--silx/utils/array_like.py596
-rw-r--r--silx/utils/debug.py103
-rw-r--r--silx/utils/deprecation.py123
-rw-r--r--silx/utils/enum.py79
-rw-r--r--silx/utils/exceptions.py33
-rw-r--r--silx/utils/files.py56
-rw-r--r--silx/utils/html.py60
-rw-r--r--silx/utils/include/silx_store_openmp.h10
-rw-r--r--silx/utils/launcher.py295
-rwxr-xr-xsilx/utils/number.py143
-rw-r--r--silx/utils/property.py52
-rw-r--r--silx/utils/proxy.py241
-rw-r--r--silx/utils/retry.py264
-rw-r--r--silx/utils/setup.py43
-rwxr-xr-xsilx/utils/test/__init__.py59
-rw-r--r--silx/utils/test/test_array_like.py445
-rw-r--r--silx/utils/test/test_debug.py99
-rw-r--r--silx/utils/test/test_deprecation.py107
-rw-r--r--silx/utils/test/test_enum.py96
-rw-r--r--silx/utils/test/test_external_resources.py99
-rw-r--r--silx/utils/test/test_html.py61
-rw-r--r--silx/utils/test/test_launcher.py204
-rw-r--r--silx/utils/test/test_launcher_command.py47
-rw-r--r--silx/utils/test/test_number.py186
-rw-r--r--silx/utils/test/test_proxy.py344
-rw-r--r--silx/utils/test/test_retry.py179
-rwxr-xr-xsilx/utils/test/test_testutils.py105
-rw-r--r--silx/utils/test/test_weakref.py330
-rwxr-xr-xsilx/utils/testutils.py333
-rw-r--r--silx/utils/weakref.py361
33 files changed, 0 insertions, 5550 deletions
diff --git a/silx/utils/ExternalResources.py b/silx/utils/ExternalResources.py
deleted file mode 100644
index e21381c..0000000
--- a/silx/utils/ExternalResources.py
+++ /dev/null
@@ -1,320 +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.
-#
-# ###########################################################################*/
-"""Helper to access to external resources.
-"""
-
-__authors__ = ["Thomas Vincent", "J. Kieffer"]
-__license__ = "MIT"
-__date__ = "08/03/2019"
-
-
-import os
-import threading
-import json
-import logging
-import tempfile
-import unittest
-import six
-
-logger = logging.getLogger(__name__)
-
-
-class ExternalResources(object):
- """Utility class which allows to download test-data from www.silx.org
- and manage the temporary data during the tests.
-
- """
-
- def __init__(self, project,
- url_base,
- env_key=None,
- timeout=60):
- """Constructor of the class
-
- :param str project: name of the project, like "silx"
- :param str url_base: base URL for the data, like "http://www.silx.org/pub"
- :param str env_key: name of the environment variable which contains the
- test_data directory, like "SILX_DATA".
- If None (default), then the name of the
- environment variable is built from the project argument:
- "<PROJECT>_DATA".
- The environment variable is optional: in case it is not set,
- a directory in the temporary folder is used.
- :param timeout: time in seconds before it breaks
- """
- self.project = project
- self._initialized = False
- self.sem = threading.Semaphore()
-
- self.env_key = env_key or (self.project.upper() + "_TESTDATA")
- self.url_base = url_base
- self.all_data = set()
- self.timeout = timeout
- self._data_home = None
-
- @property
- def data_home(self):
- """Returns the data_home path and make sure it exists in the file
- system."""
- if self._data_home is not None:
- return self._data_home
-
- data_home = os.environ.get(self.env_key)
- if data_home is None:
- try:
- import getpass
- name = getpass.getuser()
- except Exception:
- if "getlogin" in dir(os):
- name = os.getlogin()
- elif "USER" in os.environ:
- name = os.environ["USER"]
- elif "USERNAME" in os.environ:
- name = os.environ["USERNAME"]
- else:
- name = "uid" + str(os.getuid())
-
- basename = "%s_testdata_%s" % (self.project, name)
- data_home = os.path.join(tempfile.gettempdir(), basename)
- if not os.path.exists(data_home):
- os.makedirs(data_home)
- self._data_home = data_home
- return data_home
-
- def _initialize_data(self):
- """Initialize for downloading test data"""
- if not self._initialized:
- with self.sem:
- if not self._initialized:
- self.testdata = os.path.join(self.data_home, "all_testdata.json")
- if os.path.exists(self.testdata):
- with open(self.testdata) as f:
- self.all_data = set(json.load(f))
- self._initialized = True
-
- def clean_up(self):
- pass
-
- def getfile(self, filename):
- """Downloads the requested file from web-server available
- at https://www.silx.org/pub/silx/
-
- :param: relative name of the image.
- :return: full path of the locally saved file.
- """
- logger.debug("ExternalResources.getfile('%s')", filename)
-
- if not self._initialized:
- self._initialize_data()
-
- fullfilename = os.path.abspath(os.path.join(self.data_home, filename))
-
- if not os.path.isfile(fullfilename):
- logger.debug("Trying to download image %s, timeout set to %ss",
- filename, self.timeout)
- dictProxies = {}
- if "http_proxy" in os.environ:
- dictProxies['http'] = os.environ["http_proxy"]
- dictProxies['https'] = os.environ["http_proxy"]
- if "https_proxy" in os.environ:
- dictProxies['https'] = os.environ["https_proxy"]
- if dictProxies:
- proxy_handler = six.moves.urllib.request.ProxyHandler(dictProxies)
- opener = six.moves.urllib.request.build_opener(proxy_handler).open
- else:
- opener = six.moves.urllib.request.urlopen
-
- logger.debug("wget %s/%s", self.url_base, filename)
- try:
- data = opener("%s/%s" % (self.url_base, filename),
- data=None, timeout=self.timeout).read()
- logger.info("Image %s successfully downloaded.", filename)
- except six.moves.urllib.error.URLError:
- raise unittest.SkipTest("network unreachable.")
-
- if not os.path.isdir(os.path.dirname(fullfilename)):
- # Create sub-directory if needed
- os.makedirs(os.path.dirname(fullfilename))
-
- try:
- with open(fullfilename, "wb") as outfile:
- outfile.write(data)
- except IOError:
- raise IOError("unable to write downloaded \
- data to disk at %s" % self.data_home)
-
- if not os.path.isfile(fullfilename):
- raise RuntimeError(
- """Could not automatically download test images %s!
- If you are behind a firewall, please set both environment variable http_proxy and https_proxy.
- This even works under windows !
- Otherwise please try to download the images manually from
- %s/%s""" % (filename, self.url_base, filename))
-
- if filename not in self.all_data:
- self.all_data.add(filename)
- image_list = list(self.all_data)
- image_list.sort()
- try:
- with open(self.testdata, "w") as fp:
- json.dump(image_list, fp, indent=4)
- except IOError:
- logger.debug("Unable to save JSON list")
-
- return fullfilename
-
- def getdir(self, dirname):
- """Downloads the requested tarball from the server
- https://www.silx.org/pub/silx/
- and unzips it into the data directory
-
- :param: relative name of the image.
- :return: list of files with their full path.
- """
- lodn = dirname.lower()
- if (lodn.endswith("tar") or lodn.endswith("tgz") or
- lodn.endswith("tbz2") or lodn.endswith("tar.gz") or
- lodn.endswith("tar.bz2")):
- import tarfile
- engine = tarfile.TarFile.open
- elif lodn.endswith("zip"):
- import zipfile
- engine = zipfile.ZipFile
- else:
- raise RuntimeError("Unsupported archive format. Only tar and zip "
- "are currently supported")
- full_path = self.getfile(dirname)
- with engine(full_path, mode="r") as fd:
- output = os.path.join(self.data_home, dirname + "__content")
- fd.extractall(output)
- if lodn.endswith("zip"):
- result = [os.path.join(output, i) for i in fd.namelist()]
- else:
- result = [os.path.join(output, i) for i in fd.getnames()]
- return result
-
- def get_file_and_repack(self, filename):
- """
- Download the requested file, decompress and repack it to bz2 and gz.
-
- :param str filename: name of the image.
- :rtype: str
- :return: full path of the locally saved file
- """
- if not self._initialized:
- self._initialize_data()
- if filename not in self.all_data:
- self.all_data.add(filename)
- image_list = list(self.all_data)
- image_list.sort()
- try:
- with open(self.testdata, "w") as fp:
- json.dump(image_list, fp, indent=4)
- except IOError:
- logger.debug("Unable to save JSON list")
- baseimage = os.path.basename(filename)
- logger.info("UtilsTest.getimage('%s')" % baseimage)
-
- if not os.path.exists(self.data_home):
- os.makedirs(self.data_home)
- fullimagename = os.path.abspath(os.path.join(self.data_home, baseimage))
-
- if baseimage.endswith(".bz2"):
- bzip2name = baseimage
- basename = baseimage[:-4]
- gzipname = basename + ".gz"
- elif baseimage.endswith(".gz"):
- gzipname = baseimage
- basename = baseimage[:-3]
- bzip2name = basename + ".bz2"
- else:
- basename = baseimage
- gzipname = baseimage + "gz2"
- bzip2name = basename + ".bz2"
-
- fullimagename_gz = os.path.abspath(os.path.join(self.data_home, gzipname))
- fullimagename_raw = os.path.abspath(os.path.join(self.data_home, basename))
- fullimagename_bz2 = os.path.abspath(os.path.join(self.data_home, bzip2name))
-
- # The files are recreated from the bz2 file
- if not os.path.isfile(fullimagename_bz2):
- self.getfile(bzip2name)
- if not os.path.isfile(fullimagename_bz2):
- raise RuntimeError(
- """Could not automatically download test images %s!
- If you are behind a firewall, please set the environment variable http_proxy.
- Otherwise please try to download the images manually from
- %s""" % (self.url_base, filename))
-
- try:
- import bz2
- except ImportError:
- raise RuntimeError("bz2 library is needed to decompress data")
- try:
- import gzip
- except ImportError:
- gzip = None
-
- raw_file_exists = os.path.isfile(fullimagename_raw)
- gz_file_exists = os.path.isfile(fullimagename_gz)
- if not raw_file_exists or not gz_file_exists:
- with open(fullimagename_bz2, "rb") as f:
- data = f.read()
- decompressed = bz2.decompress(data)
-
- if not raw_file_exists:
- try:
- with open(fullimagename_raw, "wb") as fullimage:
- fullimage.write(decompressed)
- except IOError:
- raise IOError("unable to write decompressed \
- data to disk at %s" % self.data_home)
-
- if not gz_file_exists:
- if gzip is None:
- raise RuntimeError("gzip library is expected to recompress data")
- try:
- gzip.open(fullimagename_gz, "wb").write(decompressed)
- except IOError:
- raise IOError("unable to write gzipped \
- data to disk at %s" % self.data_home)
-
- return fullimagename
-
- def download_all(self, imgs=None):
- """Download all data needed for the test/benchmarks
-
- :param imgs: list of files to download, by default all
- :return: list of path with all files
- """
- if not self._initialized:
- self._initialize_data()
- if not imgs:
- imgs = self.all_data
- res = []
- for fn in imgs:
- logger.info("Downloading from silx.org: %s", fn)
- res.append(self.getfile(fn))
- return res
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.pxd b/silx/utils/_have_openmp.pxd
deleted file mode 100644
index 40a2857..0000000
--- a/silx/utils/_have_openmp.pxd
+++ /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 1a2e72e..0000000
--- a/silx/utils/array_like.py
+++ /dev/null
@@ -1,596 +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 sys
-
-import numpy
-import six
-import numbers
-
-__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, numbers.Integral):
- 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 5459448..0000000
--- a/silx/utils/debug.py
+++ /dev/null
@@ -1,103 +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
-
-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 7b19ee5..0000000
--- a/silx/utils/deprecation.py
+++ /dev/null
@@ -1,123 +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([])
-
-FORCE = False
-"""If true, deprecation using only_once are also generated.
-It is needed for reproducible tests.
-"""
-
-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 not FORCE and 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/enum.py b/silx/utils/enum.py
deleted file mode 100644
index fece575..0000000
--- a/silx/utils/enum.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2019 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.
-#
-# ###########################################################################*/
-"""An :class:`.Enum` class with additional features."""
-
-from __future__ import absolute_import
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "29/04/2019"
-
-
-import enum
-
-
-class Enum(enum.Enum):
- """Enum with additional class methods."""
-
- @classmethod
- def from_value(cls, value):
- """Convert a value to corresponding Enum member
-
- :param value: The value to compare to Enum members
- If it is already a member of Enum, it is returned directly.
- :return: The corresponding enum member
- :rtype: Enum
- :raise ValueError: In case the conversion is not possible
- """
- if isinstance(value, cls):
- return value
- for member in cls:
- if value == member.value:
- return member
- raise ValueError("Cannot convert: %s" % value)
-
- @classmethod
- def members(cls):
- """Returns a tuple of all members.
-
- :rtype: Tuple[Enum]
- """
- return tuple(member for member in cls)
-
- @classmethod
- def names(cls):
- """Returns a tuple of all member names.
-
- :rtype: Tuple[str]
- """
- return tuple(member.name for member in cls)
-
- @classmethod
- def values(cls):
- """Returns a tuple of all member values.
-
- :rtype: Tuple
- """
- return tuple(member.value for member in cls)
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/files.py b/silx/utils/files.py
deleted file mode 100644
index 1982c0d..0000000
--- a/silx/utils/files.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2019 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 files
-"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "19/09/2016"
-
-import os.path
-import glob
-
-def expand_filenames(filenames):
- """
- Takes a list of paths and expand it into a list of files.
-
- :param List[str] filenames: list of filenames or path with wildcards
- :rtype: List[str]
- :return: list of existing filenames or non-existing files
- (which was provided as input)
- """
- result = []
- for filename in filenames:
- if os.path.exists(filename):
- result.append(filename)
- elif glob.has_magic(filename):
- expanded_filenames = glob.glob(filename)
- if expanded_filenames:
- result += expanded_filenames
- else: # Cannot expand, add as is
- result.append(filename)
- else:
- result.append(filename)
- return result
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>")
- >>> "&lt;html&gt;"
-
- .. 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("&", "&amp;") # must be done first
- string = string.replace("<", "&lt;")
- string = string.replace(">", "&gt;")
- if quote:
- string = string.replace("'", "&apos;")
- string = string.replace("\"", "&quot;")
- 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 100755
index f852a39..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 try to return the smallest numerical type to use for
- a safe conversion. It has some known issues: precission loss.
-
- :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 * 1.2
- 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 8711799..0000000
--- a/silx/utils/proxy.py
+++ /dev/null
@@ -1,241 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2019 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"
-
-
-import functools
-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__)
-
-
-def _docstring(dest, origin):
- """Implementation of docstring decorator.
-
- It patches dest.__doc__.
- """
- if not isinstance(dest, type) and isinstance(origin, type):
- # func is not a class, but origin is, get the method with the same name
- try:
- origin = getattr(origin, dest.__name__)
- except AttributeError:
- raise ValueError(
- "origin class has no %s method" % dest.__name__)
-
- dest.__doc__ = origin.__doc__
- return dest
-
-
-def docstring(origin):
- """Decorator to initialize the docstring from another source.
-
- This is useful to duplicate a docstring for inheritance and composition.
-
- If origin is a method or a function, it copies its docstring.
- If origin is a class, the docstring is copied from the method
- of that class which has the same name as the method/function
- being decorated.
-
- :param origin:
- The method, function or class from which to get the docstring
- :raises ValueError:
- If the origin class has not method n case the
- """
- return functools.partial(_docstring, origin=origin)
diff --git a/silx/utils/retry.py b/silx/utils/retry.py
deleted file mode 100644
index adc43bc..0000000
--- a/silx/utils/retry.py
+++ /dev/null
@@ -1,264 +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.
-#
-# ############################################################################*/
-"""
-This module provides utility methods for retrying methods until they
-no longer fail.
-"""
-
-__authors__ = ["W. de Nolf"]
-__license__ = "MIT"
-__date__ = "05/02/2020"
-
-
-import time
-from functools import wraps
-from contextlib import contextmanager
-import multiprocessing
-from queue import Empty
-
-
-RETRY_PERIOD = 0.01
-
-
-class RetryTimeoutError(TimeoutError):
- pass
-
-
-class RetryError(RuntimeError):
- pass
-
-
-def _default_retry_on_error(e):
- """
- :param BaseException e:
- :returns bool:
- """
- return isinstance(e, RetryError)
-
-
-@contextmanager
-def _handle_exception(options):
- try:
- yield
- except BaseException as e:
- retry_on_error = options.get("retry_on_error")
- if retry_on_error is not None and retry_on_error(e):
- options["exception"] = e
- else:
- raise
-
-
-def _retry_loop(retry_timeout=None, retry_period=None, retry_on_error=None):
- """Iterator which is endless or ends with an RetryTimeoutError.
- It yields a dictionary which can be used to influence the loop.
-
- :param num retry_timeout:
- :param num retry_period: sleep before retry
- :param callable or None retry_on_error: checks whether an exception is
- eligible for retry
- """
- has_timeout = retry_timeout is not None
- options = {"exception": None, "retry_on_error": retry_on_error}
- if has_timeout:
- t0 = time.time()
- while True:
- yield options
- if retry_period is not None:
- time.sleep(retry_period)
- if has_timeout and (time.time() - t0) > retry_timeout:
- raise RetryTimeoutError from options.get("exception")
-
-
-def retry(
- retry_timeout=None, retry_period=None, retry_on_error=_default_retry_on_error
-):
- """Decorator for a method that needs to be executed until it not longer
- fails or until `retry_on_error` returns False.
-
- The decorator arguments can be overriden by using them when calling the
- decorated method.
-
- :param num retry_timeout:
- :param num retry_period: sleep before retry
- :param callable or None retry_on_error: checks whether an exception is
- eligible for retry
- """
-
- if retry_period is None:
- retry_period = RETRY_PERIOD
-
- def decorator(method):
- @wraps(method)
- def wrapper(*args, **kw):
- _retry_timeout = kw.pop("retry_timeout", retry_timeout)
- _retry_period = kw.pop("retry_period", retry_period)
- _retry_on_error = kw.pop("retry_on_error", retry_on_error)
- for options in _retry_loop(
- retry_timeout=_retry_timeout,
- retry_period=_retry_period,
- retry_on_error=_retry_on_error,
- ):
- with _handle_exception(options):
- return method(*args, **kw)
-
- return wrapper
-
- return decorator
-
-
-def retry_contextmanager(
- retry_timeout=None, retry_period=None, retry_on_error=_default_retry_on_error
-):
- """Decorator to make a context manager from a method that needs to be
- entered until it no longer fails or until `retry_on_error` returns False.
-
- The decorator arguments can be overriden by using them when calling the
- decorated method.
-
- :param num retry_timeout:
- :param num retry_period: sleep before retry
- :param callable or None retry_on_error: checks whether an exception is
- eligible for retry
- """
-
- if retry_period is None:
- retry_period = RETRY_PERIOD
-
- def decorator(method):
- @wraps(method)
- def wrapper(*args, **kw):
- _retry_timeout = kw.pop("retry_timeout", retry_timeout)
- _retry_period = kw.pop("retry_period", retry_period)
- _retry_on_error = kw.pop("retry_on_error", retry_on_error)
- for options in _retry_loop(
- retry_timeout=_retry_timeout,
- retry_period=_retry_period,
- retry_on_error=_retry_on_error,
- ):
- with _handle_exception(options):
- gen = method(*args, **kw)
- result = next(gen)
- options["retry_on_error"] = None
- yield result
- try:
- next(gen)
- except StopIteration:
- return
- else:
- raise RuntimeError(str(method) + " should only yield once")
-
- return contextmanager(wrapper)
-
- return decorator
-
-
-def _subprocess_main(queue, method, retry_on_error, *args, **kw):
- try:
- result = method(*args, **kw)
- except BaseException as e:
- if retry_on_error(e):
- # As the traceback gets lost, make sure the top-level
- # exception is RetryError
- e = RetryError(str(e))
- queue.put(e)
- else:
- queue.put(result)
-
-
-def retry_in_subprocess(
- retry_timeout=None, retry_period=None, retry_on_error=_default_retry_on_error
-):
- """Same as `retry` but it also retries segmentation faults.
-
- As subprocesses are spawned, you cannot use this decorator with the "@" syntax
- because the decorated method needs to be an attribute of a module:
-
- .. code-block:: python
-
- def _method(*args, **kw):
- ...
-
- method = retry_in_subprocess()(_method)
-
- :param num retry_timeout:
- :param num retry_period: sleep before retry
- :param callable or None retry_on_error: checks whether an exception is
- eligible for retry
- """
-
- if retry_period is None:
- retry_period = RETRY_PERIOD
-
- def decorator(method):
- @wraps(method)
- def wrapper(*args, **kw):
- _retry_timeout = kw.pop("retry_timeout", retry_timeout)
- _retry_period = kw.pop("retry_period", retry_period)
- _retry_on_error = kw.pop("retry_on_error", retry_on_error)
-
- ctx = multiprocessing.get_context("spawn")
-
- def start_subprocess():
- queue = ctx.Queue(maxsize=1)
- p = ctx.Process(
- target=_subprocess_main,
- args=(queue, method, retry_on_error) + args,
- kwargs=kw,
- )
- p.start()
- return p, queue
-
- def stop_subprocess(p):
- try:
- p.kill()
- except AttributeError:
- p.terminate()
- p.join()
-
- p, queue = start_subprocess()
- try:
- for options in _retry_loop(
- retry_timeout=_retry_timeout, retry_on_error=_retry_on_error
- ):
- with _handle_exception(options):
- if not p.is_alive():
- p, queue = start_subprocess()
- try:
- result = queue.get(block=True, timeout=_retry_period)
- except Empty:
- pass
- except ValueError:
- pass
- else:
- if isinstance(result, BaseException):
- stop_subprocess(p)
- raise result
- else:
- return result
- finally:
- stop_subprocess(p)
-
- return wrapper
-
- return decorator
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 100755
index b35feee..0000000
--- a/silx/utils/test/__init__.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2019 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__ = "08/03/2019"
-
-
-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
-from . import test_external_resources
-from . import test_enum
-from . import test_testutils
-from . import test_retry
-
-
-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())
- test_suite.addTest(test_external_resources.suite())
- test_suite.addTest(test_enum.suite())
- test_suite.addTest(test_testutils.suite())
- test_suite.addTest(test_retry.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 fe92db5..0000000
--- a/silx/utils/test/test_array_like.py
+++ /dev/null
@@ -1,445 +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"
-
-import h5py
-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
-
-
-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))
-
- 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_enum.py b/silx/utils/test/test_enum.py
deleted file mode 100644
index a72da46..0000000
--- a/silx/utils/test/test_enum.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2019 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 of Enum class with extra class methods"""
-
-from __future__ import absolute_import
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "29/04/2019"
-
-
-import sys
-import unittest
-
-import enum
-from silx.utils.enum import Enum
-
-
-class TestEnum(unittest.TestCase):
- """Tests for enum module."""
-
- def test(self):
- """Test with Enum"""
- class Success(Enum):
- A = 1
- B = 'B'
- self._check_enum_content(Success)
-
- @unittest.skipIf(sys.version_info.major <= 2, 'Python3 only')
- def test(self):
- """Test Enum with member redefinition"""
- with self.assertRaises(TypeError):
- class Failure(Enum):
- A = 1
- A = 'B'
-
- def test_unique(self):
- """Test with enum.unique"""
- with self.assertRaises(ValueError):
- @enum.unique
- class Failure(Enum):
- A = 1
- B = 1
-
- @enum.unique
- class Success(Enum):
- A = 1
- B = 'B'
- self._check_enum_content(Success)
-
- def _check_enum_content(self, enum_):
- """Check that the content of an enum is: <A: 1, B: 2>.
-
- :param Enum enum_:
- """
- self.assertEqual(enum_.members(), (enum_.A, enum_.B))
- self.assertEqual(enum_.names(), ('A', 'B'))
- self.assertEqual(enum_.values(), (1, 'B'))
-
- self.assertEqual(enum_.from_value(1), enum_.A)
- self.assertEqual(enum_.from_value('B'), enum_.B)
- with self.assertRaises(ValueError):
- enum_.from_value(3)
-
-
-def suite():
- test_suite = unittest.TestSuite()
- loadTests = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite.addTest(loadTests(TestEnum))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/utils/test/test_external_resources.py b/silx/utils/test/test_external_resources.py
deleted file mode 100644
index 8576029..0000000
--- a/silx/utils/test/test_external_resources.py
+++ /dev/null
@@ -1,99 +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.
-#
-# ###########################################################################*/
-"""Test for resource files management."""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "08/03/2019"
-
-
-import os
-import unittest
-import shutil
-import socket
-import six
-
-from silx.utils.ExternalResources import ExternalResources
-
-
-def isSilxWebsiteAvailable():
- try:
- six.moves.urllib.request.urlopen('http://www.silx.org', timeout=1)
- return True
- except six.moves.urllib.error.URLError:
- return False
- except socket.timeout:
- # This exception is still received in Python 2.7
- return False
-
-
-class TestExternalResources(unittest.TestCase):
- """This is a test for the ExternalResources"""
-
- @classmethod
- def setUpClass(cls):
- if not isSilxWebsiteAvailable():
- raise unittest.SkipTest("Network or silx website not available")
-
- def setUp(self):
- self.resources = ExternalResources("toto", "http://www.silx.org/pub/silx/")
-
- def tearDown(self):
- if self.resources.data_home:
- shutil.rmtree(self.resources.data_home)
- self.resources = None
-
- def test_download(self):
- "test the download from silx.org"
- f = self.resources.getfile("lena.png")
- self.assertTrue(os.path.exists(f))
- di = self.resources.getdir("source.tar.gz")
- for fi in di:
- self.assertTrue(os.path.exists(fi))
-
- def test_download_all(self):
- "test the download of all files from silx.org"
- filename = self.resources.getfile("lena.png")
- directory = "source.tar.gz"
- filelist = self.resources.getdir(directory)
- # download file and remove it to create a json mapping file
- os.remove(filename)
- directory_path = os.path.commonprefix(filelist)
- # Make sure we will rmtree a dangerous path like "/"
- self.assertIn(self.resources.data_home, directory_path)
- shutil.rmtree(directory_path)
- filelist = self.resources.download_all()
- self.assertGreater(len(filelist), 1, "At least 2 items were downloaded")
-
-
-def suite():
- loadTests = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loadTests(TestExternalResources))
- 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("&lt;html&gt;&apos;&quot;", result)
-
- def testLtAmpGt(self):
- # '&' have to be escaped first
- result = html.escape("<&>")
- self.assertEqual("&lt;&amp;&gt;", result)
-
- def testNoQuotes(self):
- result = html.escape("\"m&m's\"", quote=False)
- self.assertEqual("\"m&amp;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 4ac9636..0000000
--- a/silx/utils/test/test_number.py
+++ /dev/null
@@ -1,186 +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 testFloat32(self):
- dtype = number.min_numerical_convertible_type("-23.172")
- self.assertEqual(dtype, numpy.float32)
-
- 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 does not fit even in a 128 bits mantissa
- value = "1.0340282366920938463463374607431768211456"
- 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 72b4d21..0000000
--- a/silx/utils/test/test_proxy.py
+++ /dev/null
@@ -1,344 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2019 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 silx.utils.proxy import Proxy, docstring
-
-
-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)
-
-
-class TestDocstring(unittest.TestCase):
- """Test docstring decorator"""
-
- class Base(object):
- def method(self):
- """Docstring"""
- pass
-
- def test_inheritance(self):
- class Derived(TestDocstring.Base):
- @docstring(TestDocstring.Base)
- def method(self):
- pass
-
- self.assertEqual(Derived.method.__doc__,
- TestDocstring.Base.method.__doc__)
-
- def test_composition(self):
- class Composed(object):
- def __init__(self):
- self._base = TestDocstring.Base()
-
- @docstring(TestDocstring.Base)
- def method(self):
- return self._base.method()
-
- @docstring(TestDocstring.Base.method)
- def renamed(self):
- return self._base.method()
-
- self.assertEqual(Composed.method.__doc__,
- TestDocstring.Base.method.__doc__)
-
- self.assertEqual(Composed.renamed.__doc__,
- TestDocstring.Base.method.__doc__)
-
- def test_function(self):
- def f():
- """Docstring"""
- pass
-
- @docstring(f)
- def g():
- pass
-
- self.assertEqual(f.__doc__, g.__doc__)
-
-
-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))
- test_suite.addTest(loadTests(TestDocstring))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/utils/test/test_retry.py b/silx/utils/test/test_retry.py
deleted file mode 100644
index d223f44..0000000
--- a/silx/utils/test/test_retry.py
+++ /dev/null
@@ -1,179 +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 retry utilities"""
-
-__authors__ = ["W. de Nolf"]
-__license__ = "MIT"
-__date__ = "05/02/2020"
-
-
-import unittest
-import os
-import sys
-import time
-import tempfile
-
-from .. import retry
-
-
-def _cause_segfault():
- import ctypes
-
- i = ctypes.c_char(b"a")
- j = ctypes.pointer(i)
- c = 0
- while True:
- j[c] = b"a"
- c += 1
-
-
-def _submain(filename, kwcheck=None, ncausefailure=0, faildelay=0):
- assert filename
- assert kwcheck
- sys.stderr = open(os.devnull, "w")
-
- with open(filename, mode="r") as f:
- failcounter = int(f.readline().strip())
-
- if failcounter < ncausefailure:
- time.sleep(faildelay)
- failcounter += 1
- with open(filename, mode="w") as f:
- f.write(str(failcounter))
- if failcounter % 2:
- raise retry.RetryError
- else:
- _cause_segfault()
- return True
-
-
-_wsubmain = retry.retry_in_subprocess()(_submain)
-
-
-class TestRetry(unittest.TestCase):
- def setUp(self):
- self.test_dir = tempfile.mkdtemp()
- self.ctr_file = os.path.join(self.test_dir, "failcounter.txt")
-
- def tearDown(self):
- if os.path.exists(self.ctr_file):
- os.unlink(self.ctr_file)
- os.rmdir(self.test_dir)
-
- def test_retry(self):
- ncausefailure = 3
- faildelay = 0.1
- sufficient_timeout = ncausefailure * (faildelay + 10)
- insufficient_timeout = ncausefailure * faildelay * 0.5
-
- @retry.retry()
- def method(check, kwcheck=None):
- assert check
- assert kwcheck
- nonlocal failcounter
- if failcounter < ncausefailure:
- time.sleep(faildelay)
- failcounter += 1
- raise retry.RetryError
- return True
-
- failcounter = 0
- kw = {
- "kwcheck": True,
- "retry_timeout": sufficient_timeout,
- }
- self.assertTrue(method(True, **kw))
-
- failcounter = 0
- kw = {
- "kwcheck": True,
- "retry_timeout": insufficient_timeout,
- }
- with self.assertRaises(retry.RetryTimeoutError):
- method(True, **kw)
-
- def test_retry_contextmanager(self):
- ncausefailure = 3
- faildelay = 0.1
- sufficient_timeout = ncausefailure * (faildelay + 10)
- insufficient_timeout = ncausefailure * faildelay * 0.5
-
- @retry.retry_contextmanager()
- def context(check, kwcheck=None):
- assert check
- assert kwcheck
- nonlocal failcounter
- if failcounter < ncausefailure:
- time.sleep(faildelay)
- failcounter += 1
- raise retry.RetryError
- yield True
-
- failcounter = 0
- kw = {"kwcheck": True, "retry_timeout": sufficient_timeout}
- with context(True, **kw) as result:
- self.assertTrue(result)
-
- failcounter = 0
- kw = {"kwcheck": True, "retry_timeout": insufficient_timeout}
- with self.assertRaises(retry.RetryTimeoutError):
- with context(True, **kw) as result:
- pass
-
- def test_retry_in_subprocess(self):
- ncausefailure = 3
- faildelay = 0.1
- sufficient_timeout = ncausefailure * (faildelay + 10)
- insufficient_timeout = ncausefailure * faildelay * 0.5
-
- kw = {
- "ncausefailure": ncausefailure,
- "faildelay": faildelay,
- "kwcheck": True,
- "retry_timeout": sufficient_timeout,
- }
- with open(self.ctr_file, mode="w") as f:
- f.write("0")
- self.assertTrue(_wsubmain(self.ctr_file, **kw))
-
- kw = {
- "ncausefailure": ncausefailure,
- "faildelay": faildelay,
- "kwcheck": True,
- "retry_timeout": insufficient_timeout,
- }
- with open(self.ctr_file, mode="w") as f:
- f.write("0")
- with self.assertRaises(retry.RetryTimeoutError):
- _wsubmain(self.ctr_file, **kw)
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestRetry))
- return test_suite
-
-
-if __name__ == "__main__":
- unittest.main(defaultTest="suite")
diff --git a/silx/utils/test/test_testutils.py b/silx/utils/test/test_testutils.py
deleted file mode 100755
index c72a3d8..0000000
--- a/silx/utils/test/test_testutils.py
+++ /dev/null
@@ -1,105 +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 testutils module"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "18/11/2019"
-
-
-import unittest
-import logging
-from .. import testutils
-
-
-class TestTestLogging(unittest.TestCase):
- """Tests for TestLogging."""
-
- def testRight(self):
- logger = logging.getLogger(__name__ + "testRight")
- listener = testutils.TestLogging(logger, error=1)
- with listener:
- logger.error("expected")
- logger.info("ignored")
-
- def testCustomLevel(self):
- logger = logging.getLogger(__name__ + "testCustomLevel")
- listener = testutils.TestLogging(logger, error=1)
- with listener:
- logger.error("expected")
- logger.log(666, "custom level have to be ignored")
-
- def testWrong(self):
- logger = logging.getLogger(__name__ + "testWrong")
- listener = testutils.TestLogging(logger, error=1)
- with self.assertRaises(RuntimeError):
- with listener:
- logger.error("expected")
- logger.error("not expected")
-
- def testManyErrors(self):
- logger = logging.getLogger(__name__ + "testManyErrors")
- listener = testutils.TestLogging(logger, error=1, warning=2)
- with self.assertRaises(RuntimeError):
- with listener:
- pass
-
- def testCanBeChecked(self):
- logger = logging.getLogger(__name__ + "testCanBreak")
- listener = testutils.TestLogging(logger, error=1, warning=2)
- with self.assertRaises(RuntimeError):
- with listener:
- logger.error("aaa")
- logger.warning("aaa")
- self.assertFalse(listener.can_be_checked())
- logger.error("aaa")
- # Here we know that it's already wrong without a big cost
- self.assertTrue(listener.can_be_checked())
-
- def testWithAs(self):
- logger = logging.getLogger(__name__ + "testCanBreak")
- with testutils.TestLogging(logger) as listener:
- logger.error("aaa")
- self.assertIsNotNone(listener)
-
- def testErrorMessage(self):
- logger = logging.getLogger(__name__ + "testCanBreak")
- listener = testutils.TestLogging(logger, error=1, warning=2)
- with self.assertRaisesRegex(RuntimeError, "aaabbb"):
- with listener:
- logger.error("aaa")
- logger.warning("aaabbb")
- logger.error("aaa")
-
-
-def suite():
- loadTests = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite = unittest.TestSuite()
- test_suite.addTest(loadTests(TestTestLogging))
- 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 001193d..0000000
--- a/silx/utils/test/test_weakref.py
+++ /dev/null
@@ -1,330 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2019 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.assertNotEqual(self.list.__str__(), "[]")
-
- def testRepr(self):
- self.assertNotEqual(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 100755
index 434beee..0000000
--- a/silx/utils/testutils.py
+++ /dev/null
@@ -1,333 +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 LoggingRuntimeError(RuntimeError):
- """Raised when the `TestLogging` fails"""
-
- def __init__(self, msg, records):
- super(LoggingRuntimeError, self).__init__(msg)
- self.records = records
-
- def __str__(self):
- return super(LoggingRuntimeError, self).__str__() + " -> " + str(self.records)
-
-
-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.expected_count_by_level = {
- logging.CRITICAL: critical,
- logging.ERROR: error,
- logging.WARNING: warning,
- logging.INFO: info,
- logging.DEBUG: debug,
- logging.NOTSET: notset
- }
-
- self._expected_count = sum([v for k, v in self.expected_count_by_level.items() if v is not None])
- """Amount of any logging expected"""
-
- 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)
- self.entry_disabled = self.logger.disabled
- self.logger.disabled = False
- return self
-
- def can_be_checked(self):
- """Returns True if this listener have received enough messages to
- be valid, and then checked.
-
- This can be useful for asynchronous wait of messages. It allows process
- an early break, instead of waiting much time in an active loop.
- """
- return len(self.records) >= self._expected_count
-
- def get_count_by_level(self):
- """Returns the current message count by level.
- """
- count = {
- logging.CRITICAL: 0,
- logging.ERROR: 0,
- logging.WARNING: 0,
- logging.INFO: 0,
- logging.DEBUG: 0,
- logging.NOTSET: 0
- }
- for record in self.records:
- level = record.levelno
- if level in count:
- count[level] = count[level] + 1
- return count
-
- 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)
- self.logger.disabled = self.entry_disabled
-
- count_by_level = self.get_count_by_level()
-
- # Remove keys which does not matter
- ignored = [r for r, v in self.expected_count_by_level.items() if v is None]
- expected_count_by_level = dict(self.expected_count_by_level)
- for i in ignored:
- del count_by_level[i]
- del expected_count_by_level[i]
-
- if count_by_level != expected_count_by_level:
- # Re-send record logs through logger as they where masked
- # to help debug
- message = ""
- for level in count_by_level.keys():
- if message != "":
- message += ", "
- count = count_by_level[level]
- expected_count = expected_count_by_level[level]
- message += "%d %s (got %d)" % (expected_count, logging.getLevelName(level), count)
-
- raise LoggingRuntimeError(
- 'Expected %s' % message, records=list(self.records))
-
- 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)