#!/usr/bin/env python # -*- encoding: utf-8 -*- # CREATED:2015-02-15 10:06:03 by Brian McFee """Helpful tools for deprecation""" import warnings from inspect import signature, isclass, Parameter from functools import wraps from decorator import decorator __all__ = ["moved", "deprecated", "deprecate_positional_args"] def moved(*, moved_from, version, version_removed): """This is a decorator which can be used to mark functions as moved/renamed. It will result in a warning being emitted when the function is used. """ def __wrapper(func, *args, **kwargs): """Warn the user, and then proceed.""" warnings.warn( "{:s}\n\tThis function was moved to '{:s}.{:s}' in " "librosa version {:s}." "\n\tThis alias will be removed in librosa version " "{:s}.".format( moved_from, func.__module__, func.__name__, version, version_removed ), category=DeprecationWarning, stacklevel=3, # Would be 2, but the decorator adds a level ) return func(*args, **kwargs) return decorator(__wrapper) def deprecated(*, version, version_removed): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.""" def __wrapper(func, *args, **kwargs): """Warn the user, and then proceed.""" warnings.warn( "{:s}.{:s}\n\tDeprecated as of librosa version {:s}." "\n\tIt will be removed in librosa version {:s}.".format( func.__module__, func.__name__, version, version_removed ), category=DeprecationWarning, stacklevel=3, # Would be 2, but the decorator adds a level ) return func(*args, **kwargs) return decorator(__wrapper) # Borrowed from sklearn def deprecate_positional_args(func=None, *, version="0.10"): """Decorator for methods that issues warnings for positional arguments. Using the keyword-only argument syntax in pep 3102, arguments after the * will issue a warning when passed as a positional argument. Parameters ---------- func : callable, default=None Function to check arguments on. version : callable, default="0.10" The version when positional arguments will result in error. """ def _inner_deprecate_positional_args(f): sig = signature(f) kwonly_args = [] all_args = [] for name, param in sig.parameters.items(): if param.kind == Parameter.POSITIONAL_OR_KEYWORD: all_args.append(name) elif param.kind == Parameter.KEYWORD_ONLY: kwonly_args.append(name) @wraps(f) def inner_f(*args, **kwargs): extra_args = len(args) - len(all_args) if extra_args <= 0: return f(*args, **kwargs) # extra_args > 0 args_msg = [ "{}={}".format(name, arg) for name, arg in zip(kwonly_args[:extra_args], args[-extra_args:]) ] args_msg = ", ".join(args_msg) warnings.warn( f"Pass {args_msg} as keyword args. From version " f"{version} passing these as positional arguments " "will result in an error", FutureWarning, stacklevel=2, ) kwargs.update(zip(sig.parameters, args)) return f(**kwargs) return inner_f if func is not None: return _inner_deprecate_positional_args(func) return _inner_deprecate_positional_args