Source code for tlslite.utils.deprecations

# Copyright (c) 2018 Hubert Kario
#
# See the LICENSE file for legal information regarding use of this file.
"""Methods for deprecating old names for arguments or attributes."""
import warnings
import inspect
from functools import wraps


[docs] def deprecated_class_name(old_name, warn="Class name '{old_name}' is deprecated, " "please use '{new_name}'"): """ Class decorator to deprecate a use of class. :param str old_name: the deprecated name that will be registered, but will raise warnings if used. :param str warn: DeprecationWarning format string for informing the user what is the current class name, uses 'old_name' for the deprecated keyword name and the 'new_name' for the current one. Example: "Old name: {old_nam}, use '{new_name}' instead". """ def _wrap(obj): assert callable(obj) def _warn(): warnings.warn(warn.format(old_name=old_name, new_name=obj.__name__), DeprecationWarning, stacklevel=3) def _wrap_with_warn(func, is_inspect): @wraps(func) def _func(*args, **kwargs): if is_inspect: # XXX: If use another name to call, # you will not get the warning. # we do this instead of subclassing or metaclass as # we want to isinstance(new_name(), old_name) and # isinstance(old_name(), new_name) to work frame = inspect.currentframe().f_back code = inspect.getframeinfo(frame).code_context if [line for line in code if '{0}('.format(old_name) in line]: _warn() else: _warn() return func(*args, **kwargs) return _func # Make old name available. frame = inspect.currentframe().f_back if old_name in frame.f_globals: raise NameError("Name '{0}' already in use.".format(old_name)) if inspect.isclass(obj): obj.__init__ = _wrap_with_warn(obj.__init__, True) placeholder = obj else: placeholder = _wrap_with_warn(obj, False) frame.f_globals[old_name] = placeholder return obj return _wrap
[docs] def deprecated_params(names, warn="Param name '{old_name}' is deprecated, " "please use '{new_name}'"): """Decorator to translate obsolete names and warn about their use. :param dict names: dictionary with pairs of new_name: old_name that will be used for translating obsolete param names to new names :param str warn: DeprecationWarning format string for informing the user what is the current parameter name, uses 'old_name' for the deprecated keyword name and 'new_name' for the current one. Example: "Old name: {old_name}, use {new_name} instead". """ def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for new_name, old_name in names.items(): if old_name in kwargs: if new_name in kwargs: raise TypeError("got multiple values for keyword " "argument '{0}'".format(new_name)) warnings.warn(warn.format(old_name=old_name, new_name=new_name), DeprecationWarning, stacklevel=2) kwargs[new_name] = kwargs.pop(old_name) return func(*args, **kwargs) return wrapper return decorator
[docs] def deprecated_instance_attrs(names, warn="Attribute '{old_name}' is deprecated, " "please use '{new_name}'"): """Decorator to deprecate class instance attributes. Translates all names in `names` to use new names and emits warnings if the translation was necessary. Does apply only to instance variables and attributes (won't modify behaviour of class variables, static methods, etc. :param dict names: dictionary with paris of new_name: old_name that will be used to translate the calls :param str warn: DeprecationWarning format string for informing the user what is the current parameter name, uses 'old_name' for the deprecated keyword name and 'new_name' for the current one. Example: "Old name: {old_name}, use {new_name} instead". """ # reverse the dict as we're looking for old attributes, not new ones names = dict((j, i) for i, j in names.items()) def decorator(clazz): def getx(self, name, __old_getx=getattr(clazz, "__getattr__", None)): if name in names: warnings.warn(warn.format(old_name=name, new_name=names[name]), DeprecationWarning, stacklevel=2) return getattr(self, names[name]) if __old_getx: if hasattr(__old_getx, "__func__"): return __old_getx.__func__(self, name) return __old_getx(self, name) raise AttributeError("'{0}' object has no attribute '{1}'" .format(clazz.__name__, name)) getx.__name__ = "__getattr__" clazz.__getattr__ = getx def setx(self, name, value, __old_setx=getattr(clazz, "__setattr__")): if name in names: warnings.warn(warn.format(old_name=name, new_name=names[name]), DeprecationWarning, stacklevel=2) setattr(self, names[name], value) else: __old_setx(self, name, value) setx.__name__ = "__setattr__" clazz.__setattr__ = setx def delx(self, name, __old_delx=getattr(clazz, "__delattr__")): if name in names: warnings.warn(warn.format(old_name=name, new_name=names[name]), DeprecationWarning, stacklevel=2) delattr(self, names[name]) else: __old_delx(self, name) delx.__name__ = "__delattr__" clazz.__delattr__ = delx return clazz return decorator
[docs] def deprecated_attrs(names, warn="Attribute '{old_name}' is deprecated, " "please use '{new_name}'"): """Decorator to deprecate all specified attributes in class. Translates all names in `names` to use new names and emits warnings if the translation was necessary. Note: uses metaclass magic so is incompatible with other metaclass uses :param dict names: dictionary with paris of new_name: old_name that will be used to translate the calls :param str warn: DeprecationWarning format string for informing the user what is the current parameter name, uses 'old_name' for the deprecated keyword name and 'new_name' for the current one. Example: "Old name: {old_name}, use {new_name} instead". """ # prepare metaclass for handling all the class methods, class variables # and static methods (as they don't go through instance's __getattr__) class DeprecatedProps(type): pass metaclass = deprecated_instance_attrs(names, warn)(DeprecatedProps) def wrapper(cls): cls = deprecated_instance_attrs(names, warn)(cls) # apply metaclass orig_vars = cls.__dict__.copy() slots = orig_vars.get('__slots__') if slots is not None: if isinstance(slots, str): slots = [slots] for slots_var in slots: orig_vars.pop(slots_var) orig_vars.pop('__dict__', None) orig_vars.pop('__weakref__', None) return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper
[docs] def deprecated_method(message): """Decorator for deprecating methods. :param ste message: The message you want to display. """ def decorator(func): @wraps(func) def wrapper(*args, **kwargs): warnings.warn("{0} is a deprecated method. {1}".format(func.__name__, message), DeprecationWarning, stacklevel=2) return func(*args, **kwargs) return wrapper return decorator