summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Peuch <cortex@worlddomination.be>2020-04-16 03:56:24 +0200
committerLaurent Peuch <cortex@worlddomination.be>2020-04-16 03:56:24 +0200
commit8f56c90309db2353470a03650ee56cec5215ed44 (patch)
tree3b298e0b8e89798811e51d70859dd933314b628c
parent90a7c6ef05cbe1cae0333fa7c2529c4b63520f9b (diff)
downloadlogilab-common-8f56c90309db2353470a03650ee56cec5215ed44.tar.gz
add depreciation.attribute_renamed
-rw-r--r--logilab/common/deprecation.py45
-rw-r--r--test/unittest_deprecation.py22
2 files changed, 67 insertions, 0 deletions
diff --git a/logilab/common/deprecation.py b/logilab/common/deprecation.py
index 3cfdff0..d45424d 100644
--- a/logilab/common/deprecation.py
+++ b/logilab/common/deprecation.py
@@ -190,6 +190,51 @@ def moved(modpath, objname):
moved.__doc__ = _defaultdeprecator.moved.__doc__
+def attribute_renamed(old_name, new_name):
+ """
+ class decorator to allow getting backward compatibility for renamed attributes.
+
+ >>> @attribute_renamed(old_name="old", new_name="new")
+ ... class SomeClass:
+ ... def __init__(self):
+ ... self.new = 42
+
+ >>> some_class = SomeClass()
+ >>> print(some_class.old)
+ sample.py:15: DeprecationWarning: SomeClass.old has been renamed and is deprecated, use SomeClass.new instead
+ print(some_class.old)
+ 42
+ >>> some_class.old = 43
+ sample.py:16: DeprecationWarning: SomeClass.old has been renamed and is deprecated, use SomeClass.new instead
+ some_class.old = 43
+ >>> some_class.old == some_class.new
+ True
+ """
+ def _class_wrap(klass):
+ reason = (
+ f"{klass.__name__}.{old_name} has been renamed and is deprecated, use "
+ f"{klass.__name__}.{new_name} instead"
+ )
+
+ def _get_old(self):
+ warn(reason, DeprecationWarning, stacklevel=2)
+ return getattr(self, new_name)
+
+ def _set_old(self, value):
+ warn(reason, DeprecationWarning, stacklevel=2)
+ setattr(self, new_name, value)
+
+ def _del_old(self):
+ warn(reason, DeprecationWarning, stacklevel=2)
+ delattr(self, new_name)
+
+ setattr(klass, old_name, property(_get_old, _set_old, _del_old))
+
+ return klass
+
+ return _class_wrap
+
+
def renamed(old_name, new_function):
"""use to tell that a callable has been renamed.
diff --git a/test/unittest_deprecation.py b/test/unittest_deprecation.py
index d82c2bc..7778325 100644
--- a/test/unittest_deprecation.py
+++ b/test/unittest_deprecation.py
@@ -71,6 +71,28 @@ class RawInputTC(TestCase):
self.assertEqual(self.messages,
['The function "any_func" is deprecated', 'message'])
+ def test_attribute_renamed(self):
+ @deprecation.attribute_renamed(old_name="old", new_name="new")
+ class SomeClass:
+ def __init__(self):
+ self.new = 42
+
+ some_class = SomeClass()
+ self.assertEqual(some_class.old, some_class.new)
+ self.assertEqual(self.messages,
+ ['SomeClass.old has been renamed and is deprecated, use SomeClass.new '
+ 'instead'])
+
+ some_class.old = 43
+ self.assertEqual(some_class.old, 43)
+ self.assertEqual(some_class.old, some_class.new)
+
+ self.assertTrue(hasattr(some_class, "new"))
+ self.assertTrue(hasattr(some_class, "old"))
+ del some_class.old
+ self.assertFalse(hasattr(some_class, "new"))
+ self.assertFalse(hasattr(some_class, "old"))
+
def test_renamed(self):
def any_func():
pass