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

154 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-05 06:32 +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"""TFDecorator-aware replacements for the inspect module.""" 

16import collections 

17import functools 

18import inspect as _inspect 

19 

20import six 

21 

22from tensorflow.python.util import tf_decorator 

23 

24 

25# inspect.signature() is preferred over inspect.getfullargspec() in PY3. 

26# Note that while it can handle TFDecorators, it will ignore a TFDecorator's 

27# provided ArgSpec/FullArgSpec and instead return the signature of the 

28# inner-most function. 

29def signature(obj, *, follow_wrapped=True): 

30 """TFDecorator-aware replacement for inspect.signature.""" 

31 return _inspect.signature( 

32 tf_decorator.unwrap(obj)[1], follow_wrapped=follow_wrapped) 

33 

34 

35Parameter = _inspect.Parameter 

36Signature = _inspect.Signature 

37 

38if hasattr(_inspect, 'ArgSpec'): 

39 ArgSpec = _inspect.ArgSpec 

40else: 

41 ArgSpec = collections.namedtuple( 

42 'ArgSpec', 

43 [ 

44 'args', 

45 'varargs', 

46 'keywords', 

47 'defaults', 

48 ], 

49 ) 

50 

51 

52if hasattr(_inspect, 'FullArgSpec'): 

53 FullArgSpec = _inspect.FullArgSpec # pylint: disable=invalid-name 

54else: 

55 FullArgSpec = collections.namedtuple('FullArgSpec', [ 

56 'args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', 'kwonlydefaults', 

57 'annotations' 

58 ]) 

59 

60 

61def _convert_maybe_argspec_to_fullargspec(argspec): 

62 if isinstance(argspec, FullArgSpec): 

63 return argspec 

64 return FullArgSpec( 

65 args=argspec.args, 

66 varargs=argspec.varargs, 

67 varkw=argspec.keywords, 

68 defaults=argspec.defaults, 

69 kwonlyargs=[], 

70 kwonlydefaults=None, 

71 annotations={}) 

72 

73if hasattr(_inspect, 'getfullargspec'): 

74 _getfullargspec = _inspect.getfullargspec # pylint: disable=invalid-name 

75 

76 def _getargspec(target): 

77 """A python3 version of getargspec. 

78 

79 Calls `getfullargspec` and assigns args, varargs, 

80 varkw, and defaults to a python 2/3 compatible `ArgSpec`. 

81 

82 The parameter name 'varkw' is changed to 'keywords' to fit the 

83 `ArgSpec` struct. 

84 

85 Args: 

86 target: the target object to inspect. 

87 

88 Returns: 

89 An ArgSpec with args, varargs, keywords, and defaults parameters 

90 from FullArgSpec. 

91 """ 

92 fullargspecs = getfullargspec(target) 

93 

94 defaults = fullargspecs.defaults or () 

95 if fullargspecs.kwonlydefaults: 

96 defaults += tuple(fullargspecs.kwonlydefaults.values()) 

97 

98 if not defaults: 

99 defaults = None 

100 

101 argspecs = ArgSpec( 

102 args=fullargspecs.args + fullargspecs.kwonlyargs, 

103 varargs=fullargspecs.varargs, 

104 keywords=fullargspecs.varkw, 

105 defaults=defaults, 

106 ) 

107 return argspecs 

108else: 

109 _getargspec = _inspect.getargspec 

110 

111 def _getfullargspec(target): 

112 """A python2 version of getfullargspec. 

113 

114 Args: 

115 target: the target object to inspect. 

116 

117 Returns: 

118 A FullArgSpec with empty kwonlyargs, kwonlydefaults and annotations. 

119 """ 

120 return _convert_maybe_argspec_to_fullargspec(getargspec(target)) 

121 

122 

123def currentframe(): 

124 """TFDecorator-aware replacement for inspect.currentframe.""" 

125 return _inspect.stack()[1][0] 

126 

127 

128def getargspec(obj): 

129 """TFDecorator-aware replacement for `inspect.getargspec`. 

130 

131 Note: `getfullargspec` is recommended as the python 2/3 compatible 

132 replacement for this function. 

133 

134 Args: 

135 obj: A function, partial function, or callable object, possibly decorated. 

136 

137 Returns: 

138 The `ArgSpec` that describes the signature of the outermost decorator that 

139 changes the callable's signature, or the `ArgSpec` that describes 

140 the object if not decorated. 

141 

142 Raises: 

143 ValueError: When callable's signature can not be expressed with 

144 ArgSpec. 

145 TypeError: For objects of unsupported types. 

146 """ 

