• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15
16"""Tensor utility functions."""
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21import collections
22import functools
23import inspect
24import re
25
26from tensorflow.python.platform import tf_logging as logging
27from tensorflow.python.util import decorator_utils
28from tensorflow.python.util import is_in_graph_mode
29from tensorflow.python.util import tf_contextlib
30from tensorflow.python.util import tf_decorator
31from tensorflow.python.util import tf_inspect
32from tensorflow.tools.docs import doc_controls
33
34
35# Allow deprecation warnings to be silenced temporarily with a context manager.
36_PRINT_DEPRECATION_WARNINGS = True
37
38# Remember which deprecation warnings have been printed already.
39_PRINTED_WARNING = {}
40
41
42class DeprecatedNamesAlreadySet(Exception):
43  """Raised when setting deprecated names multiple times for the same symbol."""
44  pass
45
46
47def _add_deprecated_function_notice_to_docstring(doc, date, instructions):
48  """Adds a deprecation notice to a docstring for deprecated functions."""
49  main_text = ['THIS FUNCTION IS DEPRECATED. It will be removed %s.' %
50               ('in a future version' if date is None else ('after %s' % date))]
51  if instructions:
52    main_text.append('Instructions for updating:')
53  return decorator_utils.add_notice_to_docstring(
54      doc, instructions,
55      'DEPRECATED FUNCTION',
56      '(deprecated)', main_text)
57
58
59def _add_deprecated_arg_notice_to_docstring(doc, date, instructions,
60                                            deprecated_names):
61  """Adds a deprecation notice to a docstring for deprecated arguments."""
62
63  deprecation_string = ', '.join(sorted(deprecated_names))
64
65  return decorator_utils.add_notice_to_docstring(
66      doc, instructions, 'DEPRECATED FUNCTION ARGUMENTS',
67      '(deprecated arguments)', [
68          'SOME ARGUMENTS ARE DEPRECATED: `(%s)`. '
69          'They will be removed %s.' %
70          (deprecation_string, 'in a future version' if date is None else
71           ('after %s' % date)), 'Instructions for updating:'
72      ])
73
74
75def _add_deprecated_arg_value_notice_to_docstring(doc, date, instructions,
76                                                  deprecated_name_value_dict):
77  """Adds a deprecation notice to a docstring for deprecated arguments."""
78
79  deprecation_string = ', '.join(
80      '%s=%r' % (key, value)
81      for key, value in sorted(deprecated_name_value_dict.items()))
82
83  when = 'in a future version' if date is None else ('after %s' % date)
84
85  return decorator_utils.add_notice_to_docstring(
86      doc, instructions, 'DEPRECATED FUNCTION ARGUMENT VALUES',
87      '(deprecated argument values)', [
88          'SOME ARGUMENT VALUES ARE DEPRECATED: `(%s)`. '
89          'They will be removed %s.' % (deprecation_string, when),
90          'Instructions for updating:'
91      ])
92
93
94def _validate_deprecation_args(date, instructions):
95  if date is not None and not re.match(r'20\d\d-[01]\d-[0123]\d', date):
96    raise ValueError(f'Date must be in format YYYY-MM-DD. Received: {date}')
97  if not instructions:
98    raise ValueError(
99        'Don\'t deprecate things without conversion instructions! Specify '
100        'the `instructions` argument.')
101
102
103def _call_location(outer=False):
104  """Returns call location given level up from current call."""
105  # Two up: <_call_location>, <_call_location's caller>
106  # tf_inspect is not required here. Please ignore the lint warning by adding
107  # DISABLE_IMPORT_INSPECT_CHECK=TRUE to your cl description. Using it caused
108  # test timeouts (b/189384061).
109  f = inspect.currentframe().f_back.f_back
110  parent = f.f_back
111  if outer and parent is not None:
112    f = parent
113  return '{}:{}'.format(f.f_code.co_filename, f.f_lineno)
114
115
116def _safe_eq(a, b):
117  if a is None or b is None:
118    return a is None and b is None
119  return a == b
120
121
122def _wrap_decorator(wrapped_function):
123  """Indicate that one function wraps another.
124
125  This decorator wraps a function using `tf_decorator.make_decorator`
126  so that doc generation scripts can pick up original function
127  signature.
128  It would be better to use @functools.wrap decorator, but it would
129  not update function signature to match wrapped function in Python 2.
130
131  Args:
132    wrapped_function: The function that decorated function wraps.
133
134  Returns:
135    Function that accepts wrapper function as an argument and returns
136    `TFDecorator` instance.
137  """
138  def wrapper(wrapper_func):
139    return tf_decorator.make_decorator(wrapped_function, wrapper_func)
140  return wrapper
141
142
143def deprecated_alias(deprecated_name, name, func_or_class, warn_once=True):
144  """Deprecate a symbol in favor of a new name with identical semantics.
145
146  This function is meant to be used when defining a backwards-compatibility
147  alias for a symbol which has been moved. For example:
148
149  module1.py:
150  ```python
151  class NewNameForClass: pass
152  ```
153
154  module2.py:
155  ```python
156  import module1
157
158  DeprecatedNameForClass = deprecated_alias(
159    deprecated_name='module2.DeprecatedNameForClass',
160    name='module1.NewNameForClass',
161    func_or_class=module1.NewNameForClass)
162  ```
163
164  This function works for classes and functions.
165
166  For classes, it creates a new class which is functionally identical (it
167  inherits from the original, and overrides its constructor), but which prints
168  a deprecation warning when an instance is created. It also adds a deprecation
169  notice to the class' docstring.
170
171  For functions, it returns a function wrapped by `tf_decorator.make_decorator`.
172  That function prints a warning when used, and has a deprecation notice in its
173  docstring. This is more or less equivalent (the deprecation warning has
174  slightly different text) to writing:
175
176  ```python
177  @deprecated
178  def deprecated_alias(original_args):
179    real_function(original_args)
180  ```
181
182  Args:
183    deprecated_name: The name of the symbol that is being deprecated, to be used
184      in the warning message. This should be its fully qualified name to avoid
185      confusion.
186    name: The name of the symbol that is to be used instead of the deprecated
187      name. This should be a fully qualified name to avoid confusion.
188    func_or_class: The (non-deprecated) class or function for which a deprecated
189      alias should be created.
190    warn_once: If True (the default), only print a deprecation warning the first
191      time this function is used, or the class is instantiated.
192
193  Returns:
194    A wrapped version of `func_or_class` which prints a deprecation warning on
195    use and has a modified docstring.
196  """
197  if tf_inspect.isclass(func_or_class):
198
199    # Make a new class with __init__ wrapped in a warning.
200    class _NewClass(func_or_class):  # pylint: disable=missing-docstring
201      __doc__ = decorator_utils.add_notice_to_docstring(
202          func_or_class.__doc__, 'Please use %s instead.' % name,
203          'DEPRECATED CLASS',
204          '(deprecated)', ['THIS CLASS IS DEPRECATED. '
205                           'It will be removed in a future version. '])
206      __name__ = func_or_class.__name__
207      __module__ = _call_location(outer=True)
208
209      @_wrap_decorator(func_or_class.__init__)
210      def __init__(self, *args, **kwargs):
211        if hasattr(_NewClass.__init__, '__func__'):
212          # Python 2
213          _NewClass.__init__.__func__.__doc__ = func_or_class.__init__.__doc__
214        else:
215          # Python 3
216          _NewClass.__init__.__doc__ = func_or_class.__init__.__doc__
217
218        if _PRINT_DEPRECATION_WARNINGS:
219          # We're making the alias as we speak. The original may have other
220          # aliases, so we cannot use it to check for whether it's already been
221          # warned about.
222          if _NewClass.__init__ not in _PRINTED_WARNING:
223            if warn_once:
224              _PRINTED_WARNING[_NewClass.__init__] = True
225            logging.warning(
226                'From %s: The name %s is deprecated. Please use %s instead.\n',
227                _call_location(), deprecated_name, name)
228        super(_NewClass, self).__init__(*args, **kwargs)
229
230    return _NewClass
231  else:
232    decorator_utils.validate_callable(func_or_class, 'deprecated')
233
234    # Make a wrapper for the original
235    @functools.wraps(func_or_class)
236    def new_func(*args, **kwargs):  # pylint: disable=missing-docstring
237      if _PRINT_DEPRECATION_WARNINGS:
238        # We're making the alias as we speak. The original may have other
239        # aliases, so we cannot use it to check for whether it's already been
240        # warned about.
241        if new_func not in _PRINTED_WARNING:
242          if warn_once:
243            _PRINTED_WARNING[new_func] = True
244          logging.warning(
245              'From %s: The name %s is deprecated. Please use %s instead.\n',
246              _call_location(), deprecated_name, name)
247      return func_or_class(*args, **kwargs)
248    return tf_decorator.make_decorator(
249        func_or_class, new_func, 'deprecated',
250        _add_deprecated_function_notice_to_docstring(
251            func_or_class.__doc__, None, 'Please use %s instead.' % name))
252
253
254def deprecated_endpoints(*args):
255  """Decorator for marking endpoints deprecated.
256
257  This decorator does not print deprecation messages.
258  TODO(annarev): eventually start printing deprecation warnings when
259  @deprecation_endpoints decorator is added.
260
261  Args:
262    *args: Deprecated endpoint names.
263
264  Returns:
265    A function that takes symbol as an argument and adds
266    _tf_deprecated_api_names to that symbol.
267    _tf_deprecated_api_names would be set to a list of deprecated
268    endpoint names for the symbol.
269  """
270  def deprecated_wrapper(func):
271    # pylint: disable=protected-access
272    if '_tf_deprecated_api_names' in func.__dict__:
273      raise DeprecatedNamesAlreadySet(
274          f'Cannot set deprecated names for {func.__name__} to {args}. '
275          'Deprecated names are already set to '
276          f'{func._tf_deprecated_api_names}.')
277    func._tf_deprecated_api_names = args
278    # pylint: disable=protected-access
279    return func
280  return deprecated_wrapper
281
282
283def deprecated(date, instructions, warn_once=True):
284  """Decorator for marking functions or methods deprecated.
285
286  This decorator logs a deprecation warning whenever the decorated function is
287  called. It has the following format:
288
289    <function> (from <module>) is deprecated and will be removed after <date>.
290    Instructions for updating:
291    <instructions>
292
293  If `date` is None, 'after <date>' is replaced with 'in a future version'.
294  <function> will include the class name if it is a method.
295
296  It also edits the docstring of the function: ' (deprecated)' is appended
297  to the first line of the docstring and a deprecation notice is prepended
298  to the rest of the docstring.
299
300  Args:
301    date: String or None. The date the function is scheduled to be removed.
302      Must be ISO 8601 (YYYY-MM-DD), or None.
303    instructions: String. Instructions on how to update code using the
304      deprecated function.
305    warn_once: Boolean. Set to `True` to warn only the first time the decorated
306      function is called. Otherwise, every call will log a warning.
307
308  Returns:
309    Decorated function or method.
310
311  Raises:
312    ValueError: If date is not None or in ISO 8601 format, or instructions are
313      empty.
314  """
315  _validate_deprecation_args(date, instructions)
316
317  def deprecated_wrapper(func_or_class):
318    """Deprecation wrapper."""
319    if isinstance(func_or_class, type):
320      # If a class is deprecated, you actually want to wrap the constructor.
321      cls = func_or_class
322      if cls.__new__ is object.__new__:
323        func = cls.__init__
324        constructor_name = '__init__'
325      else:
326        func = cls.__new__
327        constructor_name = '__new__'
328
329    else:
330      cls = None
331      constructor_name = None
332      func = func_or_class
333
334    decorator_utils.validate_callable(func, 'deprecated')
335    @functools.wraps(func)
336    def new_func(*args, **kwargs):  # pylint: disable=missing-docstring
337      if _PRINT_DEPRECATION_WARNINGS:
338        if func not in _PRINTED_WARNING:
339          if warn_once:
340            _PRINTED_WARNING[func] = True
341          logging.warning(
342              'From %s: %s (from %s) is deprecated and will be removed %s.\n'
343              'Instructions for updating:\n%s',
344              _call_location(), decorator_utils.get_qualified_name(func),
345              func.__module__,
346              'in a future version' if date is None else ('after %s' % date),
347              instructions)
348      return func(*args, **kwargs)
349
350    doc_controls.set_deprecated(new_func)
351    new_func = tf_decorator.make_decorator(
352        func, new_func, 'deprecated',
353        _add_deprecated_function_notice_to_docstring(func.__doc__, date,
354                                                     instructions))
355
356    if cls is None:
357      return new_func
358    else:
359      # Insert the wrapped function as the constructor
360      setattr(cls, constructor_name, new_func)
361
362      # And update the docstring of the class.
363      cls.__doc__ = _add_deprecated_function_notice_to_docstring(
364          cls.__doc__, date, instructions)
365
366      return cls
367
368  return deprecated_wrapper
369
370
371DeprecatedArgSpec = collections.namedtuple(
372    'DeprecatedArgSpec', ['position', 'has_ok_value', 'ok_value'])
373
374
375def deprecated_args(date, instructions, *deprecated_arg_names_or_tuples,
376                    **kwargs):
377  """Decorator for marking specific function arguments as deprecated.
378
379  This decorator logs a deprecation warning whenever the decorated function is
380  called with the deprecated argument. It has the following format:
381
382    Calling <function> (from <module>) with <arg> is deprecated and will be
383    removed after <date>. Instructions for updating:
384      <instructions>
385
386  If `date` is None, 'after <date>' is replaced with 'in a future version'.
387  <function> includes the class name if it is a method.
388
389  It also edits the docstring of the function: ' (deprecated arguments)' is
390  appended to the first line of the docstring and a deprecation notice is
391  prepended to the rest of the docstring.
392
393  Args:
394    date: String or None. The date the function is scheduled to be removed.
395      Must be ISO 8601 (YYYY-MM-DD), or None.
396    instructions: String. Instructions on how to update code using the
397      deprecated function.
398    *deprecated_arg_names_or_tuples: String or 2-Tuple (String,
399      ok_val).  The string is the deprecated argument name.
400      Optionally, an ok-value may be provided.  If the user provided
401      argument equals this value, the warning is suppressed.
402    **kwargs: If `warn_once=False` is passed, every call with a deprecated
403      argument will log a warning. The default behavior is to only warn the
404      first time the function is called with any given deprecated argument.
405      All other kwargs raise `ValueError`.
406
407  Returns:
408    Decorated function or method.
409
410  Raises:
411    ValueError: If date is not None or in ISO 8601 format, instructions are
412      empty, the deprecated arguments are not present in the function
413      signature, the second element of a deprecated_tuple is not a
414      list, or if a kwarg other than `warn_once` is passed.
415  """
416  _validate_deprecation_args(date, instructions)
417  if not deprecated_arg_names_or_tuples:
418    raise ValueError('Specify which argument is deprecated.')
419  if kwargs and list(kwargs.keys()) != ['warn_once']:
420    kwargs.pop('warn_once', None)
421    raise ValueError(f'Illegal argument passed to deprecated_args: {kwargs}')
422  warn_once = kwargs.get('warn_once', True)
423
424  def _get_arg_names_to_ok_vals():
425    """Returns a dict mapping arg_name to DeprecatedArgSpec w/o position."""
426    d = {}
427    for name_or_tuple in deprecated_arg_names_or_tuples:
428      if isinstance(name_or_tuple, tuple):
429        d[name_or_tuple[0]] = DeprecatedArgSpec(-1, True, name_or_tuple[1])
430      else:
431        d[name_or_tuple] = DeprecatedArgSpec(-1, False, None)
432    return d
433
434  def _get_deprecated_positional_arguments(names_to_ok_vals, arg_spec):
435    """Builds a dictionary from deprecated arguments to their spec.
436
437    Returned dict is keyed by argument name.
438    Each value is a DeprecatedArgSpec with the following fields:
439       position: The zero-based argument position of the argument
440         within the signature.  None if the argument isn't found in
441         the signature.
442       ok_values:  Values of this argument for which warning will be
443         suppressed.
444
445    Args:
446      names_to_ok_vals: dict from string arg_name to a list of values,
447        possibly empty, which should not elicit a warning.
448      arg_spec: Output from tf_inspect.getfullargspec on the called function.
449
450    Returns:
451      Dictionary from arg_name to DeprecatedArgSpec.
452    """
453    # Extract argument list
454    arg_space = arg_spec.args + arg_spec.kwonlyargs
455    arg_name_to_pos = {
456        name: pos for pos, name in enumerate(arg_space)}
457    deprecated_positional_args = {}
458    for arg_name, spec in iter(names_to_ok_vals.items()):
459      if arg_name in arg_name_to_pos:
460        pos = arg_name_to_pos[arg_name]
461        deprecated_positional_args[arg_name] = DeprecatedArgSpec(
462            pos, spec.has_ok_value, spec.ok_value)
463    return deprecated_positional_args
464
465  deprecated_arg_names = _get_arg_names_to_ok_vals()
466
467  def deprecated_wrapper(func):
468    """Deprecation decorator."""
469    decorator_utils.validate_callable(func, 'deprecated_args')
470
471    arg_spec = tf_inspect.getfullargspec(func)
472    deprecated_positions = _get_deprecated_positional_arguments(
473        deprecated_arg_names, arg_spec)
474
475    is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names
476    is_kwargs_deprecated = arg_spec.varkw in deprecated_arg_names
477
478    if (len(deprecated_positions) + is_varargs_deprecated
479        + is_kwargs_deprecated
480        != len(deprecated_arg_names_or_tuples)):
481      known_args = (arg_spec.args
482                    + arg_spec.kwonlyargs
483                    + [arg_spec.varargs, arg_spec.varkw])
484      missing_args = [arg_name for arg_name in deprecated_arg_names
485                      if arg_name not in known_args]
486      raise ValueError('The following deprecated arguments are not present '
487                       f'in the function signature: {missing_args}. '
488                       'Expected arguments from the following list: '
489                       f'{known_args}.')
490
491    def _same_value(a, b):
492      """A comparison operation that works for multiple object types.
493
494      Returns True for two empty lists, two numeric values with the
495      same value, etc.
496
497      Returns False for (pd.DataFrame, None), and other pairs which
498      should not be considered equivalent.
499
500      Args:
501        a: value one of the comparison.
502        b: value two of the comparison.
503
504      Returns:
505        A boolean indicating whether the two inputs are the same value
506        for the purposes of deprecation.
507      """
508      if a is b:
509        return True
510      try:
511        equality = a == b
512        if isinstance(equality, bool):
513          return equality
514      except TypeError:
515        return False
516      return False
517
518    @functools.wraps(func)
519    def new_func(*args, **kwargs):
520      """Deprecation wrapper."""
521      # TODO(apassos) figure out a way to have reasonable performance with
522      # deprecation warnings and eager mode.
523      if is_in_graph_mode.IS_IN_GRAPH_MODE() and _PRINT_DEPRECATION_WARNINGS:
524        invalid_args = []
525        named_args = tf_inspect.getcallargs(func, *args, **kwargs)
526        for arg_name, spec in iter(deprecated_positions.items()):
527          if (spec.position < len(args) and
528              not (spec.has_ok_value and
529                   _same_value(named_args[arg_name], spec.ok_value))):
530            invalid_args.append(arg_name)
531        if is_varargs_deprecated and len(args) > len(arg_spec.args):
532          invalid_args.append(arg_spec.varargs)
533        if is_kwargs_deprecated and kwargs:
534          invalid_args.append(arg_spec.varkw)
535        for arg_name in deprecated_arg_names:
536          if (arg_name in kwargs and
537              not (deprecated_positions[arg_name].has_ok_value and
538                   _same_value(named_args[arg_name],
539                               deprecated_positions[arg_name].ok_value))):
540            invalid_args.append(arg_name)
541        for arg_name in invalid_args:
542          if (func, arg_name) not in _PRINTED_WARNING:
543            if warn_once:
544              _PRINTED_WARNING[(func, arg_name)] = True
545            logging.warning(
546                'From %s: calling %s (from %s) with %s is deprecated and will '
547                'be removed %s.\nInstructions for updating:\n%s',
548                _call_location(), decorator_utils.get_qualified_name(func),
549                func.__module__, arg_name,
550                'in a future version' if date is None else ('after %s' % date),
551                instructions)
552      return func(*args, **kwargs)
553
554    doc = _add_deprecated_arg_notice_to_docstring(
555        func.__doc__, date, instructions, sorted(deprecated_arg_names.keys()))
556    return tf_decorator.make_decorator(func, new_func, 'deprecated', doc)
557
558  return deprecated_wrapper
559
560
561def deprecated_arg_values(date, instructions, warn_once=True,
562                          **deprecated_kwargs):
563  """Decorator for marking specific function argument values as deprecated.
564
565  This decorator logs a deprecation warning whenever the decorated function is
566  called with the deprecated argument values. It has the following format:
567
568    Calling <function> (from <module>) with <arg>=<value> is deprecated and
569    will be removed after <date>. Instructions for updating:
570      <instructions>
571
572  If `date` is None, 'after <date>' is replaced with 'in a future version'.
573  <function> will include the class name if it is a method.
574
575  It also edits the docstring of the function: ' (deprecated arguments)' is
576  appended to the first line of the docstring and a deprecation notice is
577  prepended to the rest of the docstring.
578
579  Args:
580    date: String or None. The date the function is scheduled to be removed.
581      Must be ISO 8601 (YYYY-MM-DD), or None
582    instructions: String. Instructions on how to update code using the
583      deprecated function.
584    warn_once: If `True`, warn only the first time this function is called with
585      deprecated argument values. Otherwise, every call (with a deprecated
586      argument value) will log a warning.
587    **deprecated_kwargs: The deprecated argument values.
588
589  Returns:
590    Decorated function or method.
591
592  Raises:
593    ValueError: If date is not None or in ISO 8601 format, or instructions are
594      empty.
595  """
596  _validate_deprecation_args(date, instructions)
597  if not deprecated_kwargs:
598    raise ValueError('Specify which argument values are deprecated.')
599
600  def deprecated_wrapper(func):
601    """Deprecation decorator."""
602    decorator_utils.validate_callable(func, 'deprecated_arg_values')
603    @functools.wraps(func)
604    def new_func(*args, **kwargs):
605      """Deprecation wrapper."""
606      if _PRINT_DEPRECATION_WARNINGS:
607        named_args = tf_inspect.getcallargs(func, *args, **kwargs)
608        for arg_name, arg_value in deprecated_kwargs.items():
609          if arg_name in named_args and _safe_eq(named_args[arg_name],
610                                                 arg_value):
611            if (func, arg_name) not in _PRINTED_WARNING:
612              if warn_once:
613                _PRINTED_WARNING[(func, arg_name)] = True
614              logging.warning(
615                  'From %s: calling %s (from %s) with %s=%s is deprecated and '
616                  'will be removed %s.\nInstructions for updating:\n%s',
617                  _call_location(), decorator_utils.get_qualified_name(func),
618                  func.__module__, arg_name, arg_value, 'in a future version'
619                  if date is None else ('after %s' % date), instructions)
620      return func(*args, **kwargs)
621
622    doc = _add_deprecated_arg_value_notice_to_docstring(
623        func.__doc__, date, instructions, deprecated_kwargs)
624    return tf_decorator.make_decorator(func, new_func, 'deprecated', doc)
625
626  return deprecated_wrapper
627
628
629def deprecated_argument_lookup(new_name, new_value, old_name, old_value):
630  """Looks up deprecated argument name and ensures both are not used.
631
632  Args:
633    new_name: new name of argument
634    new_value: value of new argument (or None if not used)
635    old_name: old name of argument
636    old_value: value of old argument (or None if not used)
637  Returns:
638    The effective argument that should be used.
639  Raises:
640    ValueError: if new_value and old_value are both non-null
641  """
642  if old_value is not None:
643    if new_value is not None:
644      raise ValueError(f"Cannot specify both '{old_name}' and '{new_name}'.")
645    return old_value
646  return new_value
647
648
649def rewrite_argument_docstring(old_doc, old_argument, new_argument):
650  return old_doc.replace('`%s`' % old_argument, '`%s`' % new_argument).replace(
651      '%s:' % old_argument, '%s:' % new_argument)
652
653
654@tf_contextlib.contextmanager
655def silence():
656  """Temporarily silence deprecation warnings."""
657  global _PRINT_DEPRECATION_WARNINGS
658  print_deprecation_warnings = _PRINT_DEPRECATION_WARNINGS
659  _PRINT_DEPRECATION_WARNINGS = False
660  yield
661  _PRINT_DEPRECATION_WARNINGS = print_deprecation_warnings
662
663
664class HiddenTfApiAttribute(property):
665  """Hides a class attribute from the public API.
666
667  Attributes in public classes can be hidden from the API by having an '_' in
668  front of the name (e.g. ClassName._variables). This doesn't work when
669  attributes or methods are inherited from a parent class. To hide inherited
670  attributes, set their values to be `deprecation.hide_attribute_from_api`.
671  For example, this is used in V2 Estimator to hide the deprecated
672  export_savedmodel method:
673    class EstimatorV2(Estimator):
674       export_savedmodel = deprecation.hide_attribute_from_api('...')
675  """
676
677  def __init__(self, deprecation_message):
678
679    def raise_error(unused_self):
680      raise AttributeError(deprecation_message)
681
682    super(HiddenTfApiAttribute, self).__init__(raise_error)
683
684
685hide_attribute_from_api = HiddenTfApiAttribute  # pylint: disable=invalid-name
686
687# TODO(kathywu): Remove once cl/246395236 is submitted.
688HIDDEN_ATTRIBUTE = HiddenTfApiAttribute('This attribute has been deprecated.')
689