Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/util/deprecation.py: 19%

237 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-05 06:32 +0000

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"""Tensor utility functions.""" 

16import collections 

17import functools 

18import inspect 

19import re 

20 

21from tensorflow.python.platform import tf_logging as logging 

22from tensorflow.python.util import decorator_utils 

23from tensorflow.python.util import is_in_graph_mode 

24from tensorflow.python.util import tf_contextlib 

25from tensorflow.python.util import tf_decorator 

26from tensorflow.python.util import tf_inspect 

27from tensorflow.tools.docs import doc_controls 

28 

29# Allow deprecation warnings to be silenced temporarily with a context manager. 

30_PRINT_DEPRECATION_WARNINGS = True 

31 

32# Remember which deprecation warnings have been printed already. 

33_PRINTED_WARNING = {} 

34 

35 

36class DeprecatedNamesAlreadySet(Exception): 

37 """Raised when setting deprecated names multiple times for the same symbol.""" 

38 pass 

39 

40 

41def _add_deprecated_function_notice_to_docstring(doc, date, instructions): 

42 """Adds a deprecation notice to a docstring for deprecated functions.""" 

43 main_text = [ 

44 'THIS FUNCTION IS DEPRECATED. It will be removed %s.' % 

45 ('in a future version' if date is None else ('after %s' % date)) 

46 ] 

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, decorator_name): 

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 decorator_name: The name of the decorator. 

139 

140 Returns: 

141 Function that accepts wrapper function as an argument and returns 

142 `TFDecorator` instance. 

143 """ 

144 

145 def wrapper(wrapper_func): 

146 return tf_decorator.make_decorator(wrapped_function, wrapper_func, 

147 decorator_name) 

148 

149 return wrapper 

150 

151 

152def deprecated_alias(deprecated_name, name, func_or_class, warn_once=True): 

153 """Deprecate a symbol in favor of a new name with identical semantics. 

154 

155 This function is meant to be used when defining a backwards-compatibility 

156 alias for a symbol which has been moved. For example: 

157 

158 module1.py: 

159 ```python 

160 class NewNameForClass: pass 

161 ``` 

162 

163 module2.py: 

164 ```python 

165 import module1 

166 

167 DeprecatedNameForClass = deprecated_alias( 

168 deprecated_name='module2.DeprecatedNameForClass', 

169 name='module1.NewNameForClass', 

170 func_or_class=module1.NewNameForClass) 

171 ``` 

172 

173 This function works for classes and functions. 

174 

175 For classes, it creates a new class which is functionally identical (it 

176 inherits from the original, and overrides its constructor), but which prints 

177 a deprecation warning when an instance is created. It also adds a deprecation 

178 notice to the class' docstring. 

179 

180 For functions, it returns a function wrapped by `tf_decorator.make_decorator`. 

181 That function prints a warning when used, and has a deprecation notice in its 

182 docstring. This is more or less equivalent (the deprecation warning has 

183 slightly different text) to writing: 

184 

185 ```python 

186 @deprecated 

187 def deprecated_alias(original_args): 

188 real_function(original_args) 

189 ``` 

190 

191 Args: 

192 deprecated_name: The name of the symbol that is being deprecated, to be used 

193 in the warning message. This should be its fully qualified name to avoid 

194 confusion. 

195 name: The name of the symbol that is to be used instead of the deprecated 

196 name. This should be a fully qualified name to avoid confusion. 

197 func_or_class: The (non-deprecated) class or function for which a deprecated 

198 alias should be created. 

199 warn_once: If True (the default), only print a deprecation warning the first 

200 time this function is used, or the class is instantiated. 

201 

202 Returns: 

203 A wrapped version of `func_or_class` which prints a deprecation warning on 

204 use and has a modified docstring. 

