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