From 23c37dee3649fc6d328246f101df5e43c114c197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Wed, 8 Jan 2014 15:05:06 -0200 Subject: [PATCH] support showing subclassing deprecation warning only once --- scrapy/tests/test_utils_deprecate.py | 43 ++++++++++++++++++++++++++++ scrapy/utils/deprecate.py | 14 +++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/scrapy/tests/test_utils_deprecate.py b/scrapy/tests/test_utils_deprecate.py index 35aae8335..1d434512c 100644 --- a/scrapy/tests/test_utils_deprecate.py +++ b/scrapy/tests/test_utils_deprecate.py @@ -63,6 +63,49 @@ class WarnWhenSubclassedTest(unittest.TestCase): ) self.assertEqual(msg.lineno, lineno) + def test_warning_shown_everytime(self): + Deprecated = create_deprecated_class('Deprecated', NewName, + warn_category=MyWarning) + with warnings.catch_warnings(record=True) as w: + class U1(Deprecated): + pass + + class U2(Deprecated): + pass + + self.assertEqual(len(w), 2) + assert issubclass(w[0].category, MyWarning) + assert issubclass(w[1].category, MyWarning) + + with warnings.catch_warnings(record=True) as w: + _i1 = Deprecated() + _i2 = Deprecated() + + self.assertEqual(len(w), 2) + assert issubclass(w[0].category, MyWarning) + assert issubclass(w[1].category, MyWarning) + + def test_warning_shown_once(self): + Deprecated = create_deprecated_class('Deprecated', NewName, + warn_once=True, + warn_category=MyWarning) + with warnings.catch_warnings(record=True) as w: + class U1(Deprecated): + pass + + class U2(Deprecated): + pass + + self.assertEqual(len(w), 1) + assert issubclass(w[0].category, MyWarning) + + with warnings.catch_warnings(record=True) as w: + _i1 = Deprecated() + _i2 = Deprecated() + + self.assertEqual(len(w), 1) + assert issubclass(w[0].category, MyWarning) + def test_warning_auto_message(self): with warnings.catch_warnings(record=True) as w: Deprecated = create_deprecated_class('Deprecated', NewName) diff --git a/scrapy/utils/deprecate.py b/scrapy/utils/deprecate.py index 20f4f1796..d4e5b835e 100644 --- a/scrapy/utils/deprecate.py +++ b/scrapy/utils/deprecate.py @@ -14,6 +14,7 @@ def attribute(obj, oldattr, newattr, version='0.12'): def create_deprecated_class(name, new_class, clsdict=None, warn_category=ScrapyDeprecationWarning, + warn_once=False, subclass_warn_message="{cls} inherits from "\ "deprecated class {old}, please inherit "\ "from {new}.", @@ -47,6 +48,8 @@ def create_deprecated_class(name, new_class, clsdict=None, class DeprecatedClass(type): deprecated_class = None + warned_on_subclass = False + warned_on_instance = False def __new__(metacls, name, bases, clsdict_): cls = super(DeprecatedClass, metacls).__new__(metacls, name, bases, clsdict_) @@ -55,8 +58,10 @@ def create_deprecated_class(name, new_class, clsdict=None, return cls def __init__(cls, name, bases, clsdict_): - old = cls.__class__.deprecated_class - if cls is not old: + meta = cls.__class__ + old = meta.deprecated_class + if (cls is not old) and not (warn_once and meta.warned_on_subclass): + meta.warned_on_subclass = True msg = subclass_warn_message.format(cls=_clspath(cls), old=_clspath(old), new=_clspath(new_class)) @@ -79,7 +84,10 @@ def create_deprecated_class(name, new_class, clsdict=None, return any(c in candidates for c in mro) def __call__(cls, *args, **kwargs): - if cls is cls.__class__.deprecated_class: + meta = cls.__class__ + old = meta.deprecated_class + if (cls is old) and not (warn_once and meta.warned_on_instance): + meta.warned_on_instance = True msg = instance_warn_message.format(cls=_clspath(cls), new=_clspath(new_class)) warnings.warn(msg, warn_category, stacklevel=2)