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.""" 16from __future__ import absolute_import 17from __future__ import division 18from __future__ import print_function 19 20from collections import namedtuple 21import functools 22import inspect as _inspect 23 24import six 25 26from tensorflow.python.util import tf_decorator 27 28ArgSpec = _inspect.ArgSpec 29 30 31if hasattr(_inspect, 'FullArgSpec'): 32 FullArgSpec = _inspect.FullArgSpec # pylint: disable=invalid-name 33else: 34 FullArgSpec = namedtuple('FullArgSpec', [ 35 'args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', 'kwonlydefaults', 36 'annotations' 37 ]) 38 39 40def _convert_maybe_argspec_to_fullargspec(argspec): 41 if isinstance(argspec, FullArgSpec): 42 return argspec 43 return FullArgSpec( 44 args=argspec.args, 45 varargs=argspec.varargs, 46 varkw=argspec.keywords, 47 defaults=argspec.defaults, 48 kwonlyargs=[], 49 kwonlydefaults=None, 50 annotations={}) 51 52if hasattr(_inspect, 'getfullargspec'): 53 _getfullargspec = _inspect.getfullargspec # pylint: disable=invalid-name 54 55 def _getargspec(target): 56 """A python3 version of getargspec. 57 58 Calls `getfullargspec` and assigns args, varargs, 59 varkw, and defaults to a python 2/3 compatible `ArgSpec`. 60 61 The parameter name 'varkw' is changed to 'keywords' to fit the 62 `ArgSpec` struct. 63 64 Args: 65 target: the target object to inspect. 66 67 Returns: 68 An ArgSpec with args, varargs, keywords, and defaults parameters 69 from FullArgSpec. 70 """ 71 fullargspecs = getfullargspec(target) 72 argspecs = ArgSpec( 73 args=fullargspecs.args, 74 varargs=fullargspecs.varargs, 75 keywords=fullargspecs.varkw, 76 defaults=fullargspecs.defaults) 77 return argspecs 78else: 79 _getargspec = _inspect.getargspec 80 81 def _getfullargspec(target): 82 """A python2 version of getfullargspec. 83 84 Args: 85 target: the target object to inspect. 86 87 Returns: 88 A FullArgSpec with empty kwonlyargs, kwonlydefaults and annotations. 89 """ 90 return _convert_maybe_argspec_to_fullargspec(getargspec(target)) 91 92 93def currentframe(): 94 """TFDecorator-aware replacement for inspect.currentframe.""" 95 return _inspect.stack()[1][0] 96 97 98def getargspec(obj): 99 """TFDecorator-aware replacement for `inspect.getargspec`. 100 101 Note: `getfullargspec` is recommended as the python 2/3 compatible 102 replacement for this function. 103 104 Args: 105 obj: A function, partial function, or callable object, possibly decorated. 106 107 Returns: 108 The `ArgSpec` that describes the signature of the outermost decorator that 109 changes the callable's signature, or the `ArgSpec` that describes 110 the object if not decorated. 111 112 Raises: 113 ValueError: When callable's signature can not be expressed with 114 ArgSpec. 115 TypeError: For objects of unsupported types. 116 """ 117 if isinstance(obj, functools.partial): 118 return _get_argspec_for_partial(obj) 119 120 decorators, target = tf_decorator.unwrap(obj) 121 122 spec = next((d.decorator_argspec 123 for d in decorators 124 if d.decorator_argspec is not None), None) 125 if spec: 126 return spec 127 128 try: 129 # Python3 will handle most callables here (not partial). 130 return _getargspec(target) 131 except TypeError: 132 pass 133 134 if isinstance(target, type): 135 try: 136 return _getargspec(target.__init__) 137 except TypeError: 138 pass 139 140 try: 141 return _getargspec(target.__new__) 142 except TypeError: 143 pass 144 145 # The `type(target)` ensures that if a class is received we don't return 146 # the signature of it's __call__ method. 147 return _getargspec(type(target).__call__) 148 149 150def _get_argspec_for_partial(obj): 151 """Implements `getargspec` for `functools.partial` objects. 152 153 Args: 154 obj: The `functools.partial` obeject 155 Returns: 156 An `inspect.ArgSpec` 157 Raises: 158 ValueError: When callable's signature can not be expressed with 159 ArgSpec. 160 """ 161 # When callable is a functools.partial object, we construct its ArgSpec with 162 # following strategy: 163 # - If callable partial contains default value for positional arguments (ie. 164 # object.args), then final ArgSpec doesn't contain those positional arguments. 165 # - If callable partial contains default value for keyword arguments (ie. 166 # object.keywords), then we merge them with wrapped target. Default values 167 # from callable partial takes precedence over those from wrapped target. 168 # 169 # However, there is a case where it is impossible to construct a valid 170 # ArgSpec. Python requires arguments that have no default values must be 171 # defined before those with default values. ArgSpec structure is only valid 172 # when this presumption holds true because default values are expressed as a 173 # tuple of values without keywords and they are always assumed to belong to 174 # last K arguments where K is number of default values present. 175 # 176 # Since functools.partial can give default value to any argument, this 177 # presumption may no longer hold in some cases. For example: 178 # 179 # def func(m, n): 180 # return 2 * m + n 181 # partialed = functools.partial(func, m=1) 182 # 183 # This example will result in m having a default value but n doesn't. This is 184 # usually not allowed in Python and can not be expressed in ArgSpec correctly. 185 # 186 # Thus, we must detect cases like this by finding first argument with default 187 # value and ensures all following arguments also have default values. When 188 # this is not true, a ValueError is raised. 189 190 n_prune_args = len(obj.args) 191 partial_keywords = obj.keywords or {} 192 193 args, varargs, keywords, defaults = getargspec(obj.func) 194 195 # Pruning first n_prune_args arguments. 196 args = args[n_prune_args:] 197 198 # Partial function may give default value to any argument, therefore length 199 # of default value list must be len(args) to allow each argument to 200 # potentially be given a default value. 201 no_default = object() 202 all_defaults = [no_default] * len(args) 203 204 if defaults: 205 all_defaults[-len(defaults):] = defaults 206 207 # Fill in default values provided by partial function in all_defaults. 208 for kw, default in six.iteritems(partial_keywords): 209 idx = args.index(kw) 210 all_defaults[idx] = default 211 212 # Find first argument with default value set. 213 first_default = next( 214 (idx for idx, x in enumerate(all_defaults) if x is not no_default), None) 215 216 # If no default values are found, return ArgSpec with defaults=None. 217 if first_default is None: 218 return ArgSpec(args, varargs, keywords, None) 219 220 # Checks if all arguments have default value set after first one. 221 invalid_default_values = [ 222 args[i] for i, j in enumerate(all_defaults) 223 if j is no_default and i > first_default 224 ] 225 226 if invalid_default_values: 227 raise ValueError('Some arguments %s do not have default value, but they ' 228 'are positioned after those with default values. This can ' 229 'not be expressed with ArgSpec.' % invalid_default_values) 230 231 return ArgSpec(args, varargs, keywords, tuple(all_defaults[first_default:])) 232 233 234def getfullargspec(obj): 235 """TFDecorator-aware replacement for `inspect.getfullargspec`. 236 237 This wrapper emulates `inspect.getfullargspec` in[^)]* Python2. 238 239 Args: 240 obj: A callable, possibly decorated. 241 242 Returns: 243 The `FullArgSpec` that describes the signature of 244 the outermost decorator that changes the callable's signature. If the 245 callable is not decorated, `inspect.getfullargspec()` will be called 246 directly on the callable. 247 """ 248 decorators, target = tf_decorator.unwrap(obj) 249 return next((_convert_maybe_argspec_to_fullargspec(d.decorator_argspec) 250 for d in decorators 251 if d.decorator_argspec is not None), _getfullargspec(target)) 252 253 254def getcallargs(func, *positional, **named): 255 """TFDecorator-aware replacement for inspect.getcallargs. 256 257 Args: 258 func: A callable, possibly decorated 259 *positional: The positional arguments that would be passed to `func`. 260 **named: The named argument dictionary that would be passed to `func`. 261 262 Returns: 263 A dictionary mapping `func`'s named arguments to the values they would 264 receive if `func(*positional, **named)` were called. 265 266 `getcallargs` will use the argspec from the outermost decorator that provides 267 it. If no attached decorators modify argspec, the final unwrapped target's 268 argspec will be used. 269 """ 270 argspec = getfullargspec(func) 271 call_args = named.copy() 272 this = getattr(func, 'im_self', None) or getattr(func, '__self__', None) 273 if ismethod(func) and this: 274 positional = (this,) + positional 275 remaining_positionals = [arg for arg in argspec.args if arg not in call_args] 276 call_args.update(dict(zip(remaining_positionals, positional))) 277 default_count = 0 if not argspec.defaults else len(argspec.defaults) 278 if default_count: 279 for arg, value in zip(argspec.args[-default_count:], argspec.defaults): 280 if arg not in call_args: 281 call_args[arg] = value 282 return call_args 283 284 285def getframeinfo(*args, **kwargs): 286 return _inspect.getframeinfo(*args, **kwargs) 287 288 289def getdoc(object): # pylint: disable=redefined-builtin 290 """TFDecorator-aware replacement for inspect.getdoc. 291 292 Args: 293 object: An object, possibly decorated. 294 295 Returns: 296 The docstring associated with the object. 297 298 The outermost-decorated object is intended to have the most complete 299 documentation, so the decorated parameter is not unwrapped. 300 """ 301 return _inspect.getdoc(object) 302 303 304def getfile(object): # pylint: disable=redefined-builtin 305 """TFDecorator-aware replacement for inspect.getfile.""" 306 unwrapped_object = tf_decorator.unwrap(object)[1] 307 308 # Work around for the case when object is a stack frame 309 # and only .pyc files are used. In this case, getfile 310 # might return incorrect path. So, we get the path from f_globals 311 # instead. 312 if (hasattr(unwrapped_object, 'f_globals') and 313 '__file__' in unwrapped_object.f_globals): 314 return unwrapped_object.f_globals['__file__'] 315 return _inspect.getfile(unwrapped_object) 316 317 318def getmembers(object, predicate=None): # pylint: disable=redefined-builtin 319 """TFDecorator-aware replacement for inspect.getmembers.""" 320 return _inspect.getmembers(object, predicate) 321 322 323def getmodule(object): # pylint: disable=redefined-builtin 324 """TFDecorator-aware replacement for inspect.getmodule.""" 325 return _inspect.getmodule(object) 326 327 328def getmro(cls): 329 """TFDecorator-aware replacement for inspect.getmro.""" 330 return _inspect.getmro(cls) 331 332 333def getsource(object): # pylint: disable=redefined-builtin 334 """TFDecorator-aware replacement for inspect.getsource.""" 335 return _inspect.getsource(tf_decorator.unwrap(object)[1]) 336 337 338def getsourcefile(object): # pylint: disable=redefined-builtin 339 """TFDecorator-aware replacement for inspect.getsourcefile.""" 340 return _inspect.getsourcefile(tf_decorator.unwrap(object)[1]) 341 342 343def getsourcelines(object): # pylint: disable=redefined-builtin 344 """TFDecorator-aware replacement for inspect.getsourcelines.""" 345 return _inspect.getsourcelines(tf_decorator.unwrap(object)[1]) 346 347 348def isbuiltin(object): # pylint: disable=redefined-builtin 349 """TFDecorator-aware replacement for inspect.isbuiltin.""" 350 return _inspect.isbuiltin(tf_decorator.unwrap(object)[1]) 351 352 353def isclass(object): # pylint: disable=redefined-builtin 354 """TFDecorator-aware replacement for inspect.isclass.""" 355 return _inspect.isclass(tf_decorator.unwrap(object)[1]) 356 357 358def isfunction(object): # pylint: disable=redefined-builtin 359 """TFDecorator-aware replacement for inspect.isfunction.""" 360 return _inspect.isfunction(tf_decorator.unwrap(object)[1]) 361 362 363def isframe(object): # pylint: disable=redefined-builtin 364 """TFDecorator-aware replacement for inspect.ismodule.""" 365 return _inspect.isframe(tf_decorator.unwrap(object)[1]) 366 367 368def isgenerator(object): # pylint: disable=redefined-builtin 369 """TFDecorator-aware replacement for inspect.isgenerator.""" 370 return _inspect.isgenerator(tf_decorator.unwrap(object)[1]) 371 372 373def ismethod(object): # pylint: disable=redefined-builtin 374 """TFDecorator-aware replacement for inspect.ismethod.""" 375 return _inspect.ismethod(tf_decorator.unwrap(object)[1]) 376 377 378def ismodule(object): # pylint: disable=redefined-builtin 379 """TFDecorator-aware replacement for inspect.ismodule.""" 380 return _inspect.ismodule(tf_decorator.unwrap(object)[1]) 381 382 383def isroutine(object): # pylint: disable=redefined-builtin 384 """TFDecorator-aware replacement for inspect.isroutine.""" 385 return _inspect.isroutine(tf_decorator.unwrap(object)[1]) 386 387 388def stack(context=1): 389 """TFDecorator-aware replacement for inspect.stack.""" 390 return _inspect.stack(context)[1:] 391 392 393def getsource_no_unwrap(obj): 394 """Return source code for an object. Does not unwrap TFDecorators. 395 396 The source code is returned literally, including indentation for functions not 397 at the top level. This function is analogous to inspect.getsource, with one 398 key difference - it doesn't unwrap decorators. For simplicity, support for 399 some Python object types is dropped (tracebacks, frames, code objects). 400 401 Args: 402 obj: a class, method, or function object. 403 404 Returns: 405 source code as a string 406 407 """ 408 lines, lnum = _inspect.findsource(obj) 409 return ''.join(_inspect.getblock(lines[lnum:])) 410