205 """ 

206 if tf_inspect.isclass(func_or_class): 

207 

208 # Make a new class with __init__ wrapped in a warning. 

209 class _NewClass(func_or_class): # pylint: disable=missing-docstring 

210 __doc__ = decorator_utils.add_notice_to_docstring( 

211 func_or_class.__doc__, 

212 'Please use %s instead.' % name, 

213 'DEPRECATED CLASS', 

214 '(deprecated)', [('THIS CLASS IS DEPRECATED. ' 

215 'It will be removed in a future version. ')], 

216 notice_type='Deprecated') 

217 __name__ = func_or_class.__name__ 

218 __module__ = _call_location(outer=True) 

219 

220 @_wrap_decorator(func_or_class.__init__, 'deprecated_alias') 

221 def __init__(self, *args, **kwargs): 

222 if hasattr(_NewClass.__init__, '__func__'): 

223 # Python 2 

224 _NewClass.__init__.__func__.__doc__ = func_or_class.__init__.__doc__ 

225 else: 

226 # Python 3 

227 _NewClass.__init__.__doc__ = func_or_class.__init__.__doc__ 

228 

229 if _PRINT_DEPRECATION_WARNINGS: 

230 # We're making the alias as we speak. The original may have other 

231 # aliases, so we cannot use it to check for whether it's already been 

232 # warned about. 

233 if _NewClass.__init__ not in _PRINTED_WARNING: 

234 if warn_once: 

235 _PRINTED_WARNING[_NewClass.__init__] = True 

236 logging.warning( 

237 'From %s: The name %s is deprecated. Please use %s instead.\n', 

238 _call_location(), deprecated_name, name) 

239 super(_NewClass, self).__init__(*args, **kwargs) 

240 

241 return _NewClass 

242 else: 

243 decorator_utils.validate_callable(func_or_class, 'deprecated') 

244 

245 # Make a wrapper for the original 

246 @functools.wraps(func_or_class) 

247 def new_func(*args, **kwargs): # pylint: disable=missing-docstring 

248 if _PRINT_DEPRECATION_WARNINGS: 

249 # We're making the alias as we speak. The original may have other 

250 # aliases, so we cannot use it to check for whether it's already been 

251 # warned about. 

252 if new_func not in _PRINTED_WARNING: 

253 if warn_once: 

254 _PRINTED_WARNING[new_func] = True 

255 logging.warning( 

256 'From %s: The name %s is deprecated. Please use %s instead.\n', 

257 _call_location(), deprecated_name, name) 

258 return func_or_class(*args, **kwargs) 

259 

260 return tf_decorator.make_decorator( 

261 func_or_class, new_func, 'deprecated', 

262 _add_deprecated_function_notice_to_docstring( 

263 func_or_class.__doc__, None, 'Please use %s instead.' % name)) 

264 

265 

266def deprecated_endpoints(*args): 

267 """Decorator for marking endpoints deprecated. 

268 

269 This decorator does not print deprecation messages. 

270 TODO(annarev): eventually start printing deprecation warnings when 

271 @deprecation_endpoints decorator is added. 

272 

273 Args: 

274 *args: Deprecated endpoint names. 

275 

276 Returns: 

277 A function that takes symbol as an argument and adds 

278 _tf_deprecated_api_names to that symbol. 

279 _tf_deprecated_api_names would be set to a list of deprecated 

280 endpoint names for the symbol. 

281 """ 

282 

283 def deprecated_wrapper(func): 

284 # pylint: disable=protected-access 

285 if '_tf_deprecated_api_names' in func.__dict__: 

286 raise DeprecatedNamesAlreadySet( 

287 f'Cannot set deprecated names for {func.__name__} to {args}. ' 

288 'Deprecated names are already set to ' 

289 f'{func._tf_deprecated_api_names}.') 

290 func._tf_deprecated_api_names = args 

291 # pylint: disable=protected-access 

292 return func 

293 

294 return deprecated_wrapper 

295 

296 

297def deprecated(date, instructions, warn_once=True): 

298 """Decorator for marking functions or methods deprecated. 

299 

300 This decorator logs a deprecation warning whenever the decorated function is 

301 called. It has the following format: 

302 

303 <function> (from <module>) is deprecated and will be removed after <date>. 

304 Instructions for updating: 

305 <instructions> 

306 

307 If `date` is None, 'after <date>' is replaced with 'in a future version'. 

308 <function> will include the class name if it is a method. 

309 

310 It also edits the docstring of the function: ' (deprecated)' is appended 

311 to the first line of the docstring and a deprecation notice is prepended 

312 to the rest of the docstring. 

313 

314 Args: 

315 date: String or None. The date the function is scheduled to be removed. Must 

316 be ISO 8601 (YYYY-MM-DD), or None. 

317 instructions: String. Instructions on how to update code using the 

318 deprecated function. 

319 warn_once: Boolean. Set to `True` to warn only the first time the decorated 

320 function is called. Otherwise, every call will log a warning. 

321 

322 Returns: 

323 Decorated function or method. 

324 

325 Raises: 

326 ValueError: If date is not None or in ISO 8601 format, or instructions are 

327 empty. 