147 if isinstance(obj, functools.partial): 

148 return _get_argspec_for_partial(obj) 

149 

150 decorators, target = tf_decorator.unwrap(obj) 

151 

152 spec = next((d.decorator_argspec 

153 for d in decorators 

154 if d.decorator_argspec is not None), None) 

155 if spec: 

156 return spec 

157 

158 try: 

159 # Python3 will handle most callables here (not partial). 

160 return _getargspec(target) 

161 except TypeError: 

162 pass 

163 

164 if isinstance(target, type): 

165 try: 

166 return _getargspec(target.__init__) 

167 except TypeError: 

168 pass 

169 

170 try: 

171 return _getargspec(target.__new__) 

172 except TypeError: 

173 pass 

174 

175 # The `type(target)` ensures that if a class is received we don't return 

176 # the signature of its __call__ method. 

177 return _getargspec(type(target).__call__) 

178 

179 

180def _get_argspec_for_partial(obj): 

181 """Implements `getargspec` for `functools.partial` objects. 

182 

183 Args: 

184 obj: The `functools.partial` object 

185 Returns: 

186 An `inspect.ArgSpec` 

187 Raises: 

188 ValueError: When callable's signature can not be expressed with 

189 ArgSpec. 

190 """ 

191 # When callable is a functools.partial object, we construct its ArgSpec with 

192 # following strategy: 

193 # - If callable partial contains default value for positional arguments (ie. 

194 # object.args), then final ArgSpec doesn't contain those positional arguments. 

195 # - If callable partial contains default value for keyword arguments (ie. 

196 # object.keywords), then we merge them with wrapped target. Default values 

197 # from callable partial takes precedence over those from wrapped target. 

198 # 

199 # However, there is a case where it is impossible to construct a valid 

200 # ArgSpec. Python requires arguments that have no default values must be 

201 # defined before those with default values. ArgSpec structure is only valid 

202 # when this presumption holds true because default values are expressed as a 

203 # tuple of values without keywords and they are always assumed to belong to 

204 # last K arguments where K is number of default values present. 

205 # 

206 # Since functools.partial can give default value to any argument, this 

207 # presumption may no longer hold in some cases. For example: 

208 # 

209 # def func(m, n): 

210 # return 2 * m + n 

211 # partialed = functools.partial(func, m=1) 

212 # 

213 # This example will result in m having a default value but n doesn't. This is 

214 # usually not allowed in Python and can not be expressed in ArgSpec correctly. 

215 # 

216 # Thus, we must detect cases like this by finding first argument with default 

217 # value and ensures all following arguments also have default values. When 

218 # this is not true, a ValueError is raised. 

219 

220 n_prune_args = len(obj.args) 

221 partial_keywords = obj.keywords or {} 

222 

223 args, varargs, keywords, defaults = getargspec(obj.func) 

224 

225 # Pruning first n_prune_args arguments. 

226 args = args[n_prune_args:] 

227 

228 # Partial function may give default value to any argument, therefore length 

229 # of default value list must be len(args) to allow each argument to 

230 # potentially be given a default value. 

231 no_default = object() 

232 all_defaults = [no_default] * len(args) 

233 

234 if defaults: 

235 all_defaults[-len(defaults):] = defaults 

236 

237 # Fill in default values provided by partial function in all_defaults. 

238 for kw, default in six.iteritems(partial_keywords): 

239 if kw in args: 

240 idx = args.index(kw) 

241 all_defaults[idx] = default 

242 elif not keywords: 

243 raise ValueError(f'{obj} does not have a **kwargs parameter, but ' 

244 f'contains an unknown partial keyword {kw}.') 

245 

246 # Find first argument with default value set. 

247 first_default = next( 

248 (idx for idx, x in enumerate(all_defaults) if x is not no_default), None) 

249 

250 # If no default values are found, return ArgSpec with defaults=None. 

251 if first_default is None: 

252 return ArgSpec(args, varargs, keywords, None) 

253 

254 # Checks if all arguments have default value set after first one. 

255 invalid_default_values = [ 

256 args[i] for i, j in enumerate(all_defaults) 

257 if j is no_default and i > first_default 

258 ] 

259 

260 if invalid_default_values: 

261 raise ValueError(f'{obj} has some keyword-only arguments, which are not' 

262 f' supported: {invalid_default_values}.') 

