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