summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Peuch <cortex@worlddomination.be>2020-05-06 14:20:08 +0200
committerLaurent Peuch <cortex@worlddomination.be>2020-05-06 14:20:08 +0200
commit324419e23b73f2269fff4d44e1457d7d3bfbf66d (patch)
treee0ddff710df5b40726b751bc8dc377454b18995f
parent49dc54a7c761ca4acbef6d2104f8a45a1d5a9cb1 (diff)
downloadlogilab-common-324419e23b73f2269fff4d44e1457d7d3bfbf66d.tar.gz
[deprecation/fix] correctly automatically get the module in which deprecation utils are called
There was a missmatched combination of: * the frame wasn't always correctly grabbed * grabbing the frame in the situation of a decorator didn't make any sens, so switch to func.__module__ * the tests were bad and expected "[logilab.common]" while it should have been "[test_deprecation]" because it was there that the depreciation was declared
-rw-r--r--logilab/common/deprecation.py95
-rw-r--r--test/test_deprecation.py96
-rw-r--r--test/test_modutils.py22
-rw-r--r--test/test_registry.py6
4 files changed, 146 insertions, 73 deletions
diff --git a/logilab/common/deprecation.py b/logilab/common/deprecation.py
index 15f8087..a57ae33 100644
--- a/logilab/common/deprecation.py
+++ b/logilab/common/deprecation.py
@@ -45,7 +45,7 @@ class DeprecationWrapper(object):
setattr(self._proxied, attr, value)
-def _get_package_name(number=2):
+def _get_module_name(number=1):
"""
automagically try to determine the package name from which the warning has
been triggered by loop other calling frames.
@@ -55,29 +55,27 @@ def _get_package_name(number=2):
frame = sys._getframe()
- for i in range(number):
+ for i in range(number + 1):
if frame.f_back is None:
break
frame = frame.f_back
- if frame.f_globals["__package__"] is not None:
+ if frame.f_globals["__package__"]:
return frame.f_globals["__package__"]
file_name = os.path.split(frame.f_globals["__file__"])[1]
if file_name.endswith(".py"):
- file_name = file_name[:-len(".py")]
+ file_name = file_name[: -len(".py")]
return file_name
-def send_warning(reason, version=None, stacklevel=2):
+def send_warning(reason, version=None, stacklevel=2, module_name=None):
"""Display a deprecation message only if the version is older than the
compatible version.
"""
- module_name = _get_package_name(stacklevel + 1)
-
if module_name and version:
reason = '[%s %s] %s' % (module_name, version, reason)
elif module_name:
@@ -102,10 +100,15 @@ def callable_renamed(old_name, new_function, version=None):
"""
@wraps(new_function)
def wrapped(*args, **kwargs):
- send_warning((
- f"{old_name} has been renamed and is deprecated, uses {new_function.__name__} "
- f"instead"
- ), stacklevel=3, version=version)
+ send_warning(
+ (
+ f"{old_name} has been renamed and is deprecated, uses {new_function.__name__} "
+ f"instead"
+ ),
+ stacklevel=3,
+ version=version,
+ module_name=new_function.__module__,
+ )
return new_function(*args, **kwargs)
return wrapped
@@ -129,8 +132,13 @@ def argument_removed(old_argument_name, version=None):
@wraps(func)
def check_kwargs(*args, **kwargs):
if old_argument_name in kwargs:
- send_warning(f"argument {old_argument_name} of callable {func.__name__} has been "
- f"removed and is deprecated", stacklevel=3, version=version)
+ send_warning(
+ f"argument {old_argument_name} of callable {func.__name__} has been "
+ f"removed and is deprecated",
+ stacklevel=3,
+ version=version,
+ module_name=func.__module__,
+ )
del kwargs[old_argument_name]
return func(*args, **kwargs)
@@ -153,7 +161,7 @@ def callable_deprecated(reason=None, version=None, stacklevel=2):
@wraps(func)
def wrapped(*args, **kwargs):
- send_warning(message, version, stacklevel + 1)
+ send_warning(message, version, stacklevel + 1, module_name=func.__module__)
return func(*args, **kwargs)
return wrapped
@@ -167,10 +175,15 @@ class class_deprecated(type):
"""metaclass to print a warning on instantiation of a deprecated class"""
def __call__(cls, *args, **kwargs):
- msg = getattr(cls, "__deprecation_warning__",
- "%(cls)s is deprecated") % {'cls': cls.__name__}
- send_warning(msg, stacklevel=getattr(cls, "__deprecation_warning_stacklevel__", 4),
- version=getattr(cls, "__deprecation_warning_version__", None))
+ msg = getattr(cls, "__deprecation_warning__", "%(cls)s is deprecated") % {
+ "cls": cls.__name__
+ }
+ send_warning(
+ msg,
+ stacklevel=getattr(cls, "__deprecation_warning_stacklevel__", 3),
+ version=getattr(cls, "__deprecation_warning_version__", None),
+ module_name=getattr(cls, "__deprecation_warning_module_name__", cls.__module__),
+ )
return type.__call__(cls, *args, **kwargs)
@@ -201,15 +214,15 @@ def attribute_renamed(old_name, new_name, version=None):
)
def _get_old(self):
- send_warning(reason, stacklevel=3, version=version)
+ send_warning(reason, stacklevel=3, version=version, module_name=klass.__module__)
return getattr(self, new_name)
def _set_old(self, value):
- send_warning(reason, stacklevel=3, version=version)
+ send_warning(reason, stacklevel=3, version=version, module_name=klass.__module__)
setattr(self, new_name, value)
def _del_old(self):
- send_warning(reason, stacklevel=3, version=version)
+ send_warning(reason, stacklevel=3, version=version, module_name=klass.__module__)
delattr(self, new_name)
setattr(klass, old_name, property(_get_old, _set_old, _del_old))
@@ -240,9 +253,13 @@ def argument_renamed(old_name, new_name, version=None):
f"{new_name} has keyword arguments, only uses {new_name}")
if old_name in kwargs:
- send_warning(f"argument {old_name} of callable {func.__name__} has been renamed "
- f"and is deprecated, use keyword argument {new_name} instead",
- stacklevel=3, version=version)
+ send_warning(
+ f"argument {old_name} of callable {func.__name__} has been renamed "
+ f"and is deprecated, use keyword argument {new_name} instead",
+ stacklevel=3,
+ version=version,
+ module_name=func.__module__,
+ )
kwargs[new_name] = kwargs[old_name]
del kwargs[old_name]
@@ -271,7 +288,9 @@ def callable_moved(module_name, object_name, version=None, stacklevel=2):
def callnew(*args, **kwargs):
from logilab.common.modutils import load_module_from_name
- send_warning(message, version=version, stacklevel=stacklevel + 1)
+ send_warning(
+ message, version=version, stacklevel=stacklevel + 1, module_name=_get_module_name(1)
+ )
m = load_module_from_name(module_name)
return getattr(m, object_name)(*args, **kwargs)
@@ -282,7 +301,7 @@ def callable_moved(module_name, object_name, version=None, stacklevel=2):
moved = callable_renamed(old_name="moved", new_function=callable_moved)
-def class_renamed(old_name, new_class, message=None, version=None):
+def class_renamed(old_name, new_class, message=None, version=None, module_name=None):
"""automatically creates a class which fires a DeprecationWarning
when instantiated.
@@ -294,11 +313,16 @@ def class_renamed(old_name, new_class, message=None, version=None):
"""
class_dict = {}
if message is None:
- message = '%s is deprecated, use %s instead' % (old_name, new_class.__name__)
+ message = "%s is deprecated, use %s instead" % (old_name, new_class.__name__)
- class_dict['__deprecation_warning__'] = message
- class_dict['__deprecation_warning_version__'] = version
- class_dict['__deprecation_warning_stacklevel__'] = 5
+ class_dict["__deprecation_warning__"] = message
+ class_dict["__deprecation_warning_version__"] = version
+ class_dict["__deprecation_warning_stacklevel__"] = 3
+
+ if module_name:
+ class_dict["__deprecation_warning_module_name__"] = module_name
+ else:
+ class_dict["__deprecation_warning_module_name__"] = _get_module_name(1)
return class_deprecated(old_name, (new_class,), class_dict)
@@ -311,7 +335,12 @@ def class_moved(new_class, old_name=None, message=None, version=None):
old_name = new_class.__name__
if message is None:
- message = 'class %s is now available as %s.%s' % (
- old_name, new_class.__module__, new_class.__name__)
+ message = "class %s is now available as %s.%s" % (
+ old_name,
+ new_class.__module__,
+ new_class.__name__,
+ )
+
+ module_name = _get_module_name(1)
- return class_renamed(old_name, new_class, message=message)
+ return class_renamed(old_name, new_class, message=message, module_name=module_name)
diff --git a/test/test_deprecation.py b/test/test_deprecation.py
index 096978e..cac8664 100644
--- a/test/test_deprecation.py
+++ b/test/test_deprecation.py
@@ -48,8 +48,7 @@ class RawInputTC(TestCase):
class AnyClass(object, metaclass=deprecation.class_deprecated):
pass
AnyClass()
- self.assertEqual(self.messages,
- ['[logilab.common] AnyClass is deprecated'])
+ self.assertEqual(self.messages, ["[test_deprecation] AnyClass is deprecated"])
def test_class_renamed(self):
class AnyClass(object):
@@ -58,8 +57,9 @@ class RawInputTC(TestCase):
OldClass = deprecation.class_renamed("OldClass", AnyClass)
OldClass()
- self.assertEqual(self.messages,
- ['[logilab.common] OldClass is deprecated, use AnyClass instead'])
+ self.assertEqual(
+ self.messages, ["[test_deprecation] OldClass is deprecated, use AnyClass instead"]
+ )
def test_class_moved(self):
class AnyClass(object):
@@ -67,36 +67,53 @@ class RawInputTC(TestCase):
OldClass = deprecation.class_moved(new_class=AnyClass, old_name="OldName")
OldClass()
- self.assertEqual(self.messages,
- ['[logilab.common] class OldName is now available as unittest_deprecation.AnyClass'])
+ self.assertEqual(
+ self.messages,
+ ["[test_deprecation] class OldName is now available as test_deprecation.AnyClass"],
+ )
self.messages = []
AnyClass = deprecation.class_moved(new_class=AnyClass)
AnyClass()
- self.assertEqual(self.messages,
- ['[logilab.common] class AnyClass is now available as unittest_deprecation.AnyClass'])
+ self.assertEqual(
+ self.messages,
+ ["[test_deprecation] class AnyClass is now available as test_deprecation.AnyClass"],
+ )
def test_deprecated_func(self):
any_func = deprecation.callable_deprecated()(self.mk_func())
any_func()
- any_func = deprecation.callable_deprecated('message')(self.mk_func())
+ any_func = deprecation.callable_deprecated("message")(self.mk_func())
any_func()
- self.assertEqual(self.messages,
- ['[logilab.common] The function "any_func" is deprecated', '[logilab.common] message'])
+ self.assertEqual(
+ self.messages,
+ [
+ '[test_deprecation] The function "any_func" is deprecated',
+ "[test_deprecation] message",
+ ],
+ )
def test_deprecated_decorator(self):
@deprecation.callable_deprecated()
def any_func():
pass
+
any_func()
- @deprecation.callable_deprecated('message')
+
+ @deprecation.callable_deprecated("message")
def any_func():
pass
+
any_func()
- self.assertEqual(self.messages,
- ['[logilab.common] The function "any_func" is deprecated', '[logilab.common] message'])
+ self.assertEqual(
+ self.messages,
+ [
+ '[test_deprecation] The function "any_func" is deprecated',
+ "[test_deprecation] message",
+ ],
+ )
def test_attribute_renamed(self):
@deprecation.attribute_renamed(old_name="old", new_name="new")
@@ -106,9 +123,13 @@ class RawInputTC(TestCase):
some_class = SomeClass()
self.assertEqual(some_class.old, some_class.new)
- self.assertEqual(self.messages,
- ['[logilab.common] SomeClass.old has been renamed and is deprecated, use SomeClass.new '
- 'instead'])
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation] SomeClass.old has been renamed and is deprecated, "
+ "use SomeClass.new instead"
+ ],
+ )
some_class.old = 43
self.assertEqual(some_class.old, 43)
@@ -127,9 +148,13 @@ class RawInputTC(TestCase):
self.assertEqual(some_function(new=42), 42)
self.assertEqual(some_function(old=42), 42)
- self.assertEqual(self.messages,
- ['[logilab.common] argument old of callable some_function has been renamed and is '
- 'deprecated, use keyword argument new instead'])
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation] argument old of callable some_function has been renamed and is "
+ "deprecated, use keyword argument new instead"
+ ],
+ )
with self.assertRaises(ValueError):
some_function(new=42, old=42)
@@ -141,9 +166,13 @@ class RawInputTC(TestCase):
self.assertEqual(some_function(new=42), 42)
self.assertEqual(some_function(new=10, old=20), 10)
- self.assertEqual(self.messages,
- ['[logilab.common] argument old of callable some_function has been removed and is '
- 'deprecated'])
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation] argument old of callable some_function has been removed and is "
+ "deprecated"
+ ],
+ )
def test_callable_renamed(self):
def any_func():
@@ -152,16 +181,23 @@ class RawInputTC(TestCase):
old_func = deprecation.callable_renamed("old_func", any_func)
old_func()
- self.assertEqual(self.messages,
- ['[logilab.common] old_func has been renamed and is deprecated, uses any_func instead'])
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation] old_func has been renamed and is deprecated, "
+ "uses any_func instead"
+ ],
+ )
def test_moved(self):
- module = 'data.deprecation'
- any_func = deprecation.callable_moved(module, 'moving_target')
+ module = "data.deprecation"
+ any_func = deprecation.callable_moved(module, "moving_target")
any_func()
- self.assertEqual(self.messages,
- ['[logilab.common] object moving_target has been moved to module data.deprecation'])
+ self.assertEqual(
+ self.messages,
+ ["[test_deprecation] object moving_target has been moved to module data.deprecation"],
+ )
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest_main()
diff --git a/test/test_modutils.py b/test/test_modutils.py
index cd639e1..bcffe07 100644
--- a/test/test_modutils.py
+++ b/test/test_modutils.py
@@ -90,12 +90,15 @@ class get_module_part_tc(ModutilsTestCase):
"""given a dotted name return the module part of the name"""
def test_knownValues_get_module_part_1(self):
- self.assertEqual(modutils.get_module_part('logilab.common.modutils'),
- 'logilab.common.modutils')
+ self.assertEqual(
+ modutils.get_module_part("logilab.common.modutils"), "logilab.common.modutils"
+ )
def test_knownValues_get_module_part_2(self):
- self.assertEqual(modutils.get_module_part('logilab.common.modutils.get_module_part'),
- 'logilab.common.modutils')
+ self.assertEqual(
+ modutils.get_module_part("logilab.common.modutils.get_module_part"),
+ "logilab.common.modutils",
+ )
def test_knownValues_get_module_part_3(self):
"""relative import from given file"""
@@ -120,10 +123,13 @@ class modpath_from_file_tc(ModutilsTestCase):
def test_knownValues_modpath_from_file_1(self):
with warnings.catch_warnings(record=True) as warns:
- self.assertEqual(modutils.modpath_from_file(modutils.__file__),
- ['logilab', 'common', 'modutils'])
- self.assertIn('[logilab.common] you should avoid using modpath_from_file()',
- [str(w.message) for w in warns])
+ self.assertEqual(
+ modutils.modpath_from_file(modutils.__file__), ["logilab", "common", "modutils"]
+ )
+ self.assertIn(
+ "[logilab.common.modutils] you should avoid using modpath_from_file()",
+ [str(w.message) for w in warns],
+ )
def test_knownValues_modpath_from_file_2(self):
self.assertEqual(modutils.modpath_from_file('unittest_modutils.py',
diff --git a/test/test_registry.py b/test/test_registry.py
index 148bf77..377ec48 100644
--- a/test/test_registry.py
+++ b/test/test_registry.py
@@ -189,11 +189,13 @@ class RegistryStoreTC(TestCase):
with warnings.catch_warnings(record=True) as warns:
store.register_objects([self.datapath('regobjects.py'),
self.datapath('regobjects2.py')])
- self.assertIn('[logilab.common] use register_modnames() instead',
- [str(w.message) for w in warns])
self.assertEqual(['zereg'], list(store.keys()))
self.assertEqual(set(('appobject1', 'appobject2', 'appobject3')),
set(store['zereg']))
+ self.assertIn(
+ "[logilab.common.registry] use register_modnames() instead",
+ [str(w.message) for w in warns],
+ )
def test_autoload_modnames(self):
store = RegistryStore()