328 """ 

329 _validate_deprecation_args(date, instructions) 

330 

331 def deprecated_wrapper(func_or_class): 

332 """Deprecation wrapper.""" 

333 if isinstance(func_or_class, type): 

334 # If a class is deprecated, you actually want to wrap the constructor. 

335 cls = func_or_class 

336 if cls.__new__ is object.__new__: 

337 # If a class defaults to its parent's constructor, wrap that instead. 

338 func = cls.__init__ 

339 constructor_name = '__init__' 

340 decorators, _ = tf_decorator.unwrap(func) 

341 for decorator in decorators: 

342 if decorator.decorator_name == 'deprecated': 

343 # If the parent is already deprecated, there's nothing to do. 

344 return cls 

345 else: 

346 func = cls.__new__ 

347 constructor_name = '__new__' 

348 

349 else: 

350 cls = None 

351 constructor_name = None 

352 func = func_or_class 

353 

354 decorator_utils.validate_callable(func, 'deprecated') 

355 

356 @_wrap_decorator(func, 'deprecated') 

357 def new_func(*args, **kwargs): # pylint: disable=missing-docstring 

358 if _PRINT_DEPRECATION_WARNINGS: 

359 if func not in _PRINTED_WARNING and cls not in _PRINTED_WARNING: 

360 if warn_once: 

361 _PRINTED_WARNING[func] = True 

362 if cls: 

363 _PRINTED_WARNING[cls] = True 

364 logging.warning( 

365 'From %s: %s (from %s) is deprecated and will be removed %s.\n' 

366 'Instructions for updating:\n%s', _call_location(), 

367 decorator_utils.get_qualified_name(func), 

368 func_or_class.__module__, 

369 'in a future version' if date is None else ('after %s' % date), 

370 instructions) 

371 return func(*args, **kwargs) 

372 

373 doc_controls.set_deprecated(new_func) 

374 new_func = tf_decorator.make_decorator( 

375 func, new_func, 'deprecated', 

376 _add_deprecated_function_notice_to_docstring(func.__doc__, date, 

377 instructions)) 

378 new_func.__signature__ = inspect.signature(func) 

379 

380 if cls is None: 

381 return new_func 

382 else: 

383 # Insert the wrapped function as the constructor 

384 setattr(cls, constructor_name, new_func) 

385 

386 # And update the docstring of the class. 

387 cls.__doc__ = _add_deprecated_function_notice_to_docstring( 

388 cls.__doc__, date, instructions) 

389 

390 return cls 

391 

392 return deprecated_wrapper 

393 

394 

395DeprecatedArgSpec = collections.namedtuple( 

396 'DeprecatedArgSpec', ['position', 'has_ok_value', 'ok_value']) 

397 

398 

399def deprecated_args(date, instructions, *deprecated_arg_names_or_tuples, 

400 **kwargs): 

401 """Decorator for marking specific function arguments as deprecated. 

402 

403 This decorator logs a deprecation warning whenever the decorated function is 

404 called with the deprecated argument. It has the following format: 

405 

406 Calling <function> (from <module>) with <arg> is deprecated and will be 

407 removed after <date>. Instructions for updating: 

408 <instructions> 

409 

410 If `date` is None, 'after <date>' is replaced with 'in a future version'. 

411 <function> includes the class name if it is a method. 

412 

413 It also edits the docstring of the function: ' (deprecated arguments)' is 

414 appended to the first line of the docstring and a deprecation notice is 

415 prepended to the rest of the docstring. 

416 

417 Args: 

418 date: String or None. The date the function is scheduled to be removed. Must 

419 be ISO 8601 (YYYY-MM-DD), or None. 

420 instructions: String. Instructions on how to update code using the 

421 deprecated function. 

422 *deprecated_arg_names_or_tuples: String or 2-Tuple (String, ok_val). The 

423 string is the deprecated argument name. Optionally, an ok-value may be 

424 provided. If the user provided argument equals this value, the warning is 

425 suppressed. 

426 **kwargs: If `warn_once=False` is passed, every call with a deprecated 

427 argument will log a warning. The default behavior is to only warn the 

428 first time the function is called with any given deprecated argument. All 

429 other kwargs raise `ValueError`. 

430 

431 Returns: 

432 Decorated function or method. 

433 

434 Raises: 

435 ValueError: If date is not None or in ISO 8601 format, instructions are 

436 empty, the deprecated arguments are not present in the function 

437 signature, the second element of a deprecated_tuple is not a 

438 list, or if a kwarg other than `warn_once` is passed. 

