diff options
Diffstat (limited to 'silx/utils')
-rw-r--r-- | silx/utils/decorators.py | 71 | ||||
-rw-r--r-- | silx/utils/deprecation.py | 117 | ||||
-rw-r--r-- | silx/utils/proxy.py | 204 | ||||
-rw-r--r-- | silx/utils/test/__init__.py | 22 | ||||
-rw-r--r-- | silx/utils/test/test_deprecation.py | 107 | ||||
-rw-r--r-- | silx/utils/test/test_proxy.py | 295 |
6 files changed, 736 insertions, 80 deletions
diff --git a/silx/utils/decorators.py b/silx/utils/decorators.py deleted file mode 100644 index ff70e38..0000000 --- a/silx/utils/decorators.py +++ /dev/null @@ -1,71 +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"] -__license__ = "MIT" -__date__ = "01/03/2017" - -import os -import sys -import traceback -import logging -import functools - - -depreclog = logging.getLogger("DEPRECATION") - - -def deprecated(func=None, reason=None, replacement=None, since_version=None): - """ - 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"). - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - name = func.func_name if sys.version_info[0] < 3 else func.__name__ - msg = "%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 - depreclog.warning(msg + " %s", name, os.linesep.join([""] + traceback.format_stack()[:-1])) - return func(*args, **kwargs) - return wrapper - if func is not None: - return decorator(func) - return decorator diff --git a/silx/utils/deprecation.py b/silx/utils/deprecation.py new file mode 100644 index 0000000..f1d2a79 --- /dev/null +++ b/silx/utils/deprecation.py @@ -0,0 +1,117 @@ +# 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__ = "11/09/2017" + +import sys +import logging +import functools +import traceback + +depreclog = logging.getLogger("silx.DEPRECATION") + +deprecache = set([]) + + +def deprecated(func=None, reason=None, replacement=None, since_version=None, only_once=True): + """ + 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. + """ + 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=1) + return func(*args, **kwargs) + return wrapper + if func is not None: + return decorator(func) + return decorator + + +def deprecated_warning(type_, name, reason=None, replacement=None, + since_version=None, only_once=True, + skip_backtrace_count=0): + """ + Function to log a deprecation warning + + :param str type_: Nature of the object to be deprecated: + "Module", "Function", "Class" ... + :param name: Object name. + :param str reason: Reason for deprecating this function + (e.g. "feature no longer provided", + :param str replacement: Name of replacement function (if the reason for + deprecating was to rename the function) + :param str since_version: First *silx* version for which the function was + deprecated (e.g. "0.5.0"). + :param bool only_once: If true, the deprecation warning will only be + generated one time for each different call locations. Default is true. + :param int skip_backtrace_count: Amount of last backtrace to ignore when + logging the backtrace + """ + if not depreclog.isEnabledFor(logging.WARNING): + # Avoid computation when it is not logged + return + + msg = "%s %s is deprecated" + if since_version is not None: + msg += " since silx version %s" % since_version + msg += "." + if reason is not None: + msg += " Reason: %s." % reason + if replacement is not None: + msg += " Use '%s' instead." % replacement + msg += "\n%s" + limit = 2 + skip_backtrace_count + backtrace = "".join(traceback.format_stack(limit=limit)[0]) + backtrace = backtrace.rstrip() + if only_once: + data = (msg, type_, name, backtrace) + if data in deprecache: + return + else: + deprecache.add(data) + depreclog.warning(msg, type_, name, backtrace) diff --git a/silx/utils/proxy.py b/silx/utils/proxy.py new file mode 100644 index 0000000..2633703 --- /dev/null +++ b/silx/utils/proxy.py @@ -0,0 +1,204 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016-2017 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ###########################################################################*/ +"""Module containing proxy objects""" + +from __future__ import absolute_import, print_function, division + +__authors__ = ["V. Valls"] +__license__ = "MIT" +__date__ = "02/10/2017" + +from silx.third_party import six + + +class Proxy(object): + """Create a proxy of an object. + + Provides default methods and property using :meth:`__getattr__` and special + method by redefining them one by one. + Special methods are defined as properties, as a result if the `obj` method + is not defined, the property code fail and the special method will not be + visible. + """ + + __slots__ = ["__obj", "__weakref__"] + + def __init__(self, obj): + object.__setattr__(self, "_Proxy__obj", obj) + + __class__ = property(lambda self: self.__obj.__class__) + + def __getattr__(self, name): + return getattr(self.__obj, name) + + __setattr__ = property(lambda self: self.__obj.__setattr__) + __delattr__ = property(lambda self: self.__obj.__delattr__) + + # binary comparator methods + + __lt__ = property(lambda self: self.__obj.__lt__) + __le__ = property(lambda self: self.__obj.__le__) + __eq__ = property(lambda self: self.__obj.__eq__) + __ne__ = property(lambda self: self.__obj.__ne__) + __gt__ = property(lambda self: self.__obj.__gt__) + __ge__ = property(lambda self: self.__obj.__ge__) + + if six.PY2: + __cmp__ = property(lambda self: self.__obj.__cmp__) + + # binary numeric methods + + __add__ = property(lambda self: self.__obj.__add__) + __radd__ = property(lambda self: self.__obj.__radd__) + __iadd__ = property(lambda self: self.__obj.__iadd__) + __sub__ = property(lambda self: self.__obj.__sub__) + __rsub__ = property(lambda self: self.__obj.__rsub__) + __isub__ = property(lambda self: self.__obj.__isub__) + __mul__ = property(lambda self: self.__obj.__mul__) + __rmul__ = property(lambda self: self.__obj.__rmul__) + __imul__ = property(lambda self: self.__obj.__imul__) + + if six.PY2: + # Only part of Python 2 + # Python 3 uses __truediv__ and __floordiv__ + __div__ = property(lambda self: self.__obj.__div__) + __rdiv__ = property(lambda self: self.__obj.__rdiv__) + __idiv__ = property(lambda self: self.__obj.__idiv__) + + __truediv__ = property(lambda self: self.__obj.__truediv__) + __rtruediv__ = property(lambda self: self.__obj.__rtruediv__) + __itruediv__ = property(lambda self: self.__obj.__itruediv__) + __floordiv__ = property(lambda self: self.__obj.__floordiv__) + __rfloordiv__ = property(lambda self: self.__obj.__rfloordiv__) + __ifloordiv__ = property(lambda self: self.__obj.__ifloordiv__) + __mod__ = property(lambda self: self.__obj.__mod__) + __rmod__ = property(lambda self: self.__obj.__rmod__) + __imod__ = property(lambda self: self.__obj.__imod__) + __divmod__ = property(lambda self: self.__obj.__divmod__) + __rdivmod__ = property(lambda self: self.__obj.__rdivmod__) + __pow__ = property(lambda self: self.__obj.__pow__) + __rpow__ = property(lambda self: self.__obj.__rpow__) + __ipow__ = property(lambda self: self.__obj.__ipow__) + __lshift__ = property(lambda self: self.__obj.__lshift__) + __rlshift__ = property(lambda self: self.__obj.__rlshift__) + __ilshift__ = property(lambda self: self.__obj.__ilshift__) + __rshift__ = property(lambda self: self.__obj.__rshift__) + __rrshift__ = property(lambda self: self.__obj.__rrshift__) + __irshift__ = property(lambda self: self.__obj.__irshift__) + + # binary logical methods + + __and__ = property(lambda self: self.__obj.__and__) + __rand__ = property(lambda self: self.__obj.__rand__) + __iand__ = property(lambda self: self.__obj.__iand__) + __xor__ = property(lambda self: self.__obj.__xor__) + __rxor__ = property(lambda self: self.__obj.__rxor__) + __ixor__ = property(lambda self: self.__obj.__ixor__) + __or__ = property(lambda self: self.__obj.__or__) + __ror__ = property(lambda self: self.__obj.__ror__) + __ior__ = property(lambda self: self.__obj.__ior__) + + # unary methods + + __neg__ = property(lambda self: self.__obj.__neg__) + __pos__ = property(lambda self: self.__obj.__pos__) + __abs__ = property(lambda self: self.__obj.__abs__) + __invert__ = property(lambda self: self.__obj.__invert__) + if six.PY3: + __floor__ = property(lambda self: self.__obj.__floor__) + __ceil__ = property(lambda self: self.__obj.__ceil__) + __round__ = property(lambda self: self.__obj.__round__) + + # cast + + __repr__ = property(lambda self: self.__obj.__repr__) + __str__ = property(lambda self: self.__obj.__str__) + __complex__ = property(lambda self: self.__obj.__complex__) + __int__ = property(lambda self: self.__obj.__int__) + __float__ = property(lambda self: self.__obj.__float__) + __hash__ = property(lambda self: self.__obj.__hash__) + if six.PY2: + __long__ = property(lambda self: self.__obj.__long__) + __oct__ = property(lambda self: self.__obj.__oct__) + __hex__ = property(lambda self: self.__obj.__hex__) + __unicode__ = property(lambda self: self.__obj.__unicode__) + __nonzero__ = property(lambda self: lambda: bool(self.__obj)) + if six.PY3: + __bytes__ = property(lambda self: self.__obj.__bytes__) + __bool__ = property(lambda self: lambda: bool(self.__obj)) + __format__ = property(lambda self: self.__obj.__format__) + + # container + + __len__ = property(lambda self: self.__obj.__len__) + if six.PY3: + __length_hint__ = property(lambda self: self.__obj.__length_hint__) + __getitem__ = property(lambda self: self.__obj.__getitem__) + __missing__ = property(lambda self: self.__obj.__missing__) + __setitem__ = property(lambda self: self.__obj.__setitem__) + __delitem__ = property(lambda self: self.__obj.__delitem__) + __iter__ = property(lambda self: self.__obj.__iter__) + __reversed__ = property(lambda self: self.__obj.__reversed__) + __contains__ = property(lambda self: self.__obj.__contains__) + + if six.PY2: + __getslice__ = property(lambda self: self.__obj.__getslice__) + __setslice__ = property(lambda self: self.__obj.__setslice__) + __delslice__ = property(lambda self: self.__obj.__delslice__) + + # pickle + + __reduce__ = property(lambda self: self.__obj.__reduce__) + __reduce_ex__ = property(lambda self: self.__obj.__reduce_ex__) + + # async + + if six.PY3: + __await__ = property(lambda self: self.__obj.__await__) + __aiter__ = property(lambda self: self.__obj.__aiter__) + __anext__ = property(lambda self: self.__obj.__anext__) + __aenter__ = property(lambda self: self.__obj.__aenter__) + __aexit__ = property(lambda self: self.__obj.__aexit__) + + # other + + __index__ = property(lambda self: self.__obj.__index__) + if six.PY2: + __coerce__ = property(lambda self: self.__obj.__coerce__) + + if six.PY3: + __next__ = property(lambda self: self.__obj.__next__) + + __enter__ = property(lambda self: self.__obj.__enter__) + __exit__ = property(lambda self: self.__obj.__exit__) + + __concat__ = property(lambda self: self.__obj.__concat__) + __iconcat__ = property(lambda self: self.__obj.__iconcat__) + + if six.PY2: + __repeat__ = property(lambda self: self.__obj.__repeat__) + __irepeat__ = property(lambda self: self.__obj.__irepeat__) + + __call__ = property(lambda self: self.__obj.__call__) diff --git a/silx/utils/test/__init__.py b/silx/utils/test/__init__.py index 68e7412..14ce38d 100644 --- a/silx/utils/test/__init__.py +++ b/silx/utils/test/__init__.py @@ -24,20 +24,24 @@ # ###########################################################################*/ __authors__ = ["T. Vincent", "P. Knobel"] __license__ = "MIT" -__date__ = "03/04/2017" +__date__ = "02/10/2017" import unittest -from .test_weakref import suite as test_weakref_suite -from .test_html import suite as test_html_suite -from .test_array_like import suite as test_array_like_suite -from .test_launcher import suite as test_launcher_suite +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 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_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()) return test_suite diff --git a/silx/utils/test/test_deprecation.py b/silx/utils/test/test_deprecation.py new file mode 100644 index 0000000..b5c5de4 --- /dev/null +++ b/silx/utils/test/test_deprecation.py @@ -0,0 +1,107 @@ +# 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__ = "11/09/2017" + + +import unittest +from .. import deprecation +from silx.test import utils + + +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 + + @utils.test_logging(deprecation.depreclog.name, warning=1) + def testAnnotationWithoutParam(self): + self.deprecatedWithoutParam() + + @utils.test_logging(deprecation.depreclog.name, warning=1) + def testAnnotationWithParams(self): + self.deprecatedWithParams() + + @utils.test_logging(deprecation.depreclog.name, warning=3) + def testLoggedEveryTime(self): + """Logged everytime cause it is 3 different locations""" + self.deprecatedOnlyOnce() + self.deprecatedOnlyOnce() + self.deprecatedOnlyOnce() + + @utils.test_logging(deprecation.depreclog.name, warning=1) + def testLoggedSingleTime(self): + def log(): + self.deprecatedOnlyOnce() + log() + log() + log() + + @utils.test_logging(deprecation.depreclog.name, warning=3) + def testLoggedEveryTime2(self): + self.deprecatedEveryTime() + self.deprecatedEveryTime() + self.deprecatedEveryTime() + + @utils.test_logging(deprecation.depreclog.name, warning=1) + def testWarning(self): + deprecation.deprecated_warning(type_="t", name="n") + + def testBacktrace(self): + testLogging = utils.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_proxy.py b/silx/utils/test/test_proxy.py new file mode 100644 index 0000000..081d3d4 --- /dev/null +++ b/silx/utils/test/test_proxy.py @@ -0,0 +1,295 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ###########################################################################*/ +"""Tests for weakref module""" + +__authors__ = ["V. Valls"] +__license__ = "MIT" +__date__ = "02/10/2017" + + +import unittest +import pickle +import numpy +from ..proxy import Proxy + + +class Thing(object): + + def __init__(self, value): + self.value = value + + def __getitem__(self, selection): + return selection + 1 + + def method(self, value): + return value + 2 + + +class InheritedProxy(Proxy): + """Inheriting the proxy allow to specialisze methods""" + + def __init__(self, obj, value): + Proxy.__init__(self, obj) + self.value = value + 2 + + def __getitem__(self, selection): + return selection + 3 + + def method(self, value): + return value + 4 + + +class TestProxy(unittest.TestCase): + """Test that the proxy behave as expected""" + + def text_init(self): + obj = Thing(10) + p = Proxy(obj) + self.assertTrue(isinstance(p, Thing)) + self.assertTrue(isinstance(p, Proxy)) + + # methods and properties + + def test_has_special_method(self): + obj = Thing(10) + p = Proxy(obj) + self.assertTrue(hasattr(p, "__getitem__")) + + def test_missing_special_method(self): + obj = Thing(10) + p = Proxy(obj) + self.assertFalse(hasattr(p, "__and__")) + + def test_method(self): + obj = Thing(10) + p = Proxy(obj) + self.assertEqual(p.method(10), obj.method(10)) + + def test_property(self): + obj = Thing(10) + p = Proxy(obj) + self.assertEqual(p.value, obj.value) + + # special functions + + def test_getitem(self): + obj = Thing(10) + p = Proxy(obj) + self.assertEqual(p[10], obj[10]) + + def test_setitem(self): + obj = numpy.array([10, 20, 30]) + p = Proxy(obj) + p[0] = 20 + self.assertEqual(obj[0], 20) + + def test_slice(self): + obj = numpy.arange(20) + p = Proxy(obj) + expected = obj[0:10:2] + result = p[0:10:2] + self.assertEqual(list(result), list(expected)) + + # binary comparator methods + + def test_lt(self): + obj = numpy.array([20]) + p = Proxy(obj) + expected = obj < obj + result = p < p + self.assertEqual(result, expected) + + # binary numeric methods + + def test_add(self): + obj = numpy.array([20]) + proxy = Proxy(obj) + expected = obj + obj + result = proxy + proxy + self.assertEqual(result, expected) + + def test_iadd(self): + expected = numpy.array([20]) + expected += 10 + obj = numpy.array([20]) + result = Proxy(obj) + result += 10 + self.assertEqual(result, expected) + + def test_radd(self): + obj = numpy.array([20]) + p = Proxy(obj) + expected = 10 + obj + result = 10 + p + self.assertEqual(result, expected) + + # binary logical methods + + def test_and(self): + obj = numpy.array([20]) + p = Proxy(obj) + expected = obj & obj + result = p & p + self.assertEqual(result, expected) + + def test_iand(self): + expected = numpy.array([20]) + expected &= 10 + obj = numpy.array([20]) + result = Proxy(obj) + result &= 10 + self.assertEqual(result, expected) + + def test_rand(self): + obj = numpy.array([20]) + p = Proxy(obj) + expected = 10 & obj + result = 10 & p + self.assertEqual(result, expected) + + # unary methods + + def test_neg(self): + obj = numpy.array([20]) + p = Proxy(obj) + expected = -obj + result = -p + self.assertEqual(result, expected) + + def test_round(self): + obj = 20.5 + p = Proxy(obj) + expected = round(obj) + result = round(p) + self.assertEqual(result, expected) + + # cast + + def test_bool(self): + obj = True + p = Proxy(obj) + if p: + pass + else: + self.fail() + + def test_str(self): + obj = Thing(10) + p = Proxy(obj) + expected = str(obj) + result = str(p) + self.assertEqual(result, expected) + + def test_repr(self): + obj = Thing(10) + p = Proxy(obj) + expected = repr(obj) + result = repr(p) + self.assertEqual(result, expected) + + def test_text_bool(self): + obj = "" + p = Proxy(obj) + if p: + self.fail() + else: + pass + + def test_text_str(self): + obj = "a" + p = Proxy(obj) + expected = str(obj) + result = str(p) + self.assertEqual(result, expected) + + def test_text_repr(self): + obj = "a" + p = Proxy(obj) + expected = repr(obj) + result = repr(p) + self.assertEqual(result, expected) + + def test_hash(self): + obj = [0, 1, 2] + p = Proxy(obj) + with self.assertRaises(TypeError): + hash(p) + obj = (0, 1, 2) + p = Proxy(obj) + hash(p) + + +class TestInheritedProxy(unittest.TestCase): + """Test that inheriting the Proxy class behave as expected""" + + # methods and properties + + def test_method(self): + obj = Thing(10) + p = InheritedProxy(obj, 11) + self.assertEqual(p.method(10), 11 + 3) + + def test_property(self): + obj = Thing(10) + p = InheritedProxy(obj, 11) + self.assertEqual(p.value, 11 + 2) + + # special functions + + def test_getitem(self): + obj = Thing(10) + p = InheritedProxy(obj, 11) + self.assertEqual(p[12], 12 + 3) + + +class TestPickle(unittest.TestCase): + + def test_dumps(self): + obj = Thing(10) + p = Proxy(obj) + expected = pickle.dumps(obj) + result = pickle.dumps(p) + self.assertEqual(result, expected) + + def test_loads(self): + obj = Thing(10) + p = Proxy(obj) + obj2 = pickle.loads(pickle.dumps(p)) + self.assertTrue(isinstance(obj2, Thing)) + self.assertFalse(isinstance(obj2, Proxy)) + self.assertEqual(obj.value, obj2.value) + + +def suite(): + loadTests = unittest.defaultTestLoader.loadTestsFromTestCase + test_suite = unittest.TestSuite() + test_suite.addTest(loadTests(TestProxy)) + test_suite.addTest(loadTests(TestPickle)) + test_suite.addTest(loadTests(TestInheritedProxy)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') |