def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_arg_values') @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" if _PRINT_DEPRECATION_WARNINGS: named_args = tf_inspect.getcallargs(func, *args, **kwargs) for arg_name, arg_value in deprecated_kwargs.items(): if arg_name in named_args and _safe_eq( named_args[arg_name], arg_value): if (func, arg_name) not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[(func, arg_name)] = True logging.warning( 'From %s: calling %s (from %s) with %s=%s is deprecated and ' 'will be removed %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, arg_value, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) doc = _add_deprecated_arg_value_notice_to_docstring( func.__doc__, date, instructions, deprecated_kwargs) return tf_decorator.make_decorator(func, new_func, 'deprecated', doc)
def experimental(func): """Decorator for marking functions or methods experimental. This decorator logs an experimental warning whenever the decorated function is called. It has the following format: <function> (from <module>) is experimental and may change or be removed at any time, and without warning. <function> will include the class name if it is a method. It also edits the docstring of the function: ' (experimental)' is appended to the first line of the docstring and a notice is prepended to the rest of the docstring. Args: func: A function or method to mark experimental. Returns: Decorated function or method. """ decorator_utils.validate_callable(func, 'experimental') @functools.wraps(func) def new_func(*args, **kwargs): logging.warning( '%s (from %s) is experimental and may change or be removed at ' 'any time, and without warning.', decorator_utils.get_qualified_name(func), func.__module__) return func(*args, **kwargs) new_func.__doc__ = _add_experimental_function_notice_to_docstring( func.__doc__) return new_func
def keyword_args_only(func): """Decorator for marking specific function accepting keyword args only. This decorator raises a `ValueError` if the input `func` is called with any non-keyword args. This prevents the caller from providing the arguments in wrong order. Args: func: The function or method needed to be decorated. Returns: Decorated function or method. Raises: ValueError: If `func` is not callable. """ decorator_utils.validate_callable(func, "keyword_args_only") @functools.wraps(func) def new_func(*args, **kwargs): """Keyword args only wrapper.""" if args: raise ValueError( "Must use keyword args to call {}.".format(func.__name__)) return func(**kwargs) return new_func
def keyword_args_only(func): """Decorator for marking specific function accepting keyword args only. This decorator raises a `ValueError` if the input `func` is called with any non-keyword args. This prevents the caller from providing the arguments in wrong order. Args: func: The function or method needed to be decorated. Returns: Decorated function or method. Raises: ValueError: If `func` is not callable. """ decorator_utils.validate_callable(func, "keyword_args_only") @functools.wraps(func) def new_func(*args, **kwargs): """Keyword args only wrapper.""" if args: raise ValueError( f"The function {func.__name__} only accepts keyword arguments. " "Do not pass positional arguments. Received the following positional " f"arguments: {args}") return func(**kwargs) return new_func
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, "deprecated_arg_values") @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" named_args = inspect.getcallargs(func, *args, **kwargs) for arg_name, arg_value in deprecated_kwargs.items(): if arg_name in named_args and named_args[arg_name] == arg_value: logging.warning( "From %s: calling %s (from %s) with %s=%s is deprecated and will " "be removed after %s.\nInstructions for updating:\n%s", _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, arg_value, date, instructions, ) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_arg_notice_to_docstring(func.__doc__, date, instructions) return new_func
def test_callable(self): class TestClass(object): def __call__(self): pass decorator_utils.validate_callable(TestClass(), "test")
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, "deprecated_args") deprecated_arg_names = _get_arg_names_to_ok_vals() arg_spec = inspect.getargspec(func) deprecated_positions = _get_deprecated_positional_arguments(deprecated_arg_names, arg_spec) is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.keywords in deprecated_arg_names if len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len( deprecated_arg_names_or_tuples ): known_args = arg_spec.args + [arg_spec.varargs, arg_spec.keywords] missing_args = [arg_name for arg_name in deprecated_arg_names if arg_name not in known_args] raise ValueError( "The following deprecated arguments are not present " "in the function signature: %s. " "Found next arguments: %s." % (missing_args, known_args) ) @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" invalid_args = [] named_args = inspect.getcallargs(func, *args, **kwargs) for arg_name, spec in iter(deprecated_positions.items()): if spec.position < len(args) and not (spec.has_ok_value and named_args[arg_name] == spec.ok_value): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.keywords) for arg_name in deprecated_arg_names: if arg_name in kwargs and not ( deprecated_positions[arg_name].has_ok_value and (named_args[arg_name] == deprecated_positions[arg_name].ok_value) ): invalid_args.append(arg_name) for arg_name in invalid_args: logging.warning( "From %s: calling %s (from %s) with %s is deprecated and will " "be removed after %s.\nInstructions for updating:\n%s", _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, date, instructions, ) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_arg_notice_to_docstring(func.__doc__, date, instructions) return new_func
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_args') deprecated_arg_names = _get_arg_names_to_ok_vals() arg_spec = inspect.getargspec(func) deprecated_positions = _get_deprecated_positional_arguments( deprecated_arg_names, arg_spec) is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.keywords in deprecated_arg_names if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len(deprecated_arg_names_or_tuples)): known_args = arg_spec.args + [arg_spec.varargs, arg_spec.keywords] missing_args = [ arg_name for arg_name in deprecated_arg_names if arg_name not in known_args ] raise ValueError( 'The following deprecated arguments are not present ' 'in the function signature: %s. ' 'Found next arguments: %s.' % (missing_args, known_args)) @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" invalid_args = [] named_args = inspect.getcallargs(func, *args, **kwargs) for arg_name, spec in iter(deprecated_positions.items()): if (spec.position < len(args) and not (spec.has_ok_value and named_args[arg_name] == spec.ok_value)): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.keywords) for arg_name in deprecated_arg_names: if (arg_name in kwargs and not (deprecated_positions[arg_name].has_ok_value and (named_args[arg_name] == deprecated_positions[arg_name].ok_value))): invalid_args.append(arg_name) for arg_name in invalid_args: logging.warning( 'From %s: calling %s (from %s) with %s is deprecated and will ' 'be removed after %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, date, instructions) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions) return new_func
def deprecated_wrapper(func_or_class): """Deprecation wrapper.""" if isinstance(func_or_class, type): # If a class is deprecated, you actually want to wrap the constructor. cls = func_or_class if cls.__new__ is object.__new__: func = cls.__init__ constructor_name = '__init__' else: func = cls.__new__ constructor_name = '__new__' else: cls = None constructor_name = None func = func_or_class decorator_utils.validate_callable(func, 'deprecated') @functools.wraps(func) def new_func(*args, **kwargs): # pylint: disable=missing-docstring if _PRINT_DEPRECATION_WARNINGS: if func not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[func] = True logging.warning( 'From %s: %s (from %s) is deprecated and will be removed %s.\n' 'Instructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) doc_controls.set_deprecated(new_func) new_func = tf_decorator.make_decorator( func, new_func, 'deprecated', _add_deprecated_function_notice_to_docstring( func.__doc__, date, instructions)) if cls is None: return new_func else: # Insert the wrapped function as the constructor setattr(cls, constructor_name, new_func) # And update the docstring of the class. cls.__doc__ = _add_deprecated_function_notice_to_docstring( cls.__doc__, date, instructions) return cls
def deprecated_wrapper(func): """Deprecation wrapper.""" decorator_utils.validate_callable(func, 'deprecated') @functools.wraps(func) def new_func(*args, **kwargs): logging.warning( '%s (from %s) is deprecated and will be removed after %s.\n' 'Instructions for updating:\n%s', decorator_utils.get_qualified_name(func), func.__module__, date, instructions) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_function_notice_to_docstring( func.__doc__, date, instructions) return new_func
def deprecated_wrapper(func): """Deprecation wrapper.""" decorator_utils.validate_callable(func, 'deprecated') @functools.wraps(func) def new_func(*args, **kwargs): logging.warning( 'From %s: %s (from %s) is deprecated and will be removed ' 'after %s.\n' 'Instructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, date, instructions) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_function_notice_to_docstring( func.__doc__, date, instructions) return new_func
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_args') arg_spec = inspect.getargspec(func) deprecated_positions = [(i, arg_name) for (i, arg_name) in enumerate(arg_spec.args) if arg_name in deprecated_arg_names] is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.keywords in deprecated_arg_names if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len(deprecated_arg_names)): known_args = arg_spec.args + [arg_spec.varargs, arg_spec.keywords] missing_args = [ arg_name for arg_name in deprecated_arg_names if arg_name not in known_args ] raise ValueError( 'The following deprecated arguments are not present ' 'in the function signature: %s' % missing_args) @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" invalid_args = [] for (i, arg_name) in deprecated_positions: if i < len(args): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.keywords) for arg_name in deprecated_arg_names: if arg_name in kwargs: invalid_args.append(arg_name) for arg_name in invalid_args: logging.warning( 'Calling %s (from %s) with %s is deprecated and will be removed ' 'after %s.\nInstructions for updating:\n%s', decorator_utils.get_qualified_name(func), func.__module__, arg_name, date, instructions) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions) return new_func
def deprecated_wrapper(func): """Deprecation wrapper.""" decorator_utils.validate_callable(func, 'deprecated') @functools.wraps(func) def new_func(*args, **kwargs): logging.warning( 'From %s: %s (from %s) is deprecated and will be removed %s.\n' 'Instructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) return tf_decorator.make_decorator( func, new_func, 'deprecated', _add_deprecated_function_notice_to_docstring(func.__doc__, date, instructions))
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_arg_values') @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" named_args = inspect.getcallargs(func, *args, **kwargs) for arg_name, arg_value in deprecated_kwargs.items(): if arg_name in named_args and named_args[arg_name] == arg_value: logging.warning( 'From %s: calling %s (from %s) with %s=%s is deprecated and will ' 'be removed after %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, arg_value, date, instructions) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions) return new_func
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_args') arg_spec = inspect.getargspec(func) deprecated_positions = [ (i, arg_name) for (i, arg_name) in enumerate(arg_spec.args) if arg_name in deprecated_arg_names] is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.keywords in deprecated_arg_names if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len(deprecated_arg_names)): known_args = arg_spec.args + [arg_spec.varargs, arg_spec.keywords] missing_args = [arg_name for arg_name in deprecated_arg_names if arg_name not in known_args] raise ValueError('The following deprecated arguments are not present ' 'in the function signature: %s. ' 'Found next arguments: %s.' % (missing_args, known_args)) @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" invalid_args = [] for (i, arg_name) in deprecated_positions: if i < len(args): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.keywords) for arg_name in deprecated_arg_names: if arg_name in kwargs: invalid_args.append(arg_name) for arg_name in invalid_args: logging.warning( 'From %s: calling %s (from %s) with %s is deprecated and will ' 'be removed after %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, date, instructions) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions) return new_func
def deprecated_wrapper(func): """Deprecation wrapper.""" decorator_utils.validate_callable(func, 'deprecated') @functools.wraps(func) def new_func(*args, **kwargs): # pylint: disable=missing-docstring if _PRINT_DEPRECATION_WARNINGS: logging.warning( 'From %s: %s (from %s) is deprecated and will be removed %s.\n' 'Instructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) return tf_decorator.make_decorator( func, new_func, 'deprecated', _add_deprecated_function_notice_to_docstring( func.__doc__, date, instructions))
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_arg_values') @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" named_args = tf_inspect.getcallargs(func, *args, **kwargs) for arg_name, arg_value in deprecated_kwargs.items(): if arg_name in named_args and named_args[arg_name] == arg_value: logging.warning( 'From %s: calling %s (from %s) with %s=%s is deprecated and will ' 'be removed %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, arg_value, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) return tf_decorator.make_decorator(func, new_func, 'deprecated', _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions))
def deprecated_wrapper(func): """Deprecation wrapper.""" decorator_utils.validate_callable(func, 'deprecated') @functools.wraps(func) def new_func(*args, **kwargs): # pylint: disable=missing-docstring if _PRINT_DEPRECATION_WARNINGS: if func not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[func] = True logging.warning( 'From %s: %s (from %s) is deprecated and will be removed %s.\n' 'Instructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) return tf_decorator.make_decorator( func, new_func, 'deprecated', _add_deprecated_function_notice_to_docstring(func.__doc__, date, instructions))
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_args') arg_spec = tf_inspect.getfullargspec(func) deprecated_positions = _get_deprecated_positional_arguments( deprecated_arg_names, arg_spec) is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.varkw in deprecated_arg_names if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len(deprecated_arg_names_or_tuples)): known_args = arg_spec.args + [arg_spec.varargs, arg_spec.varkw] missing_args = [arg_name for arg_name in deprecated_arg_names if arg_name not in known_args] raise ValueError('The following deprecated arguments are not present ' 'in the function signature: %s. ' 'Found next arguments: %s.' % (missing_args, known_args)) def _same_value(a, b): """A comparison operation that works for multiple object types. Returns True for two empty lists, two numeric values with the same value, etc. Returns False for (pd.DataFrame, None), and other pairs which should not be considered equivalent. Args: a: value one of the comparison. b: value two of the comparison. Returns: A boolean indicating whether the two inputs are the same value for the purposes of deprecation. """ if a is b: return True try: equality = a == b if isinstance(equality, bool): return equality except TypeError: return False return False @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" # TODO(apassos) figure out a way to have reasonable performance with # deprecation warnings and eager mode. if is_in_graph_mode.IS_IN_GRAPH_MODE() and _PRINT_DEPRECATION_WARNINGS: invalid_args = [] named_args = tf_inspect.getcallargs(func, *args, **kwargs) for arg_name, spec in iter(deprecated_positions.items()): if (spec.position < len(args) and not (spec.has_ok_value and _same_value(named_args[arg_name], spec.ok_value))): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.varkw) for arg_name in deprecated_arg_names: if (arg_name in kwargs and not (deprecated_positions[arg_name].has_ok_value and _same_value(named_args[arg_name], deprecated_positions[arg_name].ok_value))): invalid_args.append(arg_name) for arg_name in invalid_args: if (func, arg_name) not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[(func, arg_name)] = True logging.warning( 'From %s: calling %s (from %s) with %s is deprecated and will ' 'be removed %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) doc = _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions, sorted(deprecated_arg_names.keys())) return tf_decorator.make_decorator(func, new_func, 'deprecated', doc)
def deprecated_alias(deprecated_name, name, func_or_class, warn_once=True): """Deprecate a symbol in favor of a new name with identical semantics. This function is meant to be used when defining a backwards-compatibility alias for a symbol which has been moved. For example: module1.py: ```python class NewNameForClass: pass ``` module2.py: ```python import module1 DeprecatedNameForClass = deprecated_alias( deprecated_name='module2.DeprecatedNameForClass', name='module1.NewNameForClass', module1.NewNameForClass) ``` This function works for classes and functions. For classes, it creates a new class which is functionally identical (it inherits from the original, and overrides its constructor), but which prints a deprecation warning when an instance is created. It also adds a deprecation notice to the class' docstring. For functions, it returns a function wrapped by `tf_decorator.make_decorator`. That function prints a warning when used, and has a deprecation notice in its docstring. This is more or less equivalent (the deprecation warning has slightly different text) to writing: ```python @deprecated def deprecated_alias(original_args): real_function(original_args) ``` Args: deprecated_name: The name of the symbol that is being deprecated, to be used in the warning message. This should be its fully qualified name to avoid confusion. name: The name of the symbol that is to be used instead of the deprecated name. This should be a fully qualified name to avoid confusion. func_or_class: The (non-deprecated) class or function for which a deprecated alias should be created. warn_once: If True (the default), only print a deprecation warning the first time this function is used, or the class is instantiated. Returns: A wrapped version of `func_or_class` which prints a deprecation warning on use and has a modified docstring. """ if tf_inspect.isclass(func_or_class): # Make a new class with __init__ wrapped in a warning. class _NewClass(func_or_class): # pylint: disable=missing-docstring __doc__ = decorator_utils.add_notice_to_docstring( func_or_class.__doc__, 'Please use %s instead.' % name, 'DEPRECATED CLASS', '(deprecated)', ['THIS CLASS IS DEPRECATED. ' 'It will be removed in a future version. ']) __name__ = func_or_class.__name__ __module__ = _call_location(outer=True) @_wrap_decorator(func_or_class.__init__) def __init__(self, *args, **kwargs): if hasattr(_NewClass.__init__, '__func__'): # Python 2 _NewClass.__init__.__func__.__doc__ = func_or_class.__init__.__doc__ else: # Python 3 _NewClass.__init__.__doc__ = func_or_class.__init__.__doc__ if _PRINT_DEPRECATION_WARNINGS: # We're making the alias as we speak. The original may have other # aliases, so we cannot use it to check for whether it's already been # warned about. if _NewClass.__init__ not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[_NewClass.__init__] = True logging.warning( 'From %s: The name %s is deprecated. Please use %s instead.\n', _call_location(), deprecated_name, name) super(_NewClass, self).__init__(*args, **kwargs) return _NewClass else: decorator_utils.validate_callable(func_or_class, 'deprecated') # Make a wrapper for the original @functools.wraps(func_or_class) def new_func(*args, **kwargs): # pylint: disable=missing-docstring if _PRINT_DEPRECATION_WARNINGS: # We're making the alias as we speak. The original may have other # aliases, so we cannot use it to check for whether it's already been # warned about. if new_func not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[new_func] = True logging.warning( 'From %s: The name %s is deprecated. Please use %s instead.\n', _call_location(), deprecated_name, name) return func_or_class(*args, **kwargs) return tf_decorator.make_decorator( func_or_class, new_func, 'deprecated', _add_deprecated_function_notice_to_docstring( func_or_class.__doc__, None, 'Please use %s instead.' % name))
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_args') deprecated_arg_names = _get_arg_names_to_ok_vals() arg_spec = tf_inspect.getargspec(func) deprecated_positions = _get_deprecated_positional_arguments( deprecated_arg_names, arg_spec) is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.keywords in deprecated_arg_names if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len(deprecated_arg_names_or_tuples)): known_args = arg_spec.args + [arg_spec.varargs, arg_spec.keywords] missing_args = [ arg_name for arg_name in deprecated_arg_names if arg_name not in known_args ] raise ValueError( 'The following deprecated arguments are not present ' 'in the function signature: %s. ' 'Found next arguments: %s.' % (missing_args, known_args)) def _same_value(a, b): """A comparison operation that works for multiple object types. Returns True for two empty lists, two numeric values with the same value, etc. Returns False for (pd.DataFrame, None), and other pairs which should not be considered equivalent. Args: a: value one of the comparison. b: value two of the comparison. Returns: A boolean indicating whether the two inputs are the same value for the purposes of deprecation. """ if a is b: return True try: equality = a == b if isinstance(equality, bool): return equality except TypeError: return False return False @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" invalid_args = [] named_args = tf_inspect.getcallargs(func, *args, **kwargs) for arg_name, spec in iter(deprecated_positions.items()): if (spec.position < len(args) and not (spec.has_ok_value and _same_value( named_args[arg_name], spec.ok_value))): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.keywords) for arg_name in deprecated_arg_names: if (arg_name in kwargs and not (deprecated_positions[arg_name].has_ok_value and _same_value( named_args[arg_name], deprecated_positions[arg_name].ok_value))): invalid_args.append(arg_name) for arg_name in invalid_args: logging.warning( 'From %s: calling %s (from %s) with %s is deprecated and will ' 'be removed %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) return tf_decorator.make_decorator( func, new_func, 'deprecated', _add_deprecated_arg_notice_to_docstring(func.__doc__, date, instructions))
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_args') deprecated_arg_names = _get_arg_names_to_ok_vals() arg_spec = inspect.getargspec(func) deprecated_positions = _get_deprecated_positional_arguments( deprecated_arg_names, arg_spec) is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.keywords in deprecated_arg_names if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len(deprecated_arg_names_or_tuples)): known_args = arg_spec.args + [arg_spec.varargs, arg_spec.keywords] missing_args = [arg_name for arg_name in deprecated_arg_names if arg_name not in known_args] raise ValueError('The following deprecated arguments are not present ' 'in the function signature: %s. ' 'Found next arguments: %s.' % (missing_args, known_args)) def _same_value(a, b): """A comparison operation that works for multiple object types. Returns True for two empty lists, two numeric values with the same value, etc. Returns False for (pd.DataFrame, None), and other pairs which should not be considered equivalent. Args: a: value one of the comparison. b: value two of the comparison. Returns: A boolean indicating whether the two inputs are the same value for the purposes of deprecation. """ if a is b: return True try: equality = a == b if isinstance(equality, bool): return equality except TypeError: return False return False @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" invalid_args = [] named_args = inspect.getcallargs(func, *args, **kwargs) for arg_name, spec in iter(deprecated_positions.items()): if (spec.position < len(args) and not (spec.has_ok_value and _same_value(named_args[arg_name], spec.ok_value))): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.keywords) for arg_name in deprecated_arg_names: if (arg_name in kwargs and not (deprecated_positions[arg_name].has_ok_value and _same_value(named_args[arg_name], deprecated_positions[arg_name].ok_value))): invalid_args.append(arg_name) for arg_name in invalid_args: logging.warning( 'From %s: calling %s (from %s) with %s is deprecated and will ' 'be removed after %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, date, instructions) return func(*args, **kwargs) new_func.__doc__ = _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions) return new_func
def test_method(self): decorator_utils.validate_callable(self.test_method, "test")
def deprecated_wrapper(func): """Deprecation decorator.""" decorator_utils.validate_callable(func, 'deprecated_args') arg_spec = tf_inspect.getfullargspec(func) deprecated_positions = _get_deprecated_positional_arguments( deprecated_arg_names, arg_spec) is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names is_kwargs_deprecated = arg_spec.varkw in deprecated_arg_names if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated != len(deprecated_arg_names_or_tuples)): known_args = (arg_spec.args + arg_spec.kwonlyargs + [arg_spec.varargs, arg_spec.varkw]) missing_args = [ arg_name for arg_name in deprecated_arg_names if arg_name not in known_args ] raise ValueError( 'The following deprecated arguments are not present ' f'in the function signature: {missing_args}. ' 'Expected arguments from the following list: ' f'{known_args}.') def _same_value(a, b): """A comparison operation that works for multiple object types. Returns True for two empty lists, two numeric values with the same value, etc. Returns False for (pd.DataFrame, None), and other pairs which should not be considered equivalent. Args: a: value one of the comparison. b: value two of the comparison. Returns: A boolean indicating whether the two inputs are the same value for the purposes of deprecation. """ if a is b: return True try: equality = a == b if isinstance(equality, bool): return equality except TypeError: return False return False @functools.wraps(func) def new_func(*args, **kwargs): """Deprecation wrapper.""" # TODO(apassos) figure out a way to have reasonable performance with # deprecation warnings and eager mode. if is_in_graph_mode.IS_IN_GRAPH_MODE( ) and _PRINT_DEPRECATION_WARNINGS: invalid_args = [] named_args = tf_inspect.getcallargs(func, *args, **kwargs) for arg_name, spec in iter(deprecated_positions.items()): if (spec.position < len(args) and not (spec.has_ok_value and _same_value( named_args[arg_name], spec.ok_value))): invalid_args.append(arg_name) if is_varargs_deprecated and len(args) > len(arg_spec.args): invalid_args.append(arg_spec.varargs) if is_kwargs_deprecated and kwargs: invalid_args.append(arg_spec.varkw) for arg_name in deprecated_arg_names: if (arg_name in kwargs and not ( deprecated_positions[arg_name].has_ok_value and _same_value( named_args[arg_name], deprecated_positions[arg_name].ok_value))): invalid_args.append(arg_name) for arg_name in invalid_args: if (func, arg_name) not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[(func, arg_name)] = True logging.warning( 'From %s: calling %s (from %s) with %s is deprecated and will ' 'be removed %s.\nInstructions for updating:\n%s', _call_location(), decorator_utils.get_qualified_name(func), func.__module__, arg_name, 'in a future version' if date is None else ('after %s' % date), instructions) return func(*args, **kwargs) doc = _add_deprecated_arg_notice_to_docstring( func.__doc__, date, instructions, sorted(deprecated_arg_names.keys())) return tf_decorator.make_decorator(func, new_func, 'deprecated', doc)
def deprecated_alias(deprecated_name, name, func_or_class, warn_once=True): """Deprecate a symbol in favor of a new name with identical semantics. This function is meant to be used when defining a backwards-compatibility alias for a symbol which has been moved. For example: module1.py: ```python class NewNameForClass: pass ``` module2.py: ```python import module1 DeprecatedNameForClass = deprecated_alias( deprecated_name='module2.DeprecatedNameForClass', name='module1.NewNameForClass', func_or_class=module1.NewNameForClass) ``` This function works for classes and functions. For classes, it creates a new class which is functionally identical (it inherits from the original, and overrides its constructor), but which prints a deprecation warning when an instance is created. It also adds a deprecation notice to the class' docstring. For functions, it returns a function wrapped by `tf_decorator.make_decorator`. That function prints a warning when used, and has a deprecation notice in its docstring. This is more or less equivalent (the deprecation warning has slightly different text) to writing: ```python @deprecated def deprecated_alias(original_args): real_function(original_args) ``` Args: deprecated_name: The name of the symbol that is being deprecated, to be used in the warning message. This should be its fully qualified name to avoid confusion. name: The name of the symbol that is to be used instead of the deprecated name. This should be a fully qualified name to avoid confusion. func_or_class: The (non-deprecated) class or function for which a deprecated alias should be created. warn_once: If True (the default), only print a deprecation warning the first time this function is used, or the class is instantiated. Returns: A wrapped version of `func_or_class` which prints a deprecation warning on use and has a modified docstring. """ if tf_inspect.isclass(func_or_class): # Make a new class with __init__ wrapped in a warning. class _NewClass(func_or_class): # pylint: disable=missing-docstring __doc__ = decorator_utils.add_notice_to_docstring( func_or_class.__doc__, 'Please use %s instead.' % name, 'DEPRECATED CLASS', '(deprecated)', [ 'THIS CLASS IS DEPRECATED. ' 'It will be removed in a future version. ' ]) __name__ = func_or_class.__name__ __module__ = _call_location(outer=True) @_wrap_decorator(func_or_class.__init__) def __init__(self, *args, **kwargs): if hasattr(_NewClass.__init__, '__func__'): # Python 2 _NewClass.__init__.__func__.__doc__ = func_or_class.__init__.__doc__ else: # Python 3 _NewClass.__init__.__doc__ = func_or_class.__init__.__doc__ if _PRINT_DEPRECATION_WARNINGS: # We're making the alias as we speak. The original may have other # aliases, so we cannot use it to check for whether it's already been # warned about. if _NewClass.__init__ not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[_NewClass.__init__] = True logging.warning( 'From %s: The name %s is deprecated. Please use %s instead.\n', _call_location(), deprecated_name, name) super(_NewClass, self).__init__(*args, **kwargs) return _NewClass else: decorator_utils.validate_callable(func_or_class, 'deprecated') # Make a wrapper for the original @functools.wraps(func_or_class) def new_func(*args, **kwargs): # pylint: disable=missing-docstring if _PRINT_DEPRECATION_WARNINGS: # We're making the alias as we speak. The original may have other # aliases, so we cannot use it to check for whether it's already been # warned about. if new_func not in _PRINTED_WARNING: if warn_once: _PRINTED_WARNING[new_func] = True logging.warning( 'From %s: The name %s is deprecated. Please use %s instead.\n', _call_location(), deprecated_name, name) return func_or_class(*args, **kwargs) return tf_decorator.make_decorator( func_or_class, new_func, 'deprecated', _add_deprecated_function_notice_to_docstring( func_or_class.__doc__, None, 'Please use %s instead.' % name))
def test_function(self): decorator_utils.validate_callable(_test_function, "test")
def test_partial(self): partial = functools.partial(_test_function, unused_arg=7) decorator_utils.validate_callable(partial, "test")