263 

264 return ArgSpec(args, varargs, keywords, tuple(all_defaults[first_default:])) 

265 

266 

267def getfullargspec(obj): 

268 """TFDecorator-aware replacement for `inspect.getfullargspec`. 

269 

270 This wrapper emulates `inspect.getfullargspec` in[^)]* Python2. 

271 

272 Args: 

273 obj: A callable, possibly decorated. 

274 

275 Returns: 

276 The `FullArgSpec` that describes the signature of 

277 the outermost decorator that changes the callable's signature. If the 

278 callable is not decorated, `inspect.getfullargspec()` will be called 

279 directly on the callable. 

280 """ 

281 decorators, target = tf_decorator.unwrap(obj) 

282 

283 for d in decorators: 

284 if d.decorator_argspec is not None: 

285 return _convert_maybe_argspec_to_fullargspec(d.decorator_argspec) 

286 return _getfullargspec(target) 

287 

288 

289def getcallargs(*func_and_positional, **named): 

290 """TFDecorator-aware replacement for inspect.getcallargs. 

291 

292 Args: 

293 *func_and_positional: A callable, possibly decorated, followed by any 

294 positional arguments that would be passed to `func`. 

295 **named: The named argument dictionary that would be passed to `func`. 

296 

297 Returns: 

298 A dictionary mapping `func`'s named arguments to the values they would 

299 receive if `func(*positional, **named)` were called. 

300 

301 `getcallargs` will use the argspec from the outermost decorator that provides 

302 it. If no attached decorators modify argspec, the final unwrapped target's 

303 argspec will be used. 

304 """ 

305 func = func_and_positional[0] 

306 positional = func_and_positional[1:] 

307 argspec = getfullargspec(func) 

308 call_args = named.copy() 

309 this = getattr(func, 'im_self', None) or getattr(func, '__self__', None) 

310 if ismethod(func) and this: 

311 positional = (this,) + positional 

312 remaining_positionals = [arg for arg in argspec.args if arg not in call_args] 

313 call_args.update(dict(zip(remaining_positionals, positional))) 

314 default_count = 0 if not argspec.defaults else len(argspec.defaults) 

315 if default_count: 

316 for arg, value in zip(argspec.args[-default_count:], argspec.defaults): 

317 if arg not in call_args: 

318 call_args[arg] = value 

319 if argspec.kwonlydefaults is not None: 

320 for k, v in argspec.kwonlydefaults.items(): 

321 if k not in call_args: 

322 call_args[k] = v 

323 return call_args 

324 

325 

326def getframeinfo(*args, **kwargs): 

327 return _inspect.getframeinfo(*args, **kwargs) 

328 

329 

330def getdoc(object): # pylint: disable=redefined-builtin 

331 """TFDecorator-aware replacement for inspect.getdoc. 

332 

333 Args: 

334 object: An object, possibly decorated. 

335 

336 Returns: 

337 The docstring associated with the object. 

338 

339 The outermost-decorated object is intended to have the most complete 

340 documentation, so the decorated parameter is not unwrapped. 

341 """ 

342 return _inspect.getdoc(object) 

343 

344 

345def getfile(object): # pylint: disable=redefined-builtin 

346 """TFDecorator-aware replacement for inspect.getfile.""" 

347 unwrapped_object = tf_decorator.unwrap(object)[1] 

348 

349 # Work around for the case when object is a stack frame 

350 # and only .pyc files are used. In this case, getfile 

351 # might return incorrect path. So, we get the path from f_globals 

352 # instead. 

353 if (hasattr(unwrapped_object, 'f_globals') and 

354 '__file__' in unwrapped_object.f_globals): 

355 return unwrapped_object.f_globals['__file__'] 

356 return _inspect.getfile(unwrapped_object) 

357 

358 

359def getmembers(object, predicate=None): # pylint: disable=redefined-builtin 

360 """TFDecorator-aware replacement for inspect.getmembers.""" 

361 return _inspect.getmembers(object, predicate) 

362 

363 

364def getmodule(object): # pylint: disable=redefined-builtin 

365 """TFDecorator-aware replacement for inspect.getmodule.""" 

366 return _inspect.getmodule(object) 

367 

368 

369def getmro(cls): 

370 """TFDecorator-aware replacement for inspect.getmro.""" 

371 return _inspect.getmro(cls) 

372 

373 

374def getsource(object): # pylint: disable=redefined-builtin 

375 """TFDecorator-aware replacement for inspect.getsource.""" 