439 """ 

440 _validate_deprecation_args(date, instructions) 

441 if not deprecated_arg_names_or_tuples: 

442 raise ValueError('Specify which argument is deprecated.') 

443 if kwargs and list(kwargs.keys()) != ['warn_once']: 

444 kwargs.pop('warn_once', None) 

445 raise ValueError(f'Illegal argument passed to deprecated_args: {kwargs}') 

446 warn_once = kwargs.get('warn_once', True) 

447 

448 def _get_arg_names_to_ok_vals(): 

449 """Returns a dict mapping arg_name to DeprecatedArgSpec w/o position.""" 

450 d = {} 

451 for name_or_tuple in deprecated_arg_names_or_tuples: 

452 if isinstance(name_or_tuple, tuple): 

453 d[name_or_tuple[0]] = DeprecatedArgSpec(-1, True, name_or_tuple[1]) 

454 else: 

455 d[name_or_tuple] = DeprecatedArgSpec(-1, False, None) 

456 return d 

457 

458 def _get_deprecated_positional_arguments(names_to_ok_vals, arg_spec): 

459 """Builds a dictionary from deprecated arguments to their spec. 

460 

461 Returned dict is keyed by argument name. 

462 Each value is a DeprecatedArgSpec with the following fields: 

463 position: The zero-based argument position of the argument 

464 within the signature. None if the argument isn't found in 

465 the signature. 

466 ok_values: Values of this argument for which warning will be 

467 suppressed. 

468 

469 Args: 

470 names_to_ok_vals: dict from string arg_name to a list of values, possibly 

471 empty, which should not elicit a warning. 

472 arg_spec: Output from tf_inspect.getfullargspec on the called function. 

473 

474 Returns: 

475 Dictionary from arg_name to DeprecatedArgSpec. 

476 """ 

477 # Extract argument list 

478 arg_space = arg_spec.args + arg_spec.kwonlyargs 

479 arg_name_to_pos = {name: pos for pos, name in enumerate(arg_space)} 

480 deprecated_positional_args = {} 

481 for arg_name, spec in iter(names_to_ok_vals.items()): 

482 if arg_name in arg_name_to_pos: 

483 pos = arg_name_to_pos[arg_name] 

484 deprecated_positional_args[arg_name] = DeprecatedArgSpec( 

485 pos, spec.has_ok_value, spec.ok_value) 

486 return deprecated_positional_args 

487 

488 deprecated_arg_names = _get_arg_names_to_ok_vals() 

489 

490 def deprecated_wrapper(func): 

491 """Deprecation decorator.""" 

492 decorator_utils.validate_callable(func, 'deprecated_args') 

493 

494 arg_spec = tf_inspect.getfullargspec(func) 

495 deprecated_positions = _get_deprecated_positional_arguments( 

496 deprecated_arg_names, arg_spec) 

497 

498 is_varargs_deprecated = arg_spec.varargs in deprecated_arg_names 

499 is_kwargs_deprecated = arg_spec.varkw in deprecated_arg_names 

500 

501 if (len(deprecated_positions) + is_varargs_deprecated + is_kwargs_deprecated 

502 != len(deprecated_arg_names_or_tuples)): 

503 known_args = ( 

504 arg_spec.args + arg_spec.kwonlyargs + 

505 [arg_spec.varargs, arg_spec.varkw]) 

506 missing_args = [ 

507 arg_name for arg_name in deprecated_arg_names 

508 if arg_name not in known_args 

509 ] 

510 raise ValueError('The following deprecated arguments are not present ' 

511 f'in the function signature: {missing_args}. ' 

512 'Expected arguments from the following list: ' 

513 f'{known_args}.') 

514 

515 def _same_value(a, b): 

516 """A comparison operation that works for multiple object types. 

517 

518 Returns True for two empty lists, two numeric values with the 

519 same value, etc. 

520 

521 Returns False for (pd.DataFrame, None), and other pairs which 

522 should not be considered equivalent. 

523 

524 Args: 

525 a: value one of the comparison. 

526 b: value two of the comparison. 

527 

528 Returns: 

529 A boolean indicating whether the two inputs are the same value 

530 for the purposes of deprecation. 

