Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/util/tf_should_use.py: 35%
113 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1# Copyright 2017 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"""Decorator that provides a warning if the wrapped object is never used."""
16import copy
17import sys
18import textwrap
19import traceback
20import types
22from tensorflow.python.eager import context
23from tensorflow.python.framework import ops
24from tensorflow.python.platform import tf_logging
25from tensorflow.python.util import tf_decorator
28class _TFShouldUseHelper(object):
29 """Object stored in TFShouldUse-wrapped objects.
31 When it is deleted it will emit a warning or error if its `sate` method
32 has not been called by time of deletion, and Tensorflow is not executing
33 eagerly or inside a tf.function (which use autodeps and resolve the
34 main issues this wrapper warns about).
35 """
37 def __init__(self, type_, repr_, stack_frame, error_in_function,
38 warn_in_eager):
39 self._type = type_
40 self._repr = repr_
41 self._stack_frame = stack_frame
42 self._error_in_function = error_in_function
43 if context.executing_eagerly():
44 # If warn_in_eager, sated == False. Otherwise true.
45 self._sated = not warn_in_eager
46 elif ops.inside_function():
47 if error_in_function:
48 self._sated = False
49 ops.add_exit_callback_to_default_func_graph(
50 lambda: self._check_sated(raise_error=True))
51 else:
52 self._sated = True
53 else:
54 # TF1 graph building mode
55 self._sated = False
57 def sate(self):
58 self._sated = True
59 self._type = None
60 self._repr = None
61 self._stack_frame = None
62 self._logging_module = None
64 def _check_sated(self, raise_error):
65 """Check if the object has been sated."""
66 if self._sated:
67 return
68 creation_stack = ''.join(
69 [line.rstrip()
70 for line in traceback.format_stack(self._stack_frame, limit=5)])
71 if raise_error:
72 try:
73 raise RuntimeError(
74 'Object was never used (type {}): {}. If you want to mark it as '
75 'used call its "mark_used()" method. It was originally created '
76 'here:\n{}'.format(self._type, self._repr, creation_stack))
77 finally:
78 self.sate()
79 else:
80 tf_logging.error(
81 '==================================\n'
82 'Object was never used (type {}):\n{}\nIf you want to mark it as '
83 'used call its "mark_used()" method.\nIt was originally created '
84 'here:\n{}\n'
85 '=================================='
86 .format(self._type, self._repr, creation_stack))
88 def __del__(self):
89 self._check_sated(raise_error=False)
92def _new__init__(self, wrapped_value, tf_should_use_helper):
93 # pylint: disable=protected-access
94 self._tf_should_use_helper = tf_should_use_helper
95 self._tf_should_use_wrapped_value = wrapped_value
98def _new__setattr__(self, key, value):
99 if key in ('_tf_should_use_helper', '_tf_should_use_wrapped_value'):
100 return object.__setattr__(self, key, value)
101 return setattr(
102 object.__getattribute__(self, '_tf_should_use_wrapped_value'),
103 key, value)
106def _new__getattribute__(self, key):
107 if key not in ('_tf_should_use_helper', '_tf_should_use_wrapped_value'):
108 object.__getattribute__(self, '_tf_should_use_helper').sate()
109 if key in (
110 '_tf_should_use_wrapped_value',
111 '_tf_should_use_helper',
112 'mark_used',
113 '__setattr__',
114 ):
115 return object.__getattribute__(self, key)
116 return getattr(
117 object.__getattribute__(self, '_tf_should_use_wrapped_value'), key)
120def _new_mark_used(self, *args, **kwargs):
121 object.__getattribute__(self, '_tf_should_use_helper').sate()
122 try:
123 mu = object.__getattribute__(
124 object.__getattribute__(self, '_tf_should_use_wrapped_value'),
125 'mark_used')
126 return mu(*args, **kwargs)
127 except AttributeError:
128 pass
130OVERLOADABLE_OPERATORS = {
131 '__add__',
132 '__radd__',
133 '__sub__',
134 '__rsub__',
135 '__mul__',
136 '__rmul__',
137 '__div__',
138 '__rdiv__',
139 '__truediv__',
140 '__rtruediv__',
141 '__floordiv__',
142 '__rfloordiv__',
143 '__mod__',
144 '__rmod__',
145 '__lt__',
146 '__le__',
147 '__gt__',
148 '__ge__',
149 '__ne__',
150 '__eq__',
151 '__and__',
152 '__rand__',
153 '__or__',
154 '__ror__',
155 '__xor__',
156 '__rxor__',
157 '__getitem__',
158 '__pow__',
159 '__rpow__',
160 '__invert__',
161 '__neg__',
162 '__abs__',
163 '__matmul__',
164 '__rmatmul__',
165}
168_WRAPPERS = {}
171class ShouldUseWrapper(object):
172 pass
175def _get_wrapper(x, tf_should_use_helper):
176 """Create a wrapper for object x, whose class subclasses type(x).
178 The wrapper will emit a warning if it is deleted without any of its
179 properties being accessed or methods being called.
181 Args:
182 x: The instance to wrap.
183 tf_should_use_helper: The object that tracks usage.
185 Returns:
186 An object wrapping `x`, of type `type(x)`.
187 """
188 type_x = type(x)
189 memoized = _WRAPPERS.get(type_x, None)
190 if memoized:
191 return memoized(x, tf_should_use_helper)
193 # Make a copy of `object`
194 tx = copy.deepcopy(ShouldUseWrapper)
195 # Prefer using __orig_bases__, which preserve generic type arguments.
196 bases = getattr(tx, '__orig_bases__', tx.__bases__)
198 def set_body(ns):
199 ns.update(tx.__dict__)
200 return ns
202 copy_tx = types.new_class(tx.__name__, bases, exec_body=set_body)
203 copy_tx.__init__ = _new__init__
204 copy_tx.__getattribute__ = _new__getattribute__
205 for op in OVERLOADABLE_OPERATORS:
206 if hasattr(type_x, op):
207 setattr(copy_tx, op, getattr(type_x, op))
209 copy_tx.mark_used = _new_mark_used
210 copy_tx.__setattr__ = _new__setattr__
211 _WRAPPERS[type_x] = copy_tx
213 return copy_tx(x, tf_should_use_helper)
216def _add_should_use_warning(x, error_in_function=False, warn_in_eager=False):
217 """Wraps object x so that if it is never used, a warning is logged.
219 Args:
220 x: Python object.
221 error_in_function: Python bool. If `True`, a `RuntimeError` is raised
222 if the returned value is never used when created during `tf.function`
223 tracing.
224 warn_in_eager: Python bool. If `True` raise warning if in Eager mode as well
225 as graph mode.
227 Returns:
228 An instance of `TFShouldUseWarningWrapper` which subclasses `type(x)`
229 and is a very shallow wrapper for `x` which logs access into `x`.
230 """
231 if x is None or (isinstance(x, list) and not x):
232 return x
234 if context.executing_eagerly() and not warn_in_eager:
235 return x
237 if ops.inside_function() and not error_in_function:
238 # We don't currently log warnings in tf.function calls, so just skip it.
239 return x
241 # Extract the current frame for later use by traceback printing.
242 try:
243 raise ValueError()
244 except ValueError:
245 stack_frame = sys.exc_info()[2].tb_frame.f_back
247 tf_should_use_helper = _TFShouldUseHelper(
248 type_=type(x),
249 repr_=repr(x),
250 stack_frame=stack_frame,
251 error_in_function=error_in_function,
252 warn_in_eager=warn_in_eager)
254 return _get_wrapper(x, tf_should_use_helper)
257def should_use_result(fn=None, warn_in_eager=False, error_in_function=False):
258 """Function wrapper that ensures the function's output is used.
260 If the output is not used, a `logging.error` is logged. If
261 `error_in_function` is set, then a `RuntimeError` will be raised at the
262 end of function tracing if the output is not used by that point.
264 An output is marked as used if any of its attributes are read, modified, or
265 updated. Examples when the output is a `Tensor` include:
267 - Using it in any capacity (e.g. `y = t + 0`, `sess.run(t)`)
268 - Accessing a property (e.g. getting `t.name` or `t.op`).
269 - Calling `t.mark_used()`.
271 Note, certain behaviors cannot be tracked - for these the object may not
272 be marked as used. Examples include:
274 - `t != 0`. In this case, comparison is done on types / ids.
275 - `isinstance(t, tf.Tensor)`. Similar to above.
277 Args:
278 fn: The function to wrap.
279 warn_in_eager: Whether to create warnings in Eager as well.
280 error_in_function: Whether to raise an error when creating a tf.function.
282 Returns:
283 The wrapped function.
284 """
285 def decorated(fn):
286 """Decorates the input function."""
287 def wrapped(*args, **kwargs):
288 return _add_should_use_warning(fn(*args, **kwargs),
289 warn_in_eager=warn_in_eager,
290 error_in_function=error_in_function)
291 fn_doc = fn.__doc__ or ''
292 split_doc = fn_doc.split('\n', 1)
293 if len(split_doc) == 1:
294 updated_doc = fn_doc
295 else:
296 brief, rest = split_doc
297 updated_doc = '\n'.join([brief, textwrap.dedent(rest)])
299 note = ('\n\nNote: The output of this function should be used. If it is '
300 'not, a warning will be logged or an error may be raised. '
301 'To mark the output as used, call its .mark_used() method.')
302 return tf_decorator.make_decorator(
303 target=fn,
304 decorator_func=wrapped,
305 decorator_name='should_use_result',
306 decorator_doc=updated_doc + note)
308 if fn is not None:
309 return decorated(fn)
310 else:
311 return decorated