376 return _inspect.getsource(tf_decorator.unwrap(object)[1]) 

377 

378 

379def getsourcefile(object): # pylint: disable=redefined-builtin 

380 """TFDecorator-aware replacement for inspect.getsourcefile.""" 

381 return _inspect.getsourcefile(tf_decorator.unwrap(object)[1]) 

382 

383 

384def getsourcelines(object): # pylint: disable=redefined-builtin 

385 """TFDecorator-aware replacement for inspect.getsourcelines.""" 

386 return _inspect.getsourcelines(tf_decorator.unwrap(object)[1]) 

387 

388 

389def isbuiltin(object): # pylint: disable=redefined-builtin 

390 """TFDecorator-aware replacement for inspect.isbuiltin.""" 

391 return _inspect.isbuiltin(tf_decorator.unwrap(object)[1]) 

392 

393 

394def isclass(object): # pylint: disable=redefined-builtin 

395 """TFDecorator-aware replacement for inspect.isclass.""" 

396 return _inspect.isclass(tf_decorator.unwrap(object)[1]) 

397 

398 

399def isfunction(object): # pylint: disable=redefined-builtin 

400 """TFDecorator-aware replacement for inspect.isfunction.""" 

401 return _inspect.isfunction(tf_decorator.unwrap(object)[1]) 

402 

403 

404def isframe(object): # pylint: disable=redefined-builtin 

405 """TFDecorator-aware replacement for inspect.ismodule.""" 

406 return _inspect.isframe(tf_decorator.unwrap(object)[1]) 

407 

408 

409def isgenerator(object): # pylint: disable=redefined-builtin 

410 """TFDecorator-aware replacement for inspect.isgenerator.""" 

411 return _inspect.isgenerator(tf_decorator.unwrap(object)[1]) 

412 

413 

414def isgeneratorfunction(object): # pylint: disable=redefined-builtin 

415 """TFDecorator-aware replacement for inspect.isgeneratorfunction.""" 

416 return _inspect.isgeneratorfunction(tf_decorator.unwrap(object)[1]) 

417 

418 

419def ismethod(object): # pylint: disable=redefined-builtin 

420 """TFDecorator-aware replacement for inspect.ismethod.""" 

421 return _inspect.ismethod(tf_decorator.unwrap(object)[1]) 

422 

423 

424def isanytargetmethod(object): # pylint: disable=redefined-builtin 

425 # pylint: disable=g-doc-args,g-doc-return-or-yield 

426 """Checks if `object` or a TF Decorator wrapped target contains self or cls. 

427 

428 This function could be used along with `tf_inspect.getfullargspec` to 

429 determine if the first argument of `object` argspec is self or cls. If the 

430 first argument is self or cls, it needs to be excluded from argspec when we 

431 compare the argspec to the input arguments and, if provided, the tf.function 

432 input_signature. 

433 

434 Like `tf_inspect.getfullargspec` and python `inspect.getfullargspec`, it 

435 does not unwrap python decorators. 

436 

437 Args: 

438 obj: An method, function, or functool.partial, possibly decorated by 

439 TFDecorator. 

440 

441 Returns: 

442 A bool indicates if `object` or any target along the chain of TF decorators 

443 is a method. 

444 """ 

445 decorators, target = tf_decorator.unwrap(object) 

446 for decorator in decorators: 

447 if _inspect.ismethod(decorator.decorated_target): 

448 return True 

449 

450 # TODO(b/194845243): Implement the long term solution with inspect.signature. 

451 # A functools.partial object is not a function or method. But if the wrapped 

452 # func is a method, the argspec will contain self/cls. 

453 while isinstance(target, functools.partial): 

454 target = target.func 

455 

456 # `target` is a method or an instance with __call__ 

457 return callable(target) and not _inspect.isfunction(target) 

458 

459 

460def ismodule(object): # pylint: disable=redefined-builtin 

461 """TFDecorator-aware replacement for inspect.ismodule.""" 

462 return _inspect.ismodule(tf_decorator.unwrap(object)[1]) 

463 

464 

465def isroutine(object): # pylint: disable=redefined-builtin 

466 """TFDecorator-aware replacement for inspect.isroutine.""" 

467 return _inspect.isroutine(tf_decorator.unwrap(object)[1]) 

468 

469 

470def stack(context=1): 

471 """TFDecorator-aware replacement for inspect.stack.""" 

472 return _inspect.stack(context)[1:]