531 """ 

532 if a is b: 

533 return True 

534 try: 

535 equality = a == b 

536 if isinstance(equality, bool): 

537 return equality 

538 except TypeError: 

539 return False 

540 return False 

541 

542 @functools.wraps(func) 

543 def new_func(*args, **kwargs): 

544 """Deprecation wrapper.""" 

545 # TODO(apassos) figure out a way to have reasonable performance with 

546 # deprecation warnings and eager mode. 

547 if is_in_graph_mode.IS_IN_GRAPH_MODE() and _PRINT_DEPRECATION_WARNINGS: 

548 invalid_args = [] 

549 named_args = tf_inspect.getcallargs(func, *args, **kwargs) 

550 for arg_name, spec in iter(deprecated_positions.items()): 

551 if (spec.position < len(args) and 

552 not (spec.has_ok_value and 

553 _same_value(named_args[arg_name], spec.ok_value))): 

554 invalid_args.append(arg_name) 

555 if is_varargs_deprecated and len(args) > len(arg_spec.args): 

556 invalid_args.append(arg_spec.varargs) 

557 if is_kwargs_deprecated and kwargs: 

558 invalid_args.append(arg_spec.varkw) 

559 for arg_name in deprecated_arg_names: 

560 if (arg_name in kwargs and 

561 not (deprecated_positions[arg_name].has_ok_value and 

562 _same_value(named_args[arg_name], 

563 deprecated_positions[arg_name].ok_value))): 

564 invalid_args.append(arg_name) 

565 for arg_name in invalid_args: 

566 if (func, arg_name) not in _PRINTED_WARNING: 

567 if warn_once: 

568 _PRINTED_WARNING[(func, arg_name)] = True 

569 logging.warning( 

570 'From %s: calling %s (from %s) with %s is deprecated and will ' 

571 'be removed %s.\nInstructions for updating:\n%s', 

572 _call_location(), decorator_utils.get_qualified_name(func), 

573 func.__module__, arg_name, 

574 'in a future version' if date is None else ('after %s' % date), 

575 instructions) 

576 return func(*args, **kwargs) 

577 

578 doc = _add_deprecated_arg_notice_to_docstring( 

579 func.__doc__, date, instructions, sorted(deprecated_arg_names.keys())) 

580 return tf_decorator.make_decorator(func, new_func, 'deprecated', doc) 

581 

582 return deprecated_wrapper 

583 

584 

585def deprecated_arg_values(date, 

586 instructions, 

587 warn_once=True, 

588 **deprecated_kwargs): 

589 """Decorator for marking specific function argument values as deprecated. 

590 

591 This decorator logs a deprecation warning whenever the decorated function is 

592 called with the deprecated argument values. It has the following format: 

593 

594 Calling <function> (from <module>) with <arg>=<value> is deprecated and 

595 will be removed after <date>. Instructions for updating: 

596 <instructions> 

597 

598 If `date` is None, 'after <date>' is replaced with 'in a future version'. 

599 <function> will include the class name if it is a method. 

600 

601 It also edits the docstring of the function: ' (deprecated arguments)' is 

602 appended to the first line of the docstring and a deprecation notice is 

603 prepended to the rest of the docstring. 

604 

605 Args: 

606 date: String or None. The date the function is scheduled to be removed. Must 

607 be ISO 8601 (YYYY-MM-DD), or None 

608 instructions: String. Instructions on how to update code using the 

609 deprecated function. 

610 warn_once: If `True`, warn only the first time this function is called with 

611 deprecated argument values. Otherwise, every call (with a deprecated 

612 argument value) will log a warning. 

613 **deprecated_kwargs: The deprecated argument values. 

614 

615 Returns: 

616 Decorated function or method. 

617 

618 Raises: 

619 ValueError: If date is not None or in ISO 8601 format, or instructions are 

620 empty. 

621 """ 

622 _validate_deprecation_args(date, instructions) 

623 if not deprecated_kwargs: 

624 raise ValueError('Specify which argument values are deprecated.') 

625 

626 def deprecated_wrapper(func): 

627 """Deprecation decorator.""" 

628 decorator_utils.validate_callable(func, 'deprecated_arg_values') 

629 

630 @functools.wraps(func) 

631 def new_func(*args, **kwargs): 

632 """Deprecation wrapper.""" 

633 if _PRINT_DEPRECATION_WARNINGS: 

634 named_args = tf_inspect.getcallargs(func, *args, **kwargs) 

635 for arg_name, arg_value in deprecated_kwargs.items(): 

636 if arg_name in named_args and _safe_eq(named_args[arg_name], 

637 arg_value): 

638 if (func, arg_name) not in _PRINTED_WARNING: 

639 if warn_once: 

640 _PRINTED_WARNING[(func, arg_name)] = True 

641 logging.warning( 

642 'From %s: calling %s (from %s) with %s=%s is deprecated and ' 

643 'will be removed %s.\nInstructions for updating:\n%s', 

644 _call_location(), decorator_utils.get_qualified_name(func), 

645 func.__module__, arg_name, arg_value, 

646 'in a future version' if date is None else 

647 ('after %s' % date), instructions) 

648 return func(*args, **kwargs) 

649 

650 doc = _add_deprecated_arg_value_notice_to_docstring(func.__doc__, date, 

651 instructions, 

652 deprecated_kwargs) 

653 return tf_decorator.make_decorator(func, new_func, 'deprecated', doc) 

654 

655 return deprecated_wrapper 

656 

657 

658def deprecated_argument_lookup(new_name, new_value, old_name, old_value): 

659 """Looks up deprecated argument name and ensures both are not used. 

