Skip to content

Commit

Permalink
allow specifying custom warning classes (#7)
Browse files Browse the repository at this point in the history
* allow specifying custom warning classes

* changelog for #7
  • Loading branch information
mmerickel authored and tseaver committed Nov 15, 2016
1 parent 77d3db0 commit 4758439
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 38 deletions.
6 changes: 4 additions & 2 deletions 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)
------------------
Expand Down
50 changes: 26 additions & 24 deletions src/zope/deprecation/deprecation.py
Expand Up @@ -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__'):
Expand All @@ -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)
Expand All @@ -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__'):
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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__
Expand Down
109 changes: 97 additions & 12 deletions src/zope/deprecation/tests.py
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand All @@ -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')
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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()
Expand All @@ -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'
Expand All @@ -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
Expand All @@ -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')
Expand All @@ -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()
Expand Down Expand Up @@ -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'
Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -483,6 +565,9 @@ class DummyGetSetDeleteProperty(DummyGetSetProperty):
def __delete__(self, inst):
self.inst = inst

class DummyWarning(DeprecationWarning):
pass

DummyProperty = DummyGetSetDeleteProperty

def _getTestsModule():
Expand Down

0 comments on commit 4758439

Please sign in to comment.