warn when the deprecated class is instanciated

This commit is contained in:
Daniel Graña 2013-12-31 15:54:30 -02:00
parent 8c4e1db5fe
commit b41ad38fab
2 changed files with 40 additions and 6 deletions

View File

@ -42,6 +42,27 @@ class WarnWhenSubclassedTest(unittest.TestCase):
)
self.assertEqual(msg.lineno, inspect.getsourcelines(UserClass)[1])
def test_warning_on_instance(self):
with warnings.catch_warnings(record=True) as w:
Deprecated = create_deprecated_class('Deprecated', NewName,
warn_category=MyWarning)
class UserClass(Deprecated):
pass
_, lineno = Deprecated(), inspect.getlineno(inspect.currentframe())
_ = UserClass()
self.assertEqual(len(w), 2)
msg = w[1]
assert issubclass(msg.category, MyWarning)
self.assertEqual(
str(msg.message),
"scrapy.tests.test_utils_deprecate.Deprecated is deprecated, "
"instanciate scrapy.tests.test_utils_deprecate.NewName instead."
)
self.assertEqual(msg.lineno, lineno)
def test_warning_auto_message(self):
with warnings.catch_warnings(record=True) as w:
Deprecated = create_deprecated_class('Deprecated', NewName)

View File

@ -14,11 +14,16 @@ def attribute(obj, oldattr, newattr, version='0.12'):
def create_deprecated_class(name, new_class, clsdict=None,
warn_category=ScrapyDeprecationWarning,
warn_message="{cls} inherits from deprecated class {old}, "\
"please inherit from {new}."):
subclass_warn_message="{cls} inherits from "\
"deprecated class {old}, please inherit "\
"from {new}.",
instance_warn_message="{cls} is deprecated, "\
"instanciate {new} instead."):
"""
Return a "deprecated" class that causes its subclasses to issue a warning.
Subclasses of ``new_class`` are considered subclasses of this class.
It also warns when the deprecated class is instanciated, but do not when
its subclasses are instanciated.
It can be used to rename a base class in a library. For example, if we
have
@ -50,10 +55,11 @@ def create_deprecated_class(name, new_class, clsdict=None,
return cls
def __init__(cls, name, bases, clsdict_):
if cls is not cls.__class__.deprecated_class:
msg = warn_message.format(cls=_clspath(cls),
old=_clspath(cls.__class__.deprecated_class),
new=_clspath(new_class))
old = cls.__class__.deprecated_class
if cls is not old:
msg = subclass_warn_message.format(cls=_clspath(cls),
old=_clspath(old),
new=_clspath(new_class))
warnings.warn(msg, warn_category, stacklevel=2)
super(DeprecatedClass, cls).__init__(name, bases, clsdict_)
@ -72,6 +78,13 @@ def create_deprecated_class(name, new_class, clsdict=None,
candidates = {cls, new_class}
return any(c in candidates for c in mro)
def __call__(cls, *args, **kwargs):
if cls is cls.__class__.deprecated_class:
msg = instance_warn_message.format(cls=_clspath(cls),
new=_clspath(new_class))
warnings.warn(msg, warn_category, stacklevel=2)
return super(DeprecatedClass, cls).__call__(*args, **kwargs)
deprecated_cls = DeprecatedClass(name, (new_class,), clsdict or {})
frm = inspect.stack()[1]
parent_module = inspect.getmodule(frm[0])