diff options
author | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2012-10-23 10:32:56 +0200 |
---|---|---|
committer | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2012-10-23 10:32:56 +0200 |
commit | 19a3867a64d38cd2c9f16cc1988e2daeea7aadc6 (patch) | |
tree | fb193fa153cdc384bb65532487838f4b20ce9b8f | |
parent | 6b786f3fee5da562e94b9dc14ba08564e8b9bcc9 (diff) | |
download | logilab-common-19a3867a64d38cd2c9f16cc1988e2daeea7aadc6.tar.gz |
[py3k @cached] fix compat of dark corners for the @monkeypatch decorator, making tests pass. Closes #104047
-rw-r--r-- | decorators.py | 29 | ||||
-rw-r--r-- | test/unittest_decorators.py | 12 |
2 files changed, 36 insertions, 5 deletions
diff --git a/decorators.py b/decorators.py index 43c3652..66539a7 100644 --- a/decorators.py +++ b/decorators.py @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of logilab-common. @@ -19,6 +19,7 @@ __docformat__ = "restructuredtext en" import sys +import types from time import clock, time from logilab.common.compat import callable, method_type @@ -273,11 +274,33 @@ def monkeypatch(klass, methodname=None): raise AttributeError('%s has no __name__ attribute: ' 'you should provide an explicit `methodname`' % func) - if callable(func) and sys.version_info < (3, 0): - setattr(klass, name, method_type(func, None, klass)) + if callable(func): + if sys.version_info < (3, 0): + setattr(klass, name, method_type(func, None, klass)) + elif isinstance(func, types.FunctionType): + setattr(klass, name, func) + else: + setattr(klass, name, UnboundMethod(func)) else: # likely a property # this is quite borderline but usage already in the wild ... setattr(klass, name, func) return func return decorator + +if sys.version_info >= (3, 0): + class UnboundMethod(object): + """unbound method wrapper necessary for python3 where we can't turn + arbitrary object (eg class implementing __call__) into a method, as + there is no more unbound method and only function are turned + automatically to method when accessed through an instance. + """ + __slots__ = ('_callable',) + + def __init__(self, callable): + self._callable = callable + + def __get__(self, instance, objtype): + if instance is None: + return self._callable + return types.MethodType(self._callable, instance) diff --git a/test/unittest_decorators.py b/test/unittest_decorators.py index 72d8b07..f6c0c75 100644 --- a/test/unittest_decorators.py +++ b/test/unittest_decorators.py @@ -17,6 +17,7 @@ # with logilab-common. If not, see <http://www.gnu.org/licenses/>. """unit tests for the decorators module """ +import sys import types from logilab.common.testlib import TestCase, unittest_main @@ -34,8 +35,15 @@ class DecoratorsTC(TestCase): @monkeypatch(MyClass) def meth2(self): return 12 - self.assertTrue(isinstance(MyClass.meth1, types.MethodType)) - self.assertTrue(isinstance(MyClass.meth2, types.MethodType)) + if sys.version_info < (3, 0): + # with python3, unbound method are functions + self.assertIsInstance(MyClass.meth1, types.MethodType) + self.assertIsInstance(MyClass.meth2, types.MethodType) + else: + self.assertIsInstance(MyClass.meth1, types.FunctionType) + self.assertIsInstance(MyClass.meth2, types.FunctionType) + self.assertEqual(MyClass().meth1(), 12) + self.assertEqual(MyClass().meth2(), 12) def test_monkeypatch_callable_non_callable(self): tester = self |