codekingpro/portable-devtools
114k
1import warnings2from collections.abc import Iterable3from functools import wraps4from inspect import Parameter, signature5 6 7def _deprecate_positional_args(*, version: str):8 """Decorator for methods that issues warnings for positional arguments.9 Using the keyword-only argument syntax in pep 3102, arguments after the10 * will issue a warning when passed as a positional argument.11 12 Args:13 version (`str`):14 The version when positional arguments will result in error.15 """16 17 def _inner_deprecate_positional_args(f):18 sig = signature(f)19 kwonly_args = []20 all_args = []21 for name, param in sig.parameters.items():22 if param.kind == Parameter.POSITIONAL_OR_KEYWORD:23 all_args.append(name)24 elif param.kind == Parameter.KEYWORD_ONLY:25 kwonly_args.append(name)26 27 @wraps(f)28 def inner_f(*args, **kwargs):29 extra_args = len(args) - len(all_args)30 if extra_args <= 0:31 return f(*args, **kwargs)32 # extra_args > 033 args_msg = [34 f"{name}='{arg}'" if isinstance(arg, str) else f"{name}={arg}"35 for name, arg in zip(kwonly_args[:extra_args], args[-extra_args:])36 ]37 args_msg = ", ".join(args_msg)38 warnings.warn(39 f"Deprecated positional argument(s) used in '{f.__name__}': pass"40 f" {args_msg} as keyword args. From version {version} passing these"41 " as positional arguments will result in an error,",42 FutureWarning,43 )44 kwargs.update(zip(sig.parameters, args))45 return f(**kwargs)46 47 return inner_f48 49 return _inner_deprecate_positional_args50 51 52def _deprecate_arguments(53 *,54 version: str,55 deprecated_args: Iterable[str],56 custom_message: str | None = None,57):58 """Decorator to issue warnings when using deprecated arguments.59 60 TODO: could be useful to be able to set a custom error message.61 62 Args:63 version (`str`):64 The version when deprecated arguments will result in error.65 deprecated_args (`list[str]`):66 List of the arguments to be deprecated.67 custom_message (`str`, *optional*):68 Warning message that is raised. If not passed, a default warning message69 will be created.70 """71 72 def _inner_deprecate_positional_args(f):73 sig = signature(f)74 75 @wraps(f)76 def inner_f(*args, **kwargs):77 # Check for used deprecated arguments78 used_deprecated_args = []79 for _, parameter in zip(args, sig.parameters.values()):80 if parameter.name in deprecated_args:81 used_deprecated_args.append(parameter.name)82 for kwarg_name, kwarg_value in kwargs.items():83 if (84 # If argument is deprecated but still used85 kwarg_name in deprecated_args86 # And then the value is not the default value87 and kwarg_value != sig.parameters[kwarg_name].default88 ):89 used_deprecated_args.append(kwarg_name)90 91 # Warn and proceed92 if len(used_deprecated_args) > 0:93 message = (94 f"Deprecated argument(s) used in '{f.__name__}':"95 f" {', '.join(used_deprecated_args)}. Will not be supported from"96 f" version '{version}'."97 )98 if custom_message is not None:99 message += "\n\n" + custom_message100 warnings.warn(message, FutureWarning)101 return f(*args, **kwargs)102 103 return inner_f104 105 return _inner_deprecate_positional_args106 107 108def _deprecate_method(*, version: str, message: str | None = None):109 """Decorator to issue warnings when using a deprecated method.110 111 Args:112 version (`str`):113 The version when deprecated arguments will result in error.114 message (`str`, *optional*):115 Warning message that is raised. If not passed, a default warning message116 will be created.117 """118 119 def _inner_deprecate_method(f):120 name = f.__name__121 if name == "__init__":122 name = f.__qualname__.split(".")[0] # class name instead of method name123 124 @wraps(f)125 def inner_f(*args, **kwargs):126 warning_message = (127 f"'{name}' (from '{f.__module__}') is deprecated and will be removed from version '{version}'."128 )129 if message is not None:130 warning_message += " " + message131 warnings.warn(warning_message, FutureWarning)132 return f(*args, **kwargs)133 134 return inner_f135 136 return _inner_deprecate_method137 