660 

661 Args: 

662 new_name: new name of argument 

663 new_value: value of new argument (or None if not used) 

664 old_name: old name of argument 

665 old_value: value of old argument (or None if not used) 

666 

667 Returns: 

668 The effective argument that should be used. 

669 Raises: 

670 ValueError: if new_value and old_value are both non-null 

671 """ 

672 if old_value is not None: 

673 if new_value is not None: 

674 raise ValueError(f"Cannot specify both '{old_name}' and '{new_name}'.") 

675 return old_value 

676 return new_value 

677 

678 

679def rewrite_argument_docstring(old_doc, old_argument, new_argument): 

680 return old_doc.replace('`%s`' % old_argument, 

681 '`%s`' % new_argument).replace('%s:' % old_argument, 

682 '%s:' % new_argument) 

683 

684 

685@tf_contextlib.contextmanager 

686def silence(): 

687 """Temporarily silence deprecation warnings.""" 

688 global _PRINT_DEPRECATION_WARNINGS 

689 print_deprecation_warnings = _PRINT_DEPRECATION_WARNINGS 

690 _PRINT_DEPRECATION_WARNINGS = False 

691 yield 

692 _PRINT_DEPRECATION_WARNINGS = print_deprecation_warnings 

693 

694 

695def deprecate_moved_module(deprecated_name, new_module, deletion_version): 

696 """Logs a warning when a module that has been moved to a new location is used. 

697 

698 Copy the following code into the old module: 

699 

700 ``` 

701 import deprecation 

702 import new_module 

703 

704 __getattr__ = deprecation.deprecate_moved_module( 

705 __name__, new_module, "2.9") # adjust version number. 

706 ``` 

707 

708 Args: 

709 deprecated_name: Name of old module. 

710 new_module: Module to replace the old module. 

711 deletion_version: Version of TensorFlow in which the old module will be 

712 removed. 

713 

714 Returns: 

715 A function that logs a warning and returns the symbol from the new module. 

716 Set this function as the module's `__getattr__`. 

717 """ 

718 

719 def getter(name): 

720 if getter not in _PRINTED_WARNING and _PRINT_DEPRECATION_WARNINGS: 

721 _PRINTED_WARNING[getter] = True 

722 logging.warning( 

723 'Please fix your imports. Module %s has been moved to %s. The old ' 

724 'module will be deleted in version %s.', deprecated_name, 

725 new_module.__name__, deletion_version) 

726 return getattr(new_module, name) 

727 

728 return getter 

729 

730 

731class HiddenTfApiAttribute(property): 

732 """Hides a class attribute from the public API. 

733 

734 Attributes in public classes can be hidden from the API by having an '_' in 

735 front of the name (e.g. ClassName._variables). This doesn't work when 

736 attributes or methods are inherited from a parent class. To hide inherited 

737 attributes, set their values to be `deprecation.hide_attribute_from_api`. 

738 For example, this is used in V2 Estimator to hide the deprecated 

739 export_savedmodel method: 

740 class EstimatorV2(Estimator): 

741 export_savedmodel = deprecation.hide_attribute_from_api('...') 

742 """ 

743 

744 def __init__(self, deprecation_message): 

745 

746 def raise_error(unused_self): 

747 raise AttributeError(deprecation_message) 

748 

749 super(HiddenTfApiAttribute, self).__init__(raise_error) 

750 

751 

752hide_attribute_from_api = HiddenTfApiAttribute # pylint: disable=invalid-name 

753 

754# TODO(kathywu): Remove once cl/246395236 is submitted. 

755HIDDEN_ATTRIBUTE = HiddenTfApiAttribute('This attribute has been deprecated.')