summaryrefslogtreecommitdiff
path: root/silx/utils
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2018-07-31 16:22:25 +0200
committerPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2018-07-31 16:22:25 +0200
commit159ef14fb9e198bb0066ea14e6b980f065de63dd (patch)
treebc37c7d4ba09ee59deb708897fa0571709aec293 /silx/utils
parent270d5ddc31c26b62379e3caa9044dd75ccc71847 (diff)
New upstream version 0.8.0+dfsg
Diffstat (limited to 'silx/utils')
-rw-r--r--silx/utils/__init__.py28
-rw-r--r--silx/utils/_have_openmp.pxi49
-rw-r--r--silx/utils/debug.py102
-rw-r--r--silx/utils/include/silx_store_openmp.h10
-rw-r--r--silx/utils/number.py143
-rw-r--r--silx/utils/test/__init__.py6
-rw-r--r--silx/utils/test/test_debug.py99
-rw-r--r--silx/utils/test/test_number.py181
-rw-r--r--silx/utils/weakref.py4
9 files changed, 619 insertions, 3 deletions
diff --git a/silx/utils/__init__.py b/silx/utils/__init__.py
index e69de29..f803a5f 100644
--- a/silx/utils/__init__.py
+++ b/silx/utils/__init__.py
@@ -0,0 +1,28 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2018 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""This package contains a set of miscellaneous convenient features.
+
+See silx documentation: http://www.silx.org/doc/silx/latest/
+"""
diff --git a/silx/utils/_have_openmp.pxi b/silx/utils/_have_openmp.pxi
new file mode 100644
index 0000000..40a2857
--- /dev/null
+++ b/silx/utils/_have_openmp.pxi
@@ -0,0 +1,49 @@
+# 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/debug.py b/silx/utils/debug.py
new file mode 100644
index 0000000..d6ca328
--- /dev/null
+++ b/silx/utils/debug.py
@@ -0,0 +1,102 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+
+
+import inspect
+import types
+import logging
+from silx.third_party import six
+
+
+debug_logger = logging.getLogger("silx.DEBUG")
+
+_indent = 0
+
+
+def log_method(func, class_name=None):
+ """Decorator to inject a warning log before an after any function/method.
+
+ .. code-block:: python
+
+ @log_method
+ def foo():
+ return None
+
+ :param callable func: The function to patch
+ :param str class_name: In case a method, provide the class name
+ """
+ def wrapper(*args, **kwargs):
+ global _indent
+
+ indent = " " * _indent
+ func_name = func.func_name if six.PY2 else func.__name__
+ if class_name is not None:
+ name = "%s.%s" % (class_name, func_name)
+ else:
+ name = "%s" % (func_name)
+
+ debug_logger.warning("%s%s" % (indent, name))
+ _indent += 1
+ result = func(*args, **kwargs)
+ _indent -= 1
+ debug_logger.warning("%sreturn (%s)" % (indent, name))
+ return result
+ return wrapper
+
+
+def log_all_methods(base_class):
+ """Decorator to inject a warning log before an after any method provided by
+ a class.
+
+ .. code-block:: python
+
+ @log_all_methods
+ class Foo(object):
+
+ def a(self):
+ return None
+
+ def b(self):
+ return self.a()
+
+ Here is the output when calling the `b` method.
+
+ .. code-block::
+
+ WARNING:silx.DEBUG:_Foobar.b
+ WARNING:silx.DEBUG: _Foobar.a
+ WARNING:silx.DEBUG: return (_Foobar.a)
+ WARNING:silx.DEBUG:return (_Foobar.b)
+
+ :param class base_class: The class to patch
+ """
+ methodTypes = (types.MethodType, types.FunctionType, types.BuiltinFunctionType, types.BuiltinMethodType)
+ for name, func in inspect.getmembers(base_class):
+ if isinstance(func, methodTypes):
+ if func.__name__ not in ["__subclasshook__", "__new__"]:
+ # patching __new__ in Python2 break the object, then we skip it
+ setattr(base_class, name, log_method(func, base_class.__name__))
+
+ return base_class
diff --git a/silx/utils/include/silx_store_openmp.h b/silx/utils/include/silx_store_openmp.h
new file mode 100644
index 0000000..f04f630
--- /dev/null
+++ b/silx/utils/include/silx_store_openmp.h
@@ -0,0 +1,10 @@
+
+
+
+/** 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/number.py b/silx/utils/number.py
new file mode 100644
index 0000000..e6a87e7
--- /dev/null
+++ b/silx/utils/number.py
@@ -0,0 +1,143 @@
+# 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("^\s*[-+]?0*(\d+?)?(?:\.(\d+))?(?:[eE]([-+]?\d+))?\s*$")
+
+
+def is_longdouble_64bits():
+ """Returns true if the system uses floating-point 64bits for it's
+ long double type.
+
+ .. note:: Comparing `numpy.longdouble` and `numpy.float64` on Windows is not
+ possible (or at least not will all the numpy version)
+ """
+ return _biggest_float == numpy.float64
+
+
+def min_numerical_convertible_type(string, check_accuracy=True):
+ """
+ Parse the string and return the smallest numerical type to use for a safe
+ conversion.
+
+ :param str string: Representation of a float/integer with text
+ :param bool check_accuracy: If true, a warning is pushed on the logger
+ in case there is a loss of accuracy.
+ :raise ValueError: When the string is not a numerical value
+ :retrun: A numpy numerical type
+ """
+ if string == "":
+ raise ValueError("Not a numerical value")
+ match = _parse_numeric_value.match(string)
+ if match is None:
+ raise ValueError("Not a numerical value")
+ number, decimal, exponent = match.groups()
+
+ if decimal is None and exponent is None:
+ # It's an integer
+ # TODO: We could find the int type without converting the number
+ value = int(string)
+ return numpy.min_scalar_type(value).type
+
+ # Try floating-point
+ try:
+ value = _biggest_float(string)
+ except ValueError:
+ raise ValueError("Not a numerical value")
+
+ if number is None:
+ number = ""
+ if decimal is None:
+ decimal = ""
+ if exponent is None:
+ exponent = "0"
+
+ nb_precision_digits = int(exponent) - len(decimal) - 1
+ precision = _biggest_float(10) ** nb_precision_digits * 2.5
+ previous_type = _biggest_float
+ for numpy_type in _float_types:
+ if numpy_type == _biggest_float:
+ # value was already casted using the bigger type
+ continue
+ reduced_value = numpy_type(value)
+ if not numpy.isfinite(reduced_value):
+ break
+ # numpy isclose(atol=is not accurate enough)
+ diff = value - reduced_value
+ # numpy 1.8.2 looks to do the substraction using float64...
+ # we lose precision here
+ diff = numpy.abs(diff)
+ if diff > precision:
+ break
+ previous_type = numpy_type
+
+ # It's the smaller float type which fit with enougth precision
+ numpy_type = previous_type
+
+ if check_accuracy and numpy_type == _biggest_float:
+ # Check the precision using the original string
+ expected = number + decimal
+ # This format the number without python convertion
+ try:
+ result = numpy.array2string(value, precision=len(number) + len(decimal), floatmode="fixed")
+ except TypeError:
+ # numpy 1.8.2 do not have floatmode argument
+ _logger.warning("Not able to check accuracy of the conversion of '%s' using %s", string, _biggest_float)
+ return numpy_type
+
+ result = result.replace(".", "").replace("-", "")
+ if not result.startswith(expected):
+ _logger.warning("Not able to convert '%s' using %s without losing precision", string, _biggest_float)
+
+ return numpy_type
diff --git a/silx/utils/test/__init__.py b/silx/utils/test/__init__.py
index 14ce38d..f16cbdc 100644
--- a/silx/utils/test/__init__.py
+++ b/silx/utils/test/__init__.py
@@ -24,7 +24,7 @@
# ###########################################################################*/
__authors__ = ["T. Vincent", "P. Knobel"]
__license__ = "MIT"
-__date__ = "02/10/2017"
+__date__ = "24/05/2018"
import unittest
@@ -34,6 +34,8 @@ from . import test_array_like
from . import test_launcher
from . import test_deprecation
from . import test_proxy
+from . import test_debug
+from . import test_number
def suite():
@@ -44,4 +46,6 @@ def suite():
test_suite.addTest(test_launcher.suite())
test_suite.addTest(test_deprecation.suite())
test_suite.addTest(test_proxy.suite())
+ test_suite.addTest(test_debug.suite())
+ test_suite.addTest(test_number.suite())
return test_suite
diff --git a/silx/utils/test/test_debug.py b/silx/utils/test/test_debug.py
new file mode 100644
index 0000000..da08960
--- /dev/null
+++ b/silx/utils/test/test_debug.py
@@ -0,0 +1,99 @@
+# 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_number.py b/silx/utils/test/test_number.py
new file mode 100644
index 0000000..c900b32
--- /dev/null
+++ b/silx/utils/test/test_number.py
@@ -0,0 +1,181 @@
+# coding: utf-8
+# /*##########################################################################
+# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ############################################################################*/
+"""Tests for silx.uitls.number module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "05/06/2018"
+
+import logging
+import numpy
+import unittest
+import pkg_resources
+from silx.utils import number
+from silx.utils import testutils
+
+_logger = logging.getLogger(__name__)
+
+
+class TestConversionTypes(testutils.ParametricTestCase):
+
+ def testEmptyFail(self):
+ self.assertRaises(ValueError, number.min_numerical_convertible_type, "")
+
+ def testStringFail(self):
+ self.assertRaises(ValueError, number.min_numerical_convertible_type, "a")
+
+ def testInteger(self):
+ dtype = number.min_numerical_convertible_type("1456")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger))
+
+ def testTrailledInteger(self):
+ dtype = number.min_numerical_convertible_type(" \t\n\r1456\t\n\r")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger))
+
+ def testPositiveInteger(self):
+ dtype = number.min_numerical_convertible_type("+1456")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger))
+
+ def testNegativeInteger(self):
+ dtype = number.min_numerical_convertible_type("-1456")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.signedinteger))
+
+ def testIntegerExponential(self):
+ dtype = number.min_numerical_convertible_type("14e10")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testIntegerPositiveExponential(self):
+ dtype = number.min_numerical_convertible_type("14e+10")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testIntegerNegativeExponential(self):
+ dtype = number.min_numerical_convertible_type("14e-10")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testNumberDecimal(self):
+ dtype = number.min_numerical_convertible_type("14.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testPositiveNumberDecimal(self):
+ dtype = number.min_numerical_convertible_type("+14.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testNegativeNumberDecimal(self):
+ dtype = number.min_numerical_convertible_type("-14.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testDecimal(self):
+ dtype = number.min_numerical_convertible_type(".50")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testPositiveDecimal(self):
+ dtype = number.min_numerical_convertible_type("+.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testNegativeDecimal(self):
+ dtype = number.min_numerical_convertible_type("-.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testMantissa16(self):
+ dtype = number.min_numerical_convertible_type("1.50")
+ self.assertEqual(dtype, numpy.float16)
+
+ def testMantissa32(self):
+ dtype = number.min_numerical_convertible_type("1400.50")
+ self.assertEqual(dtype, numpy.float32)
+
+ def testMantissa64(self):
+ dtype = number.min_numerical_convertible_type("10000.000010")
+ self.assertEqual(dtype, numpy.float64)
+
+ def testMantissa80(self):
+ self.skipIfFloat80NotSupported()
+ dtype = number.min_numerical_convertible_type("1000000000.00001013")
+
+ if pkg_resources.parse_version(numpy.version.version) <= pkg_resources.parse_version("1.10.4"):
+ # numpy 1.8.2 -> Debian 8
+ # Checking a float128 precision with numpy 1.8.2 using abs(diff) is not working.
+ # It looks like the difference is done using float64 (diff == 0.0)
+ expected = (numpy.longdouble, numpy.float64)
+ else:
+ expected = (numpy.longdouble, )
+ self.assertIn(dtype, expected)
+
+ def testExponent32(self):
+ dtype = number.min_numerical_convertible_type("14.0e30")
+ self.assertEqual(dtype, numpy.float32)
+
+ def testExponent64(self):
+ dtype = number.min_numerical_convertible_type("14.0e300")
+ self.assertEqual(dtype, numpy.float64)
+
+ def testExponent80(self):
+ self.skipIfFloat80NotSupported()
+ dtype = number.min_numerical_convertible_type("14.0e3000")
+ self.assertEqual(dtype, numpy.longdouble)
+
+ def testFloat32ToString(self):
+ value = str(numpy.float32(numpy.pi))
+ dtype = number.min_numerical_convertible_type(value)
+ self.assertIn(dtype, (numpy.float32, numpy.float64))
+
+ def skipIfFloat80NotSupported(self):
+ if number.is_longdouble_64bits():
+ self.skipTest("float-80bits not supported")
+
+ def testLosePrecisionUsingFloat80(self):
+ self.skipIfFloat80NotSupported()
+ if pkg_resources.parse_version(numpy.version.version) <= pkg_resources.parse_version("1.10.4"):
+ self.skipTest("numpy > 1.10.4 expected")
+ value = "1000000000.00001013332"
+ func = testutils.test_logging(number._logger.name, warning=1)
+ func = func(number.min_numerical_convertible_type)
+ dtype = func(value)
+ self.assertIn(dtype, (numpy.longdouble, ))
+
+ def testMillisecondEpochTime(self):
+ datetimes = ['1465803236.495412',
+ '1465803236.999362',
+ '1465803237.504311',
+ '1465803238.009261',
+ '1465803238.512211',
+ '1465803239.016160',
+ '1465803239.520110',
+ '1465803240.026059',
+ '1465803240.529009']
+ for datetime in datetimes:
+ with self.subTest(datetime=datetime):
+ dtype = number.min_numerical_convertible_type(datetime)
+ self.assertEqual(dtype, numpy.float64)
+
+
+def suite():
+ loadTests = unittest.defaultTestLoader.loadTestsFromTestCase
+ test_suite = unittest.TestSuite()
+ test_suite.addTest(loadTests(TestConversionTypes))
+ return test_suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest="suite")
diff --git a/silx/utils/weakref.py b/silx/utils/weakref.py
index 42d7392..06646e8 100644
--- a/silx/utils/weakref.py
+++ b/silx/utils/weakref.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016 European Synchrotron Radiation Facility
+# 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
@@ -328,7 +328,7 @@ class WeakList(list):
ref = self.__create_ref(obj)
self.__list.insert(index, ref)
- def pop(self, index):
+ def pop(self, index=-1):
"""Remove and return an object at the requested index"""
self.__clean()
obj = self.__list.pop(index)()