diff options
-rw-r--r-- | logilab/common/deprecation.py | 95 | ||||
-rw-r--r-- | test/test_deprecation.py | 96 | ||||
-rw-r--r-- | test/test_modutils.py | 22 | ||||
-rw-r--r-- | test/test_registry.py | 6 |
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() |