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

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 

21 

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 

26 

27 

28class _TFShouldUseHelper(object): 

29 """Object stored in TFShouldUse-wrapped objects. 

30 

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 """ 

36 

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 

56 

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 

63 

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)) 

87 

88 def __del__(self): 

89 self._check_sated(raise_error=False) 

90 

91 

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 

96 

97 

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) 

104 

105 

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) 

118 

119 

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 

129 

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} 

166 

167 

168_WRAPPERS = {} 

169 

170 

171class ShouldUseWrapper(object): 

172 pass 

173 

174 

175def _get_wrapper(x, tf_should_use_helper): 

176 """Create a wrapper for object x, whose class subclasses type(x). 

177 

178 The wrapper will emit a warning if it is deleted without any of its 

179 properties being accessed or methods being called. 

180 

181 Args: 

182 x: The instance to wrap. 

183 tf_should_use_helper: The object that tracks usage. 

184 

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) 

192 

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__) 

197 

198 def set_body(ns): 

199 ns.update(tx.__dict__) 

200 return ns 

201 

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)) 

208 

209 copy_tx.mark_used = _new_mark_used 

210 copy_tx.__setattr__ = _new__setattr__ 

211 _WRAPPERS[type_x] = copy_tx 

212 

213 return copy_tx(x, tf_should_use_helper) 

214 

215 

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. 

218 

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. 

226 

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 

233 

234 if context.executing_eagerly() and not warn_in_eager: 

235 return x 

236 

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 

240 

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 

246 

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) 

253 

254 return _get_wrapper(x, tf_should_use_helper) 

255 

256 

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. 

259 

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. 

263 

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: 

266 

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()`. 

270 

271 Note, certain behaviors cannot be tracked - for these the object may not 

272 be marked as used. Examples include: 

273 

274 - `t != 0`. In this case, comparison is done on types / ids. 

275 - `isinstance(t, tf.Tensor)`. Similar to above. 

276 

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. 

281 

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)]) 

298 

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) 

307 

308 if fn is not None: 

309 return decorated(fn) 

310 else: 

311 return decorated