1""" 2 sphinx.util.inspect 3 ~~~~~~~~~~~~~~~~~~~ 4 5 Helpers for inspecting Python modules. 6 7 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 :license: BSD, see LICENSE for details. 9""" 10 11import builtins 12import contextlib 13import enum 14import inspect 15import re 16import sys 17import types 18import typing 19import warnings 20from functools import partial, partialmethod 21from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA 22from io import StringIO 23from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, cast 24 25from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning 26from sphinx.pycode.ast import ast # for py35-37 27from sphinx.pycode.ast import unparse as ast_unparse 28from sphinx.util import logging 29from sphinx.util.typing import ForwardRef 30from sphinx.util.typing import stringify as stringify_annotation 31 32if sys.version_info > (3, 7): 33 from types import ClassMethodDescriptorType, MethodDescriptorType, WrapperDescriptorType 34else: 35 ClassMethodDescriptorType = type(object.__init__) 36 MethodDescriptorType = type(str.join) 37 WrapperDescriptorType = type(dict.__dict__['fromkeys']) 38 39if False: 40 # For type annotation 41 from typing import Type # NOQA 42 43logger = logging.getLogger(__name__) 44 45memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE) 46 47 48# Copied from the definition of inspect.getfullargspec from Python master, 49# and modified to remove the use of special flags that break decorated 50# callables and bound methods in the name of backwards compatibility. Used 51# under the terms of PSF license v2, which requires the above statement 52# and the following: 53# 54# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 55# 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software 56# Foundation; All Rights Reserved 57def getargspec(func: Callable) -> Any: 58 """Like inspect.getfullargspec but supports bound methods, and wrapped 59 methods.""" 60 warnings.warn('sphinx.ext.inspect.getargspec() is deprecated', 61 RemovedInSphinx50Warning, stacklevel=2) 62 63 sig = inspect.signature(func) 64 65 args = [] 66 varargs = None 67 varkw = None 68 kwonlyargs = [] 69 defaults = () 70 annotations = {} 71 defaults = () 72 kwdefaults = {} 73 74 if sig.return_annotation is not sig.empty: 75 annotations['return'] = sig.return_annotation 76 77 for param in sig.parameters.values(): 78 kind = param.kind 79 name = param.name 80 81 if kind is Parameter.POSITIONAL_ONLY: 82 args.append(name) 83 elif kind is Parameter.POSITIONAL_OR_KEYWORD: 84 args.append(name) 85 if param.default is not param.empty: 86 defaults += (param.default,) # type: ignore 87 elif kind is Parameter.VAR_POSITIONAL: 88 varargs = name 89 elif kind is Parameter.KEYWORD_ONLY: 90 kwonlyargs.append(name) 91 if param.default is not param.empty: 92 kwdefaults[name] = param.default 93 elif kind is Parameter.VAR_KEYWORD: 94 varkw = name 95 96 if param.annotation is not param.empty: 97 annotations[name] = param.annotation 98 99 if not kwdefaults: 100 # compatibility with 'func.__kwdefaults__' 101 kwdefaults = None 102 103 if not defaults: 104 # compatibility with 'func.__defaults__' 105 defaults = None 106 107 return inspect.FullArgSpec(args, varargs, varkw, defaults, 108 kwonlyargs, kwdefaults, annotations) 109 110 111def unwrap(obj: Any) -> Any: 112 """Get an original object from wrapped object (wrapped functions).""" 113 try: 114 if hasattr(obj, '__sphinx_mock__'): 115 # Skip unwrapping mock object to avoid RecursionError 116 return obj 117 else: 118 return inspect.unwrap(obj) 119 except ValueError: 120 # might be a mock object 121 return obj 122 123 124def unwrap_all(obj: Any, *, stop: Callable = None) -> Any: 125 """ 126 Get an original object from wrapped object (unwrapping partials, wrapped 127 functions, and other decorators). 128 """ 129 while True: 130 if stop and stop(obj): 131 return obj 132 elif ispartial(obj): 133 obj = obj.func 134 elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'): 135 obj = obj.__wrapped__ 136 elif isclassmethod(obj): 137 obj = obj.__func__ 138 elif isstaticmethod(obj): 139 obj = obj.__func__ 140 else: 141 return obj 142 143 144def getall(obj: Any) -> Optional[Sequence[str]]: 145 """Get __all__ attribute of the module as dict. 146 147 Return None if given *obj* does not have __all__. 148 Raises ValueError if given *obj* have invalid __all__. 149 """ 150 __all__ = safe_getattr(obj, '__all__', None) 151 if __all__ is None: 152 return None 153 else: 154 if (isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__)): 155 return __all__ 156 else: 157 raise ValueError(__all__) 158 159 160def getannotations(obj: Any) -> Mapping[str, Any]: 161 """Get __annotations__ from given *obj* safely.""" 162 __annotations__ = safe_getattr(obj, '__annotations__', None) 163 if isinstance(__annotations__, Mapping): 164 return __annotations__ 165 else: 166 return {} 167 168 169def getmro(obj: Any) -> Tuple["Type", ...]: 170 """Get __mro__ from given *obj* safely.""" 171 __mro__ = safe_getattr(obj, '__mro__', None) 172 if isinstance(__mro__, tuple): 173 return __mro__ 174 else: 175 return tuple() 176 177 178def getslots(obj: Any) -> Optional[Dict]: 179 """Get __slots__ attribute of the class as dict. 180 181 Return None if gienv *obj* does not have __slots__. 182 Raises TypeError if given *obj* is not a class. 183 Raises ValueError if given *obj* have invalid __slots__. 184 """ 185 if not inspect.isclass(obj): 186 raise TypeError 187 188 __slots__ = safe_getattr(obj, '__slots__', None) 189 if __slots__ is None: 190 return None 191 elif isinstance(__slots__, dict): 192 return __slots__ 193 elif isinstance(__slots__, str): 194 return {__slots__: None} 195 elif isinstance(__slots__, (list, tuple)): 196 return {e: None for e in __slots__} 197 else: 198 raise ValueError 199 200 201def isNewType(obj: Any) -> bool: 202 """Check the if object is a kind of NewType.""" 203 __module__ = safe_getattr(obj, '__module__', None) 204 __qualname__ = safe_getattr(obj, '__qualname__', None) 205 if __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type': 206 return True 207 else: 208 return False 209 210 211def isenumclass(x: Any) -> bool: 212 """Check if the object is subclass of enum.""" 213 return inspect.isclass(x) and issubclass(x, enum.Enum) 214 215 216def isenumattribute(x: Any) -> bool: 217 """Check if the object is attribute of enum.""" 218 return isinstance(x, enum.Enum) 219 220 221def unpartial(obj: Any) -> Any: 222 """Get an original object from partial object. 223 224 This returns given object itself if not partial. 225 """ 226 while ispartial(obj): 227 obj = obj.func 228 229 return obj 230 231 232def ispartial(obj: Any) -> bool: 233 """Check if the object is partial.""" 234 return isinstance(obj, (partial, partialmethod)) 235 236 237def isclassmethod(obj: Any) -> bool: 238 """Check if the object is classmethod.""" 239 if isinstance(obj, classmethod): 240 return True 241 elif inspect.ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__): 242 return True 243 244 return False 245 246 247def isstaticmethod(obj: Any, cls: Any = None, name: str = None) -> bool: 248 """Check if the object is staticmethod.""" 249 if isinstance(obj, staticmethod): 250 return True 251 elif cls and name: 252 # trace __mro__ if the method is defined in parent class 253 # 254 # .. note:: This only works well with new style classes. 255 for basecls in getattr(cls, '__mro__', [cls]): 256 meth = basecls.__dict__.get(name) 257 if meth: 258 if isinstance(meth, staticmethod): 259 return True 260 else: 261 return False 262 263 return False 264 265 266def isdescriptor(x: Any) -> bool: 267 """Check if the object is some kind of descriptor.""" 268 for item in '__get__', '__set__', '__delete__': 269 if hasattr(safe_getattr(x, item, None), '__call__'): 270 return True 271 return False 272 273 274def isabstractmethod(obj: Any) -> bool: 275 """Check if the object is an abstractmethod.""" 276 return safe_getattr(obj, '__isabstractmethod__', False) is True 277 278 279def is_cython_function_or_method(obj: Any) -> bool: 280 """Check if the object is a function or method in cython.""" 281 try: 282 return obj.__class__.__name__ == 'cython_function_or_method' 283 except AttributeError: 284 return False 285 286 287def isattributedescriptor(obj: Any) -> bool: 288 """Check if the object is an attribute like descriptor.""" 289 if inspect.isdatadescriptor(obj): 290 # data descriptor is kind of attribute 291 return True 292 elif isdescriptor(obj): 293 # non data descriptor 294 unwrapped = unwrap(obj) 295 if isfunction(unwrapped) or isbuiltin(unwrapped) or inspect.ismethod(unwrapped): 296 # attribute must not be either function, builtin and method 297 return False 298 elif is_cython_function_or_method(unwrapped): 299 # attribute must not be either function and method (for cython) 300 return False 301 elif inspect.isclass(unwrapped): 302 # attribute must not be a class 303 return False 304 elif isinstance(unwrapped, (ClassMethodDescriptorType, 305 MethodDescriptorType, 306 WrapperDescriptorType)): 307 # attribute must not be a method descriptor 308 return False 309 elif type(unwrapped).__name__ == "instancemethod": 310 # attribute must not be an instancemethod (C-API) 311 return False 312 else: 313 return True 314 else: 315 return False 316 317 318def is_singledispatch_function(obj: Any) -> bool: 319 """Check if the object is singledispatch function.""" 320 if (inspect.isfunction(obj) and 321 hasattr(obj, 'dispatch') and 322 hasattr(obj, 'register') and 323 obj.dispatch.__module__ == 'functools'): 324 return True 325 else: 326 return False 327 328 329def is_singledispatch_method(obj: Any) -> bool: 330 """Check if the object is singledispatch method.""" 331 try: 332 from functools import singledispatchmethod # type: ignore 333 return isinstance(obj, singledispatchmethod) 334 except ImportError: # py35-37 335 return False 336 337 338def isfunction(obj: Any) -> bool: 339 """Check if the object is function.""" 340 return inspect.isfunction(unwrap_all(obj)) 341 342 343def isbuiltin(obj: Any) -> bool: 344 """Check if the object is builtin.""" 345 return inspect.isbuiltin(unwrap_all(obj)) 346 347 348def isroutine(obj: Any) -> bool: 349 """Check is any kind of function or method.""" 350 return inspect.isroutine(unwrap_all(obj)) 351 352 353def iscoroutinefunction(obj: Any) -> bool: 354 """Check if the object is coroutine-function.""" 355 # unwrap staticmethod, classmethod and partial (except wrappers) 356 obj = unwrap_all(obj, stop=lambda o: hasattr(o, '__wrapped__')) 357 if hasattr(obj, '__code__') and inspect.iscoroutinefunction(obj): 358 # check obj.__code__ because iscoroutinefunction() crashes for custom method-like 359 # objects (see https://github.com/sphinx-doc/sphinx/issues/6605) 360 return True 361 else: 362 return False 363 364 365def isproperty(obj: Any) -> bool: 366 """Check if the object is property.""" 367 if sys.version_info >= (3, 8): 368 from functools import cached_property # cached_property is available since py3.8 369 if isinstance(obj, cached_property): 370 return True 371 372 return isinstance(obj, property) 373 374 375def isgenericalias(obj: Any) -> bool: 376 """Check if the object is GenericAlias.""" 377 if (hasattr(typing, '_GenericAlias') and # only for py37+ 378 isinstance(obj, typing._GenericAlias)): # type: ignore 379 return True 380 elif (hasattr(types, 'GenericAlias') and # only for py39+ 381 isinstance(obj, types.GenericAlias)): # type: ignore 382 return True 383 elif (hasattr(typing, '_SpecialGenericAlias') and # for py39+ 384 isinstance(obj, typing._SpecialGenericAlias)): # type: ignore 385 return True 386 else: 387 return False 388 389 390def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any: 391 """A getattr() that turns all exceptions into AttributeErrors.""" 392 try: 393 return getattr(obj, name, *defargs) 394 except Exception as exc: 395 # sometimes accessing a property raises an exception (e.g. 396 # NotImplementedError), so let's try to read the attribute directly 397 try: 398 # In case the object does weird things with attribute access 399 # such that accessing `obj.__dict__` may raise an exception 400 return obj.__dict__[name] 401 except Exception: 402 pass 403 404 # this is a catch-all for all the weird things that some modules do 405 # with attribute access 406 if defargs: 407 return defargs[0] 408 409 raise AttributeError(name) from exc 410 411 412def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None, 413 attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]: 414 """A version of inspect.getmembers() that uses safe_getattr().""" 415 warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning, stacklevel=2) 416 417 results = [] # type: List[Tuple[str, Any]] 418 for key in dir(object): 419 try: 420 value = attr_getter(object, key, None) 421 except AttributeError: 422 continue 423 if not predicate or predicate(value): 424 results.append((key, value)) 425 results.sort() 426 return results 427 428 429def object_description(object: Any) -> str: 430 """A repr() implementation that returns text safe to use in reST context.""" 431 if isinstance(object, dict): 432 try: 433 sorted_keys = sorted(object) 434 except Exception: 435 pass # Cannot sort dict keys, fall back to generic repr 436 else: 437 items = ("%s: %s" % 438 (object_description(key), object_description(object[key])) 439 for key in sorted_keys) 440 return "{%s}" % ", ".join(items) 441 if isinstance(object, set): 442 try: 443 sorted_values = sorted(object) 444 except TypeError: 445 pass # Cannot sort set values, fall back to generic repr 446 else: 447 return "{%s}" % ", ".join(object_description(x) for x in sorted_values) 448 if isinstance(object, frozenset): 449 try: 450 sorted_values = sorted(object) 451 except TypeError: 452 pass # Cannot sort frozenset values, fall back to generic repr 453 else: 454 return "frozenset({%s})" % ", ".join(object_description(x) 455 for x in sorted_values) 456 try: 457 s = repr(object) 458 except Exception as exc: 459 raise ValueError from exc 460 # Strip non-deterministic memory addresses such as 461 # ``<__main__.A at 0x7f68cb685710>`` 462 s = memory_address_re.sub('', s) 463 return s.replace('\n', ' ') 464 465 466def is_builtin_class_method(obj: Any, attr_name: str) -> bool: 467 """If attr_name is implemented at builtin class, return True. 468 469 >>> is_builtin_class_method(int, '__init__') 470 True 471 472 Why this function needed? CPython implements int.__init__ by Descriptor 473 but PyPy implements it by pure Python code. 474 """ 475 try: 476 mro = getmro(obj) 477 cls = next(c for c in mro if attr_name in safe_getattr(c, '__dict__', {})) 478 except StopIteration: 479 return False 480 481 try: 482 name = safe_getattr(cls, '__name__') 483 except AttributeError: 484 return False 485 486 return getattr(builtins, name, None) is cls 487 488 489class DefaultValue: 490 """A simple wrapper for default value of the parameters of overload functions.""" 491 492 def __init__(self, value: str) -> None: 493 self.value = value 494 495 def __eq__(self, other: object) -> bool: 496 return self.value == other 497 498 def __repr__(self) -> str: 499 return self.value 500 501 502def _should_unwrap(subject: Callable) -> bool: 503 """Check the function should be unwrapped on getting signature.""" 504 if (safe_getattr(subject, '__globals__', None) and 505 subject.__globals__.get('__name__') == 'contextlib' and # type: ignore 506 subject.__globals__.get('__file__') == contextlib.__file__): # type: ignore 507 # contextmanger should be unwrapped 508 return True 509 510 return False 511 512 513def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = None, 514 type_aliases: Dict = {}) -> inspect.Signature: 515 """Return a Signature object for the given *subject*. 516 517 :param bound_method: Specify *subject* is a bound method or not 518 :param follow_wrapped: Same as ``inspect.signature()``. 519 """ 520 521 if follow_wrapped is None: 522 follow_wrapped = True 523 else: 524 warnings.warn('The follow_wrapped argument of sphinx.util.inspect.signature() is ' 525 'deprecated', RemovedInSphinx50Warning, stacklevel=2) 526 527 try: 528 try: 529 if _should_unwrap(subject): 530 signature = inspect.signature(subject) 531 else: 532 signature = inspect.signature(subject, follow_wrapped=follow_wrapped) 533 except ValueError: 534 # follow built-in wrappers up (ex. functools.lru_cache) 535 signature = inspect.signature(subject) 536 parameters = list(signature.parameters.values()) 537 return_annotation = signature.return_annotation 538 except IndexError: 539 # Until python 3.6.4, cpython has been crashed on inspection for 540 # partialmethods not having any arguments. 541 # https://bugs.python.org/issue33009 542 if hasattr(subject, '_partialmethod'): 543 parameters = [] 544 return_annotation = Parameter.empty 545 else: 546 raise 547 548 try: 549 # Resolve annotations using ``get_type_hints()`` and type_aliases. 550 annotations = typing.get_type_hints(subject, None, type_aliases) 551 for i, param in enumerate(parameters): 552 if param.name in annotations: 553 parameters[i] = param.replace(annotation=annotations[param.name]) 554 if 'return' in annotations: 555 return_annotation = annotations['return'] 556 except Exception: 557 # ``get_type_hints()`` does not support some kind of objects like partial, 558 # ForwardRef and so on. 559 pass 560 561 if bound_method: 562 if inspect.ismethod(subject): 563 # ``inspect.signature()`` considers the subject is a bound method and removes 564 # first argument from signature. Therefore no skips are needed here. 565 pass 566 else: 567 if len(parameters) > 0: 568 parameters.pop(0) 569 570 # To allow to create signature object correctly for pure python functions, 571 # pass an internal parameter __validate_parameters__=False to Signature 572 # 573 # For example, this helps a function having a default value `inspect._empty`. 574 # refs: https://github.com/sphinx-doc/sphinx/issues/7935 575 return inspect.Signature(parameters, return_annotation=return_annotation, # type: ignore 576 __validate_parameters__=False) 577 578 579def evaluate_signature(sig: inspect.Signature, globalns: Dict = None, localns: Dict = None 580 ) -> inspect.Signature: 581 """Evaluate unresolved type annotations in a signature object.""" 582 def evaluate_forwardref(ref: ForwardRef, globalns: Dict, localns: Dict) -> Any: 583 """Evaluate a forward reference.""" 584 if sys.version_info > (3, 9): 585 return ref._evaluate(globalns, localns, frozenset()) 586 else: 587 return ref._evaluate(globalns, localns) 588 589 def evaluate(annotation: Any, globalns: Dict, localns: Dict) -> Any: 590 """Evaluate unresolved type annotation.""" 591 try: 592 if isinstance(annotation, str): 593 ref = ForwardRef(annotation, True) 594 annotation = evaluate_forwardref(ref, globalns, localns) 595 596 if isinstance(annotation, ForwardRef): 597 annotation = evaluate_forwardref(ref, globalns, localns) 598 elif isinstance(annotation, str): 599 # might be a ForwardRef'ed annotation in overloaded functions 600 ref = ForwardRef(annotation, True) 601 annotation = evaluate_forwardref(ref, globalns, localns) 602 except (NameError, TypeError): 603 # failed to evaluate type. skipped. 604 pass 605 606 return annotation 607 608 if globalns is None: 609 globalns = {} 610 if localns is None: 611 localns = globalns 612 613 parameters = list(sig.parameters.values()) 614 for i, param in enumerate(parameters): 615 if param.annotation: 616 annotation = evaluate(param.annotation, globalns, localns) 617 parameters[i] = param.replace(annotation=annotation) 618 619 return_annotation = sig.return_annotation 620 if return_annotation: 621 return_annotation = evaluate(return_annotation, globalns, localns) 622 623 return sig.replace(parameters=parameters, return_annotation=return_annotation) 624 625 626def stringify_signature(sig: inspect.Signature, show_annotation: bool = True, 627 show_return_annotation: bool = True) -> str: 628 """Stringify a Signature object. 629 630 :param show_annotation: Show annotation in result 631 """ 632 args = [] 633 last_kind = None 634 for param in sig.parameters.values(): 635 if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: 636 # PEP-570: Separator for Positional Only Parameter: / 637 args.append('/') 638 if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD, 639 param.POSITIONAL_ONLY, 640 None): 641 # PEP-3102: Separator for Keyword Only Parameter: * 642 args.append('*') 643 644 arg = StringIO() 645 if param.kind == param.VAR_POSITIONAL: 646 arg.write('*' + param.name) 647 elif param.kind == param.VAR_KEYWORD: 648 arg.write('**' + param.name) 649 else: 650 arg.write(param.name) 651 652 if show_annotation and param.annotation is not param.empty: 653 arg.write(': ') 654 arg.write(stringify_annotation(param.annotation)) 655 if param.default is not param.empty: 656 if show_annotation and param.annotation is not param.empty: 657 arg.write(' = ') 658 else: 659 arg.write('=') 660 arg.write(object_description(param.default)) 661 662 args.append(arg.getvalue()) 663 last_kind = param.kind 664 665 if last_kind == Parameter.POSITIONAL_ONLY: 666 # PEP-570: Separator for Positional Only Parameter: / 667 args.append('/') 668 669 if (sig.return_annotation is Parameter.empty or 670 show_annotation is False or 671 show_return_annotation is False): 672 return '(%s)' % ', '.join(args) 673 else: 674 annotation = stringify_annotation(sig.return_annotation) 675 return '(%s) -> %s' % (', '.join(args), annotation) 676 677 678def signature_from_str(signature: str) -> inspect.Signature: 679 """Create a Signature object from string.""" 680 code = 'def func' + signature + ': pass' 681 module = ast.parse(code) 682 function = cast(ast.FunctionDef, module.body[0]) # type: ignore 683 684 return signature_from_ast(function, code) 685 686 687def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signature: 688 """Create a Signature object from AST *node*.""" 689 args = node.args 690 defaults = list(args.defaults) 691 params = [] 692 if hasattr(args, "posonlyargs"): 693 posonlyargs = len(args.posonlyargs) # type: ignore 694 positionals = posonlyargs + len(args.args) 695 else: 696 posonlyargs = 0 697 positionals = len(args.args) 698 699 for _ in range(len(defaults), positionals): 700 defaults.insert(0, Parameter.empty) 701 702 if hasattr(args, "posonlyargs"): 703 for i, arg in enumerate(args.posonlyargs): # type: ignore 704 if defaults[i] is Parameter.empty: 705 default = Parameter.empty 706 else: 707 default = DefaultValue(ast_unparse(defaults[i], code)) 708 709 annotation = ast_unparse(arg.annotation, code) or Parameter.empty 710 params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY, 711 default=default, annotation=annotation)) 712 713 for i, arg in enumerate(args.args): 714 if defaults[i + posonlyargs] is Parameter.empty: 715 default = Parameter.empty 716 else: 717 default = DefaultValue(ast_unparse(defaults[i + posonlyargs], code)) 718 719 annotation = ast_unparse(arg.annotation, code) or Parameter.empty 720 params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD, 721 default=default, annotation=annotation)) 722 723 if args.vararg: 724 annotation = ast_unparse(args.vararg.annotation, code) or Parameter.empty 725 params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL, 726 annotation=annotation)) 727 728 for i, arg in enumerate(args.kwonlyargs): 729 default = ast_unparse(args.kw_defaults[i], code) or Parameter.empty 730 annotation = ast_unparse(arg.annotation, code) or Parameter.empty 731 params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default, 732 annotation=annotation)) 733 734 if args.kwarg: 735 annotation = ast_unparse(args.kwarg.annotation, code) or Parameter.empty 736 params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD, 737 annotation=annotation)) 738 739 return_annotation = ast_unparse(node.returns, code) or Parameter.empty 740 741 return inspect.Signature(params, return_annotation=return_annotation) 742 743 744class Signature: 745 """The Signature object represents the call signature of a callable object and 746 its return annotation. 747 """ 748 749 empty = inspect.Signature.empty 750 751 def __init__(self, subject: Callable, bound_method: bool = False, 752 has_retval: bool = True) -> None: 753 warnings.warn('sphinx.util.inspect.Signature() is deprecated', 754 RemovedInSphinx40Warning, stacklevel=2) 755 756 # check subject is not a built-in class (ex. int, str) 757 if (isinstance(subject, type) and 758 is_builtin_class_method(subject, "__new__") and 759 is_builtin_class_method(subject, "__init__")): 760 raise TypeError("can't compute signature for built-in type {}".format(subject)) 761 762 self.subject = subject 763 self.has_retval = has_retval 764 self.partialmethod_with_noargs = False 765 766 try: 767 self.signature = inspect.signature(subject) # type: Optional[inspect.Signature] 768 except IndexError: 769 # Until python 3.6.4, cpython has been crashed on inspection for 770 # partialmethods not having any arguments. 771 # https://bugs.python.org/issue33009 772 if hasattr(subject, '_partialmethod'): 773 self.signature = None 774 self.partialmethod_with_noargs = True 775 else: 776 raise 777 778 try: 779 self.annotations = typing.get_type_hints(subject) 780 except Exception: 781 # get_type_hints() does not support some kind of objects like partial, 782 # ForwardRef and so on. For them, it raises an exception. In that case, 783 # we try to build annotations from argspec. 784 self.annotations = {} 785 786 if bound_method: 787 # client gives a hint that the subject is a bound method 788 789 if inspect.ismethod(subject): 790 # inspect.signature already considers the subject is bound method. 791 # So it is not need to skip first argument. 792 self.skip_first_argument = False 793 else: 794 self.skip_first_argument = True 795 else: 796 # inspect.signature recognizes type of method properly without any hints 797 self.skip_first_argument = False 798 799 @property 800 def parameters(self) -> Mapping: 801 if self.partialmethod_with_noargs: 802 return {} 803 else: 804 return self.signature.parameters 805 806 @property 807 def return_annotation(self) -> Any: 808 if self.signature: 809 if self.has_retval: 810 return self.signature.return_annotation 811 else: 812 return Parameter.empty 813 else: 814 return None 815 816 def format_args(self, show_annotation: bool = True) -> str: 817 def get_annotation(param: Parameter) -> Any: 818 if isinstance(param.annotation, str) and param.name in self.annotations: 819 return self.annotations[param.name] 820 else: 821 return param.annotation 822 823 args = [] 824 last_kind = None 825 for i, param in enumerate(self.parameters.values()): 826 # skip first argument if subject is bound method 827 if self.skip_first_argument and i == 0: 828 continue 829 830 arg = StringIO() 831 832 # insert '*' between POSITIONAL args and KEYWORD_ONLY args:: 833 # func(a, b, *, c, d): 834 if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD, 835 param.POSITIONAL_ONLY, 836 None): 837 args.append('*') 838 839 if param.kind in (param.POSITIONAL_ONLY, 840 param.POSITIONAL_OR_KEYWORD, 841 param.KEYWORD_ONLY): 842 arg.write(param.name) 843 if show_annotation and param.annotation is not param.empty: 844 arg.write(': ') 845 arg.write(stringify_annotation(get_annotation(param))) 846 if param.default is not param.empty: 847 if param.annotation is param.empty or show_annotation is False: 848 arg.write('=') 849 arg.write(object_description(param.default)) 850 else: 851 arg.write(' = ') 852 arg.write(object_description(param.default)) 853 elif param.kind == param.VAR_POSITIONAL: 854 arg.write('*') 855 arg.write(param.name) 856 if show_annotation and param.annotation is not param.empty: 857 arg.write(': ') 858 arg.write(stringify_annotation(get_annotation(param))) 859 elif param.kind == param.VAR_KEYWORD: 860 arg.write('**') 861 arg.write(param.name) 862 if show_annotation and param.annotation is not param.empty: 863 arg.write(': ') 864 arg.write(stringify_annotation(get_annotation(param))) 865 866 args.append(arg.getvalue()) 867 last_kind = param.kind 868 869 if self.return_annotation is Parameter.empty or show_annotation is False: 870 return '(%s)' % ', '.join(args) 871 else: 872 if 'return' in self.annotations: 873 annotation = stringify_annotation(self.annotations['return']) 874 else: 875 annotation = stringify_annotation(self.return_annotation) 876 877 return '(%s) -> %s' % (', '.join(args), annotation) 878 879 def format_annotation(self, annotation: Any) -> str: 880 """Return formatted representation of a type annotation.""" 881 return stringify_annotation(annotation) 882 883 def format_annotation_new(self, annotation: Any) -> str: 884 """format_annotation() for py37+""" 885 return stringify_annotation(annotation) 886 887 def format_annotation_old(self, annotation: Any) -> str: 888 """format_annotation() for py36 or below""" 889 return stringify_annotation(annotation) 890 891 892def getdoc(obj: Any, attrgetter: Callable = safe_getattr, 893 allow_inherited: bool = False, cls: Any = None, name: str = None) -> str: 894 """Get the docstring for the object. 895 896 This tries to obtain the docstring for some kind of objects additionally: 897 898 * partial functions 899 * inherited docstring 900 * inherited decorated methods 901 """ 902 doc = attrgetter(obj, '__doc__', None) 903 if ispartial(obj) and doc == obj.__class__.__doc__: 904 return getdoc(obj.func) 905 elif doc is None and allow_inherited: 906 doc = inspect.getdoc(obj) 907 908 if doc is None and cls: 909 # inspect.getdoc() does not support some kind of inherited and decorated methods. 910 # This tries to obtain the docstring from super classes. 911 for basecls in getattr(cls, '__mro__', []): 912 meth = safe_getattr(basecls, name, None) 913 if meth is not None: 914 doc = inspect.getdoc(meth) 915 if doc: 916 break 917 918 return doc 919