From 4758439ed8eb3066fdc1936583574fbcefc9c783 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 14 Nov 2016 21:59:45 -0600 Subject: [PATCH] allow specifying custom warning classes (#7) * allow specifying custom warning classes * changelog for #7 --- CHANGES.rst | 6 +- src/zope/deprecation/deprecation.py | 50 +++++++------ src/zope/deprecation/tests.py | 109 +++++++++++++++++++++++++--- 3 files changed, 127 insertions(+), 38 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b19ae04..0f37198 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,10 +1,12 @@ ``zope.deprecation`` Changelog ============================== -4.2.1 (unreleased) +4.3.0 (unreleased) ------------------ -- TBD +- Allow custom warning classes to be specified to override the default + ``DeprecationWarning``. + See https://github.com/zopefoundation/zope.deprecation/pull/7 4.2.0 (2016-11-07) ------------------ diff --git a/src/zope/deprecation/deprecation.py b/src/zope/deprecation/deprecation.py index 3cb0fd8..017b979 100644 --- a/src/zope/deprecation/deprecation.py +++ b/src/zope/deprecation/deprecation.py @@ -70,12 +70,12 @@ def __init__(self, module): self.__original_module = module self.__deprecated = {} - def deprecate(self, names, message): + def deprecate(self, names, message, cls=DeprecationWarning): """Deprecate the given names.""" if not isinstance(names, (tuple, list)): names = (names,) for name in names: - self.__deprecated[name] = message + self.__deprecated[name] = (message, cls) def __getattribute__(self, name): if name == 'deprecate' or name.startswith('_DeprecationProxy__'): @@ -86,9 +86,8 @@ def __getattribute__(self, name): if name in ogetattr(self, '_DeprecationProxy__deprecated'): if __show__(): - warnings.warn( - name + ': ' + self.__deprecated[name], - DeprecationWarning, 2) + msg, cls = self.__deprecated[name] + warnings.warn(name + ': ' + msg, cls, 2) return getattr(ogetattr(self, '_DeprecationProxy__original_module'), name) @@ -107,9 +106,10 @@ def __delattr__(self, name): class DeprecatedModule(object): - def __init__(self, module, msg): + def __init__(self, module, msg, cls=DeprecationWarning): self.__original_module = module self.__msg = msg + self.__cls = cls def __getattribute__(self, name): if name.startswith('_DeprecatedModule__'): @@ -119,7 +119,7 @@ def __getattribute__(self, name): return types.ModuleType if __show__(): - warnings.warn(self.__msg, DeprecationWarning, 2) + warnings.warn(self.__msg, self.__cls, 2) return getattr(ogetattr(self, '_DeprecatedModule__original_module'), name) @@ -136,39 +136,40 @@ def __delattr__(self, name): class DeprecatedGetProperty(object): - def __init__(self, prop, message): + def __init__(self, prop, message, cls=DeprecationWarning): self.message = message self.prop = prop + self.cls = cls def __get__(self, inst, klass): if __show__(): - warnings.warn(self.message, DeprecationWarning, 2) + warnings.warn(self.message, self.cls, 2) return self.prop.__get__(inst, klass) class DeprecatedGetSetProperty(DeprecatedGetProperty): def __set__(self, inst, prop): if __show__(): - warnings.warn(self.message, DeprecationWarning, 2) + warnings.warn(self.message, self.cls, 2) self.prop.__set__(inst, prop) class DeprecatedGetSetDeleteProperty(DeprecatedGetSetProperty): def __delete__(self, inst): if __show__(): - warnings.warn(self.message, DeprecationWarning, 2) + warnings.warn(self.message, self.cls, 2) self.prop.__delete__(inst) -def DeprecatedMethod(method, message): +def DeprecatedMethod(method, message, cls=DeprecationWarning): def deprecated_method(*args, **kw): if __show__(): - warnings.warn(message, DeprecationWarning, 2) + warnings.warn(message, cls, 2) return method(*args, **kw) return deprecated_method -def deprecated(specifier, message): +def deprecated(specifier, message, cls=DeprecationWarning): """Deprecate the given names.""" # A string specifier (or list of strings) means we're called @@ -180,42 +181,43 @@ def deprecated(specifier, message): if not isinstance(sys.modules[modname], DeprecationProxy): sys.modules[modname] = DeprecationProxy(sys.modules[modname]) - sys.modules[modname].deprecate(specifier, message) + sys.modules[modname].deprecate(specifier, message, cls) # Anything else can mean the specifier is a function/method, # module, or just an attribute of a class elif isinstance(specifier, types.FunctionType): - return DeprecatedMethod(specifier, message) + return DeprecatedMethod(specifier, message, cls) elif isinstance(specifier, types.ModuleType): - return DeprecatedModule(specifier, message) + return DeprecatedModule(specifier, message, cls) else: prop = specifier if hasattr(prop, '__get__') and hasattr(prop, '__set__') and \ hasattr(prop, '__delete__'): - return DeprecatedGetSetDeleteProperty(prop, message) + return DeprecatedGetSetDeleteProperty(prop, message, cls) elif hasattr(prop, '__get__') and hasattr(prop, '__set__'): - return DeprecatedGetSetProperty(prop, message) + return DeprecatedGetSetProperty(prop, message, cls) elif hasattr(prop, '__get__'): - return DeprecatedGetProperty(prop, message) + return DeprecatedGetProperty(prop, message, cls) class deprecate(object): """Deprecation decorator""" - def __init__(self, msg): + def __init__(self, msg, cls=DeprecationWarning): self.msg = msg + self.cls = cls def __call__(self, func): - return DeprecatedMethod(func, self.msg) + return DeprecatedMethod(func, self.msg, self.cls) -def moved(to_location, unsupported_in=None): +def moved(to_location, unsupported_in=None, cls=DeprecationWarning): old = sys._getframe(1).f_globals['__name__'] message = '%s has moved to %s.' % (old, to_location) if unsupported_in: message += " Import of %s will become unsupported in %s" % ( old, unsupported_in) - warnings.warn(message, DeprecationWarning, 3) + warnings.warn(message, cls, 3) __import__(to_location) fromdict = sys.modules[to_location].__dict__ diff --git a/src/zope/deprecation/tests.py b/src/zope/deprecation/tests.py index fa9a801..3d1c6e4 100644 --- a/src/zope/deprecation/tests.py +++ b/src/zope/deprecation/tests.py @@ -91,6 +91,15 @@ def test_deprecate_and__getattribute__string(self): self.warnings.w, [('ClassFixture: hello', DeprecationWarning, 2)]) + def test_deprecate_and__getattribute__string_with_custom_cls(self): + tests = _getTestsModule() + proxy = self._makeOne(tests) + proxy.deprecate('ClassFixture', 'hello', DummyWarning) + self.assertEqual(proxy.ClassFixture, ClassFixture) + self.assertEqual( + self.warnings.w, + [('ClassFixture: hello', DummyWarning, 2)]) + def test_deprecate_and__getattribute__sequence(self): tests = _getTestsModule() proxy = self._makeOne(tests) @@ -160,9 +169,9 @@ def _getTargetClass(self): from zope.deprecation.deprecation import DeprecatedModule return DeprecatedModule - def _makeOne(self, module, msg): + def _makeOne(self, module, msg, *args): cls = self._getTargetClass() - return cls(module, msg) + return cls(module, msg, *args) def test___getattribute____class__(self): tests = _getTestsModule() @@ -183,6 +192,15 @@ def test___getattribute___deprecated(self): [('hello', DeprecationWarning, 2)] ) + def test___getattribute___deprecated_with_custom_cls(self): + tests = _getTestsModule() + proxy = self._makeOne(tests, 'hello', DummyWarning) + self.assertEqual(proxy.ClassFixture, ClassFixture) + self.assertEqual( + self.warnings.w, + [('hello', DummyWarning, 2)] + ) + def test___getattribute__missing(self): tests = _getTestsModule() proxy = self._makeOne(tests, 'hello') @@ -234,9 +252,9 @@ def _getTargetClass(self): from zope.deprecation.deprecation import DeprecatedGetProperty return DeprecatedGetProperty - def _makeOne(self, prop, msg): + def _makeOne(self, prop, msg, *args): cls = self._getTargetClass() - return cls(prop, msg) + return cls(prop, msg, *args) def test___get__(self): prop = DummyProperty() @@ -249,6 +267,17 @@ def test___get__(self): [('hello', DeprecationWarning, 2)] ) + def test___get___with_custom_cls(self): + prop = DummyProperty() + proxy = self._makeOne(prop, 'hello', DummyWarning) + self.assertEqual(proxy.__get__('inst', 'cls'), None) + self.assertEqual(prop.inst, 'inst') + self.assertEqual(prop.cls, 'cls') + self.assertEqual( + self.warnings.w, + [('hello', DummyWarning, 2)] + ) + def test___get__noshow(self): prop = DummyProperty() self.show.on = False @@ -273,6 +302,17 @@ def test___set__(self): self.warnings.w, [('hello', DeprecationWarning, 2)] ) + + def test___set___with_custom_cls(self): + prop = DummyProperty() + proxy = self._makeOne(prop, 'hello', DummyWarning) + self.assertEqual(proxy.__set__('inst', 'prop'), None) + self.assertEqual(prop.inst, 'inst') + self.assertEqual(prop.prop, 'prop') + self.assertEqual( + self.warnings.w, + [('hello', DummyWarning, 2)] + ) def test___set__noshow(self): prop = DummyProperty() @@ -297,6 +337,16 @@ def test___delete__(self): self.warnings.w, [('hello', DeprecationWarning, 2)] ) + + def test___delete___with_custom_cls(self): + prop = DummyProperty() + proxy = self._makeOne(prop, 'hello', DummyWarning) + self.assertEqual(proxy.__delete__('inst'), None) + self.assertEqual(prop.inst, 'inst') + self.assertEqual( + self.warnings.w, + [('hello', DummyWarning, 2)] + ) def test___delete__noshow(self): prop = DummyProperty() @@ -309,9 +359,9 @@ def test___delete__noshow(self): ) class TestDeprecatedMethod(WarningsSetupBase, unittest.TestCase): - def _callFUT(self, method, message): + def _callFUT(self, method, message, *args): from zope.deprecation.deprecation import DeprecatedMethod - return DeprecatedMethod(method, message) + return DeprecatedMethod(method, message, *args) def fixture(self, a, b, c=1): return 'fixture' @@ -324,6 +374,14 @@ def test_it(self): [('hello', DeprecationWarning, 2)] ) + def test_it_with_custom_cls(self): + result = self._callFUT(self.fixture, 'hello', DummyWarning) + self.assertEqual(result('a', 'b', c=2), 'fixture') + self.assertEqual( + self.warnings.w, + [('hello', DummyWarning, 2)] + ) + def test_it_noshow(self): result = self._callFUT(self.fixture, 'hello') self.show.on = False @@ -339,9 +397,9 @@ def tearDown(self): super(Test_deprecated, self).tearDown() sys.modules['zope.deprecation.tests'] = self.mod - def _callFUT(self, spec, message): + def _callFUT(self, spec, message, *args): from zope.deprecation.deprecation import deprecated - return deprecated(spec, message) + return deprecated(spec, message, *args) def test_string_specifier(self): self._callFUT('ClassFixture', 'hello') @@ -352,6 +410,15 @@ def test_string_specifier(self): self.warnings.w, [('ClassFixture: hello', DeprecationWarning, 2)]) + def test_string_specifier_with_custom_cls(self): + self._callFUT('ClassFixture', 'hello', DummyWarning) + mod = _getTestsModule() + self.assertNotEqual(mod, self.mod) + self.assertEqual(mod.ClassFixture, ClassFixture) + self.assertEqual( + self.warnings.w, + [('ClassFixture: hello', DummyWarning, 2)]) + def test_string_specifier_sys_modules_already_mutated(self): from zope.deprecation.deprecation import DeprecationProxy mod = _getTestsModule() @@ -412,9 +479,9 @@ def _getTargetClass(self): from zope.deprecation.deprecation import deprecate return deprecate - def _makeOne(self, msg): + def _makeOne(self, msg, *args): cls = self._getTargetClass() - return cls(msg) + return cls(msg, *args) def fixture(self): return 'fixture' @@ -427,6 +494,14 @@ def test___call__(self): self.warnings.w, [('hello', DeprecationWarning, 2)]) + def test___call__with_custom_cls(self): + proxy = self._makeOne('hello', DummyWarning) + result = proxy(functionfixture) + self.assertEqual(result(), None) + self.assertEqual( + self.warnings.w, + [('hello', DummyWarning, 2)]) + class Test_moved(WarningsSetupBase, unittest.TestCase): def setUp(self): super(Test_moved, self).setUp() @@ -435,9 +510,9 @@ def tearDown(self): super(Test_moved, self).tearDown() del _getTestsModule().__dict__['abc'] - def _callFUT(self, to_location, unsupported_in): + def _callFUT(self, to_location, unsupported_in, *args): from zope.deprecation.deprecation import moved - return moved(to_location, unsupported_in) + return moved(to_location, unsupported_in, *args) def test_unsupported_None(self): self._callFUT('zope.deprecation.fixture', None) @@ -446,6 +521,13 @@ def test_unsupported_None(self): [('zope.deprecation.tests has moved to zope.deprecation.fixture.', DeprecationWarning, 3)]) + def test_unsupported_None_with_custom_cls(self): + self._callFUT('zope.deprecation.fixture', None, DummyWarning) + self.assertEqual( + self.warnings.w, + [('zope.deprecation.tests has moved to zope.deprecation.fixture.', + DummyWarning, 3)]) + def test_unsupported_not_None(self): self._callFUT('zope.deprecation.fixture', '1.3') self.assertEqual( @@ -483,6 +565,9 @@ class DummyGetSetDeleteProperty(DummyGetSetProperty): def __delete__(self, inst): self.inst = inst +class DummyWarning(DeprecationWarning): + pass + DummyProperty = DummyGetSetDeleteProperty def _getTestsModule():