Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/sphinx/util/inspect.py: 9%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

485 statements  

1"""Helpers for inspecting Python modules.""" 

2 

3from __future__ import annotations 

4 

5import ast 

6import builtins 

7import contextlib 

8import enum 

9import inspect 

10import re 

11import sys 

12import types 

13import typing 

14from collections.abc import Mapping 

15from functools import cached_property, partial, partialmethod, singledispatchmethod 

16from importlib import import_module 

17from inspect import Parameter, Signature 

18from io import StringIO 

19from types import ClassMethodDescriptorType, MethodDescriptorType, WrapperDescriptorType 

20from typing import TYPE_CHECKING, Any 

21 

22from sphinx.pycode.ast import unparse as ast_unparse 

23from sphinx.util import logging 

24from sphinx.util.typing import ForwardRef, stringify_annotation 

25 

26if TYPE_CHECKING: 

27 from collections.abc import Callable, Sequence 

28 from inspect import _ParameterKind 

29 from types import MethodType, ModuleType 

30 from typing import Final, Protocol, Union 

31 

32 from typing_extensions import TypeAlias, TypeIs 

33 

34 class _SupportsGet(Protocol): 

35 def __get__(self, __instance: Any, __owner: type | None = ...) -> Any: ... # NoQA: E704 

36 

37 class _SupportsSet(Protocol): 

38 # instance and value are contravariants but we do not need that precision 

39 def __set__(self, __instance: Any, __value: Any) -> None: ... # NoQA: E704 

40 

41 class _SupportsDelete(Protocol): 

42 # instance is contravariant but we do not need that precision 

43 def __delete__(self, __instance: Any) -> None: ... # NoQA: E704 

44 

45 _RoutineType: TypeAlias = Union[ 

46 types.FunctionType, 

47 types.LambdaType, 

48 types.MethodType, 

49 types.BuiltinFunctionType, 

50 types.BuiltinMethodType, 

51 types.WrapperDescriptorType, 

52 types.MethodDescriptorType, 

53 types.ClassMethodDescriptorType, 

54 ] 

55 _SignatureType: TypeAlias = Union[ 

56 Callable[..., Any], 

57 staticmethod, 

58 classmethod, 

59 ] 

60 

61logger = logging.getLogger(__name__) 

62 

63memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE) 

64 

65# re-export as is 

66isasyncgenfunction = inspect.isasyncgenfunction 

67ismethod = inspect.ismethod 

68ismethoddescriptor = inspect.ismethoddescriptor 

69isclass = inspect.isclass 

70ismodule = inspect.ismodule 

71 

72 

73def unwrap(obj: Any) -> Any: 

74 """Get an original object from wrapped object (wrapped functions). 

75 

76 Mocked objects are returned as is. 

77 """ 

78 if hasattr(obj, '__sphinx_mock__'): 

79 # Skip unwrapping mock object to avoid RecursionError 

80 return obj 

81 

82 try: 

83 return inspect.unwrap(obj) 

84 except ValueError: 

85 # might be a mock object 

86 return obj 

87 

88 

89def unwrap_all(obj: Any, *, stop: Callable[[Any], bool] | None = None) -> Any: 

90 """Get an original object from wrapped object. 

91 

92 Unlike :func:`unwrap`, this unwraps partial functions, wrapped functions, 

93 class methods and static methods. 

94 

95 When specified, *stop* is a predicate indicating whether an object should 

96 be unwrapped or not. 

97 """ 

98 if callable(stop): 

99 while not stop(obj): 

100 if ispartial(obj): 

101 obj = obj.func 

102 elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'): 

103 obj = obj.__wrapped__ 

104 elif isclassmethod(obj) or isstaticmethod(obj): 

105 obj = obj.__func__ 

106 else: 

107 return obj 

108 return obj # in case the while loop never starts 

109 

110 while True: 

111 if ispartial(obj): 

112 obj = obj.func 

113 elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'): 

114 obj = obj.__wrapped__ 

115 elif isclassmethod(obj) or isstaticmethod(obj): 

116 obj = obj.__func__ 

117 else: 

118 return obj 

119 

120 

121def getall(obj: Any) -> Sequence[str] | None: 

122 """Get the ``__all__`` attribute of an object as a sequence. 

123 

124 This returns ``None`` if the given ``obj.__all__`` does not exist and 

125 raises :exc:`ValueError` if ``obj.__all__`` is not a list or tuple of 

126 strings. 

127 """ 

128 __all__ = safe_getattr(obj, '__all__', None) 

129 if __all__ is None: 

130 return None 

131 if isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__): 

132 return __all__ 

133 raise ValueError(__all__) 

134 

135 

136def getannotations(obj: Any) -> Mapping[str, Any]: 

137 """Safely get the ``__annotations__`` attribute of an object.""" 

138 if sys.version_info >= (3, 10, 0) or not isinstance(obj, type): 

139 __annotations__ = safe_getattr(obj, '__annotations__', None) 

140 else: 

141 # Workaround for bugfix not available until python 3.10 as recommended by docs 

142 # https://docs.python.org/3.10/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older 

143 __dict__ = safe_getattr(obj, '__dict__', {}) 

144 __annotations__ = __dict__.get('__annotations__', None) 

145 if isinstance(__annotations__, Mapping): 

146 return __annotations__ 

147 return {} 

148 

149 

150def getglobals(obj: Any) -> Mapping[str, Any]: 

151 """Safely get :attr:`obj.__globals__ <function.__globals__>`.""" 

152 __globals__ = safe_getattr(obj, '__globals__', None) 

153 if isinstance(__globals__, Mapping): 

154 return __globals__ 

155 return {} 

156 

157 

158def getmro(obj: Any) -> tuple[type, ...]: 

159 """Safely get :attr:`obj.__mro__ <class.__mro__>`.""" 

160 __mro__ = safe_getattr(obj, '__mro__', None) 

161 if isinstance(__mro__, tuple): 

162 return __mro__ 

163 return () 

164 

165 

166def getorigbases(obj: Any) -> tuple[Any, ...] | None: 

167 """Safely get ``obj.__orig_bases__``. 

168 

169 This returns ``None`` if the object is not a class or if ``__orig_bases__`` 

170 is not well-defined (e.g., a non-tuple object or an empty sequence). 

171 """ 

172 if not isclass(obj): 

173 return None 

174 

175 # Get __orig_bases__ from obj.__dict__ to avoid accessing the parent's __orig_bases__. 

176 # refs: https://github.com/sphinx-doc/sphinx/issues/9607 

177 __dict__ = safe_getattr(obj, '__dict__', {}) 

178 __orig_bases__ = __dict__.get('__orig_bases__') 

179 if isinstance(__orig_bases__, tuple) and len(__orig_bases__) > 0: 

180 return __orig_bases__ 

181 return None 

182 

183 

184def getslots(obj: Any) -> dict[str, Any] | dict[str, None] | None: 

185 """Safely get :term:`obj.__slots__ <__slots__>` as a dictionary if any. 

186 

187 - This returns ``None`` if ``obj.__slots__`` does not exist. 

188 - This raises a :exc:`TypeError` if *obj* is not a class. 

189 - This raises a :exc:`ValueError` if ``obj.__slots__`` is invalid. 

190 """ 

191 if not isclass(obj): 

192 raise TypeError 

193 

194 __slots__ = safe_getattr(obj, '__slots__', None) 

195 if __slots__ is None: 

196 return None 

197 elif isinstance(__slots__, dict): 

198 return __slots__ 

199 elif isinstance(__slots__, str): 

200 return {__slots__: None} 

201 elif isinstance(__slots__, (list, tuple)): 

202 return dict.fromkeys(__slots__) 

203 else: 

204 raise ValueError 

205 

206 

207def isNewType(obj: Any) -> bool: 

208 """Check the if object is a kind of :class:`~typing.NewType`.""" 

209 if sys.version_info[:2] >= (3, 10): 

210 return isinstance(obj, typing.NewType) 

211 __module__ = safe_getattr(obj, '__module__', None) 

212 __qualname__ = safe_getattr(obj, '__qualname__', None) 

213 return __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type' 

214 

215 

216def isenumclass(x: Any) -> TypeIs[type[enum.Enum]]: 

217 """Check if the object is an :class:`enumeration class <enum.Enum>`.""" 

218 return isclass(x) and issubclass(x, enum.Enum) 

219 

220 

221def isenumattribute(x: Any) -> TypeIs[enum.Enum]: 

222 """Check if the object is an enumeration attribute.""" 

223 return isinstance(x, enum.Enum) 

224 

225 

226def unpartial(obj: Any) -> Any: 

227 """Get an original object from a partial-like object. 

228 

229 If *obj* is not a partial object, it is returned as is. 

230 

231 .. seealso:: :func:`ispartial` 

232 """ 

233 while ispartial(obj): 

234 obj = obj.func 

235 return obj 

236 

237 

238def ispartial(obj: Any) -> TypeIs[partial | partialmethod]: 

239 """Check if the object is a partial function or method.""" 

240 return isinstance(obj, (partial, partialmethod)) 

241 

242 

243def isclassmethod( 

244 obj: Any, 

245 cls: Any = None, 

246 name: str | None = None, 

247) -> TypeIs[classmethod]: 

248 """Check if the object is a :class:`classmethod`.""" 

249 if isinstance(obj, classmethod): 

250 return True 

251 if ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__): 

252 return True 

253 if cls and name: 

254 # trace __mro__ if the method is defined in parent class 

255 sentinel = object() 

256 for basecls in getmro(cls): 

257 meth = basecls.__dict__.get(name, sentinel) 

258 if meth is not sentinel: 

259 return isclassmethod(meth) 

260 return False 

261 

262 

263def isstaticmethod( 

264 obj: Any, 

265 cls: Any = None, 

266 name: str | None = None, 

267) -> TypeIs[staticmethod]: 

268 """Check if the object is a :class:`staticmethod`.""" 

269 if isinstance(obj, staticmethod): 

270 return True 

271 if cls and name: 

272 # trace __mro__ if the method is defined in parent class 

273 sentinel = object() 

274 for basecls in getattr(cls, '__mro__', [cls]): 

275 meth = basecls.__dict__.get(name, sentinel) 

276 if meth is not sentinel: 

277 return isinstance(meth, staticmethod) 

278 return False 

279 

280 

281def isdescriptor(x: Any) -> TypeIs[_SupportsGet | _SupportsSet | _SupportsDelete]: 

282 """Check if the object is a :external+python:term:`descriptor`.""" 

283 return any( 

284 callable(safe_getattr(x, item, None)) for item in ('__get__', '__set__', '__delete__') 

285 ) 

286 

287 

288def isabstractmethod(obj: Any) -> bool: 

289 """Check if the object is an :func:`abstractmethod`.""" 

290 return safe_getattr(obj, '__isabstractmethod__', False) is True 

291 

292 

293def isboundmethod(method: MethodType) -> bool: 

294 """Check if the method is a bound method.""" 

295 return safe_getattr(method, '__self__', None) is not None 

296 

297 

298def is_cython_function_or_method(obj: Any) -> bool: 

299 """Check if the object is a function or method in cython.""" 

300 try: 

301 return obj.__class__.__name__ == 'cython_function_or_method' 

302 except AttributeError: 

303 return False 

304 

305 

306_DESCRIPTOR_LIKE: Final[tuple[type, ...]] = ( 

307 ClassMethodDescriptorType, 

308 MethodDescriptorType, 

309 WrapperDescriptorType, 

310) 

311 

312 

313def isattributedescriptor(obj: Any) -> bool: 

314 """Check if the object is an attribute-like descriptor.""" 

315 if inspect.isdatadescriptor(obj): 

316 # data descriptor is kind of attribute 

317 return True 

318 if isdescriptor(obj): 

319 # non data descriptor 

320 unwrapped = unwrap(obj) 

321 if isfunction(unwrapped) or isbuiltin(unwrapped) or ismethod(unwrapped): 

322 # attribute must not be either function, builtin and method 

323 return False 

324 if is_cython_function_or_method(unwrapped): 

325 # attribute must not be either function and method (for cython) 

326 return False 

327 if isclass(unwrapped): 

328 # attribute must not be a class 

329 return False 

330 if isinstance(unwrapped, _DESCRIPTOR_LIKE): 

331 # attribute must not be a method descriptor 

332 return False 

333 # attribute must not be an instancemethod (C-API) 

334 return type(unwrapped).__name__ != 'instancemethod' 

335 return False 

336 

337 

338def is_singledispatch_function(obj: Any) -> bool: 

339 """Check if the object is a :func:`~functools.singledispatch` function.""" 

340 return ( 

341 inspect.isfunction(obj) 

342 and hasattr(obj, 'dispatch') 

343 and hasattr(obj, 'register') 

344 and obj.dispatch.__module__ == 'functools' 

345 ) 

346 

347 

348def is_singledispatch_method(obj: Any) -> TypeIs[singledispatchmethod]: 

349 """Check if the object is a :class:`~functools.singledispatchmethod`.""" 

350 return isinstance(obj, singledispatchmethod) 

351 

352 

353def isfunction(obj: Any) -> TypeIs[types.FunctionType]: 

354 """Check if the object is a user-defined function. 

355 

356 Partial objects are unwrapped before checking them. 

357 

358 .. seealso:: :external+python:func:`inspect.isfunction` 

359 """ 

360 return inspect.isfunction(unpartial(obj)) 

361 

362 

363def isbuiltin(obj: Any) -> TypeIs[types.BuiltinFunctionType]: 

364 """Check if the object is a built-in function or method. 

365 

366 Partial objects are unwrapped before checking them. 

367 

368 .. seealso:: :external+python:func:`inspect.isbuiltin` 

369 """ 

370 return inspect.isbuiltin(unpartial(obj)) 

371 

372 

373def isroutine(obj: Any) -> TypeIs[_RoutineType]: 

374 """Check if the object is a kind of function or method. 

375 

376 Partial objects are unwrapped before checking them. 

377 

378 .. seealso:: :external+python:func:`inspect.isroutine` 

379 """ 

380 return inspect.isroutine(unpartial(obj)) 

381 

382 

383def iscoroutinefunction(obj: Any) -> TypeIs[Callable[..., types.CoroutineType]]: 

384 """Check if the object is a :external+python:term:`coroutine` function.""" 

385 obj = unwrap_all(obj, stop=_is_wrapped_coroutine) 

386 return inspect.iscoroutinefunction(obj) 

387 

388 

389def _is_wrapped_coroutine(obj: Any) -> bool: 

390 """Check if the object is wrapped coroutine-function.""" 

391 if isstaticmethod(obj) or isclassmethod(obj) or ispartial(obj): 

392 # staticmethod, classmethod and partial method are not a wrapped coroutine-function 

393 # Note: Since 3.10, staticmethod and classmethod becomes a kind of wrappers 

394 return False 

395 return hasattr(obj, '__wrapped__') 

396 

397 

398def isproperty(obj: Any) -> TypeIs[property | cached_property]: 

399 """Check if the object is property (possibly cached).""" 

400 return isinstance(obj, (property, cached_property)) 

401 

402 

403def isgenericalias(obj: Any) -> TypeIs[types.GenericAlias]: 

404 """Check if the object is a generic alias.""" 

405 return isinstance(obj, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined] 

406 

407 

408def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any: 

409 """A getattr() that turns all exceptions into AttributeErrors.""" 

410 try: 

411 return getattr(obj, name, *defargs) 

412 except Exception as exc: 

413 # sometimes accessing a property raises an exception (e.g. 

414 # NotImplementedError), so let's try to read the attribute directly 

415 try: 

416 # In case the object does weird things with attribute access 

417 # such that accessing `obj.__dict__` may raise an exception 

418 return obj.__dict__[name] 

419 except Exception: 

420 pass 

421 

422 # this is a catch-all for all the weird things that some modules do 

423 # with attribute access 

424 if defargs: 

425 return defargs[0] 

426 

427 raise AttributeError(name) from exc 

428 

429 

430def object_description(obj: Any, *, _seen: frozenset[int] = frozenset()) -> str: 

431 """A repr() implementation that returns text safe to use in reST context. 

432 

433 Maintains a set of 'seen' object IDs to detect and avoid infinite recursion. 

434 """ 

435 seen = _seen 

436 if isinstance(obj, dict): 

437 if id(obj) in seen: 

438 return 'dict(...)' 

439 seen |= {id(obj)} 

440 try: 

441 sorted_keys = sorted(obj) 

442 except TypeError: 

443 # Cannot sort dict keys, fall back to using descriptions as a sort key 

444 sorted_keys = sorted(obj, key=lambda k: object_description(k, _seen=seen)) 

445 

446 items = ( 

447 (object_description(key, _seen=seen), object_description(obj[key], _seen=seen)) 

448 for key in sorted_keys 

449 ) 

450 return '{%s}' % ', '.join(f'{key}: {value}' for (key, value) in items) 

451 elif isinstance(obj, set): 

452 if id(obj) in seen: 

453 return 'set(...)' 

454 seen |= {id(obj)} 

455 try: 

456 sorted_values = sorted(obj) 

457 except TypeError: 

458 # Cannot sort set values, fall back to using descriptions as a sort key 

459 sorted_values = sorted(obj, key=lambda x: object_description(x, _seen=seen)) 

460 return '{%s}' % ', '.join(object_description(x, _seen=seen) for x in sorted_values) 

461 elif isinstance(obj, frozenset): 

462 if id(obj) in seen: 

463 return 'frozenset(...)' 

464 seen |= {id(obj)} 

465 try: 

466 sorted_values = sorted(obj) 

467 except TypeError: 

468 # Cannot sort frozenset values, fall back to using descriptions as a sort key 

469 sorted_values = sorted(obj, key=lambda x: object_description(x, _seen=seen)) 

470 return 'frozenset({%s})' % ', '.join( 

471 object_description(x, _seen=seen) for x in sorted_values 

472 ) 

473 elif isinstance(obj, enum.Enum): 

474 if obj.__repr__.__func__ is not enum.Enum.__repr__: # type: ignore[attr-defined] 

475 return repr(obj) 

476 return f'{obj.__class__.__name__}.{obj.name}' 

477 elif isinstance(obj, tuple): 

478 if id(obj) in seen: 

479 return 'tuple(...)' 

480 seen |= frozenset([id(obj)]) 

481 return '({}{})'.format( 

482 ', '.join(object_description(x, _seen=seen) for x in obj), 

483 ',' * (len(obj) == 1), 

484 ) 

485 elif isinstance(obj, list): 

486 if id(obj) in seen: 

487 return 'list(...)' 

488 seen |= {id(obj)} 

489 return '[%s]' % ', '.join(object_description(x, _seen=seen) for x in obj) 

490 

491 try: 

492 s = repr(obj) 

493 except Exception as exc: 

494 raise ValueError from exc 

495 # Strip non-deterministic memory addresses such as 

496 # ``<__main__.A at 0x7f68cb685710>`` 

497 s = memory_address_re.sub('', s) 

498 return s.replace('\n', ' ') 

499 

500 

501def is_builtin_class_method(obj: Any, attr_name: str) -> bool: 

502 """Check whether *attr_name* is implemented on a builtin class. 

503 

504 >>> is_builtin_class_method(int, '__init__') 

505 True 

506 

507 

508 This function is needed since CPython implements ``int.__init__`` via 

509 descriptors, but PyPy implementation is written in pure Python code. 

510 """ 

511 mro = getmro(obj) 

512 

513 try: 

514 cls = next(c for c in mro if attr_name in safe_getattr(c, '__dict__', {})) 

515 except StopIteration: 

516 return False 

517 

518 try: 

519 name = safe_getattr(cls, '__name__') 

520 except AttributeError: 

521 return False 

522 

523 return getattr(builtins, name, None) is cls 

524 

525 

526class DefaultValue: 

527 """A simple wrapper for default value of the parameters of overload functions.""" 

528 

529 def __init__(self, value: str) -> None: 

530 self.value = value 

531 

532 def __eq__(self, other: object) -> bool: 

533 return self.value == other 

534 

535 def __repr__(self) -> str: 

536 return self.value 

537 

538 

539class TypeAliasForwardRef: 

540 """Pseudo typing class for :confval:`autodoc_type_aliases`. 

541 

542 This avoids the error on evaluating the type inside :func:`typing.get_type_hints()`. 

543 """ 

544 

545 def __init__(self, name: str) -> None: 

546 self.name = name 

547 

548 def __call__(self) -> None: 

549 # Dummy method to imitate special typing classes 

550 pass 

551 

552 def __eq__(self, other: Any) -> bool: 

553 return self.name == other 

554 

555 def __hash__(self) -> int: 

556 return hash(self.name) 

557 

558 def __repr__(self) -> str: 

559 return self.name 

560 

561 

562class TypeAliasModule: 

563 """Pseudo module class for :confval:`autodoc_type_aliases`.""" 

564 

565 def __init__(self, modname: str, mapping: Mapping[str, str]) -> None: 

566 self.__modname = modname 

567 self.__mapping = mapping 

568 

569 self.__module: ModuleType | None = None 

570 

571 def __getattr__(self, name: str) -> Any: 

572 fullname = '.'.join(filter(None, [self.__modname, name])) 

573 if fullname in self.__mapping: 

574 # exactly matched 

575 return TypeAliasForwardRef(self.__mapping[fullname]) 

576 else: 

577 prefix = fullname + '.' 

578 nested = {k: v for k, v in self.__mapping.items() if k.startswith(prefix)} 

579 if nested: 

580 # sub modules or classes found 

581 return TypeAliasModule(fullname, nested) 

582 else: 

583 # no sub modules or classes found. 

584 try: 

585 # return the real submodule if exists 

586 return import_module(fullname) 

587 except ImportError: 

588 # return the real class 

589 if self.__module is None: 

590 self.__module = import_module(self.__modname) 

591 

592 return getattr(self.__module, name) 

593 

594 

595class TypeAliasNamespace(dict[str, Any]): 

596 """Pseudo namespace class for :confval:`autodoc_type_aliases`. 

597 

598 Useful for looking up nested objects via ``namespace.foo.bar.Class``. 

599 """ 

600 

601 def __init__(self, mapping: Mapping[str, str]) -> None: 

602 super().__init__() 

603 self.__mapping = mapping 

604 

605 def __getitem__(self, key: str) -> Any: 

606 if key in self.__mapping: 

607 # exactly matched 

608 return TypeAliasForwardRef(self.__mapping[key]) 

609 else: 

610 prefix = key + '.' 

611 nested = {k: v for k, v in self.__mapping.items() if k.startswith(prefix)} 

612 if nested: 

613 # sub modules or classes found 

614 return TypeAliasModule(key, nested) 

615 else: 

616 raise KeyError 

617 

618 

619def _should_unwrap(subject: _SignatureType) -> bool: 

620 """Check the function should be unwrapped on getting signature.""" 

621 __globals__ = getglobals(subject) 

622 # contextmanger should be unwrapped 

623 return ( 

624 __globals__.get('__name__') == 'contextlib' 

625 and __globals__.get('__file__') == contextlib.__file__ 

626 ) 

627 

628 

629def signature( 

630 subject: _SignatureType, 

631 bound_method: bool = False, 

632 type_aliases: Mapping[str, str] | None = None, 

633) -> Signature: 

634 """Return a Signature object for the given *subject*. 

635 

636 :param bound_method: Specify *subject* is a bound method or not 

637 """ 

638 if type_aliases is None: 

639 type_aliases = {} 

640 

641 try: 

642 if _should_unwrap(subject): 

643 signature = inspect.signature(subject) # type: ignore[arg-type] 

644 else: 

645 signature = inspect.signature(subject, follow_wrapped=True) # type: ignore[arg-type] 

646 except ValueError: 

647 # follow built-in wrappers up (ex. functools.lru_cache) 

648 signature = inspect.signature(subject) # type: ignore[arg-type] 

649 parameters = list(signature.parameters.values()) 

650 return_annotation = signature.return_annotation 

651 

652 try: 

653 # Resolve annotations using ``get_type_hints()`` and type_aliases. 

654 localns = TypeAliasNamespace(type_aliases) 

655 annotations = typing.get_type_hints(subject, None, localns, include_extras=True) 

656 for i, param in enumerate(parameters): 

657 if param.name in annotations: 

658 annotation = annotations[param.name] 

659 if isinstance(annotation, TypeAliasForwardRef): 

660 annotation = annotation.name 

661 parameters[i] = param.replace(annotation=annotation) 

662 if 'return' in annotations: 

663 if isinstance(annotations['return'], TypeAliasForwardRef): 

664 return_annotation = annotations['return'].name 

665 else: 

666 return_annotation = annotations['return'] 

667 except Exception: 

668 # ``get_type_hints()`` does not support some kind of objects like partial, 

669 # ForwardRef and so on. 

670 pass 

671 

672 if bound_method: 

673 if inspect.ismethod(subject): 

674 # ``inspect.signature()`` considers the subject is a bound method and removes 

675 # first argument from signature. Therefore no skips are needed here. 

676 pass 

677 else: 

678 if len(parameters) > 0: 

679 parameters.pop(0) 

680 

681 # To allow to create signature object correctly for pure python functions, 

682 # pass an internal parameter __validate_parameters__=False to Signature 

683 # 

684 # For example, this helps a function having a default value `inspect._empty`. 

685 # refs: https://github.com/sphinx-doc/sphinx/issues/7935 

686 return Signature( 

687 parameters, return_annotation=return_annotation, __validate_parameters__=False 

688 ) 

689 

690 

691def evaluate_signature( 

692 sig: Signature, 

693 globalns: dict[str, Any] | None = None, 

694 localns: dict[str, Any] | None = None, 

695) -> Signature: 

696 """Evaluate unresolved type annotations in a signature object.""" 

697 if globalns is None: 

698 globalns = {} 

699 if localns is None: 

700 localns = globalns 

701 

702 parameters = list(sig.parameters.values()) 

703 for i, param in enumerate(parameters): 

704 if param.annotation: 

705 annotation = _evaluate(param.annotation, globalns, localns) 

706 parameters[i] = param.replace(annotation=annotation) 

707 

708 return_annotation = sig.return_annotation 

709 if return_annotation: 

710 return_annotation = _evaluate(return_annotation, globalns, localns) 

711 

712 return sig.replace(parameters=parameters, return_annotation=return_annotation) 

713 

714 

715def _evaluate_forwardref( 

716 ref: ForwardRef, 

717 globalns: dict[str, Any] | None, 

718 localns: dict[str, Any] | None, 

719) -> Any: 

720 """Evaluate a forward reference.""" 

721 if sys.version_info >= (3, 12, 4): 

722 # ``type_params`` were added in 3.13 and the signature of _evaluate() 

723 # is not backward-compatible (it was backported to 3.12.4, so anything 

724 # before 3.12.4 still has the old signature). 

725 # 

726 # See: https://github.com/python/cpython/pull/118104. 

727 return ref._evaluate(globalns, localns, {}, recursive_guard=frozenset()) # type: ignore[arg-type, misc] 

728 return ref._evaluate(globalns, localns, frozenset()) 

729 

730 

731def _evaluate( 

732 annotation: Any, 

733 globalns: dict[str, Any], 

734 localns: dict[str, Any], 

735) -> Any: 

736 """Evaluate unresolved type annotation.""" 

737 try: 

738 if isinstance(annotation, str): 

739 ref = ForwardRef(annotation, True) 

740 annotation = _evaluate_forwardref(ref, globalns, localns) 

741 

742 if isinstance(annotation, ForwardRef): 

743 annotation = _evaluate_forwardref(ref, globalns, localns) 

744 elif isinstance(annotation, str): 

745 # might be a ForwardRef'ed annotation in overloaded functions 

746 ref = ForwardRef(annotation, True) 

747 annotation = _evaluate_forwardref(ref, globalns, localns) 

748 except (NameError, TypeError): 

749 # failed to evaluate type. skipped. 

750 pass 

751 

752 return annotation 

753 

754 

755def stringify_signature( 

756 sig: Signature, 

757 show_annotation: bool = True, 

758 show_return_annotation: bool = True, 

759 unqualified_typehints: bool = False, 

760) -> str: 

761 """Stringify a :class:`~inspect.Signature` object. 

762 

763 :param show_annotation: If enabled, show annotations on the signature 

764 :param show_return_annotation: If enabled, show annotation of the return value 

765 :param unqualified_typehints: If enabled, show annotations as unqualified 

766 (ex. io.StringIO -> StringIO) 

767 """ 

768 if unqualified_typehints: 

769 mode = 'smart' 

770 else: 

771 mode = 'fully-qualified' 

772 

773 EMPTY = Parameter.empty 

774 

775 args = [] 

776 last_kind = None 

777 for param in sig.parameters.values(): 

778 if param.kind != Parameter.POSITIONAL_ONLY and last_kind == Parameter.POSITIONAL_ONLY: 

779 # PEP-570: Separator for Positional Only Parameter: / 

780 args.append('/') 

781 if param.kind == Parameter.KEYWORD_ONLY and last_kind in ( 

782 Parameter.POSITIONAL_OR_KEYWORD, 

783 Parameter.POSITIONAL_ONLY, 

784 None, 

785 ): 

786 # PEP-3102: Separator for Keyword Only Parameter: * 

787 args.append('*') 

788 

789 arg = StringIO() 

790 if param.kind is Parameter.VAR_POSITIONAL: 

791 arg.write('*' + param.name) 

792 elif param.kind is Parameter.VAR_KEYWORD: 

793 arg.write('**' + param.name) 

794 else: 

795 arg.write(param.name) 

796 

797 if show_annotation and param.annotation is not EMPTY: 

798 arg.write(': ') 

799 arg.write(stringify_annotation(param.annotation, mode)) # type: ignore[arg-type] 

800 if param.default is not EMPTY: 

801 if show_annotation and param.annotation is not EMPTY: 

802 arg.write(' = ') 

803 else: 

804 arg.write('=') 

805 arg.write(object_description(param.default)) 

806 

807 args.append(arg.getvalue()) 

808 last_kind = param.kind 

809 

810 if last_kind is Parameter.POSITIONAL_ONLY: 

811 # PEP-570: Separator for Positional Only Parameter: / 

812 args.append('/') 

813 

814 concatenated_args = ', '.join(args) 

815 if sig.return_annotation is EMPTY or not show_annotation or not show_return_annotation: 

816 return f'({concatenated_args})' 

817 else: 

818 retann = stringify_annotation(sig.return_annotation, mode) # type: ignore[arg-type] 

819 return f'({concatenated_args}) -> {retann}' 

820 

821 

822def signature_from_str(signature: str) -> Signature: 

823 """Create a :class:`~inspect.Signature` object from a string.""" 

824 code = 'def func' + signature + ': pass' 

825 module = ast.parse(code) 

826 function = typing.cast(ast.FunctionDef, module.body[0]) 

827 

828 return signature_from_ast(function, code) 

829 

830 

831def signature_from_ast(node: ast.FunctionDef, code: str = '') -> Signature: 

832 """Create a :class:`~inspect.Signature` object from an AST node.""" 

833 EMPTY = Parameter.empty 

834 

835 args: ast.arguments = node.args 

836 defaults: tuple[ast.expr | None, ...] = tuple(args.defaults) 

837 pos_only_offset = len(args.posonlyargs) 

838 defaults_offset = pos_only_offset + len(args.args) - len(defaults) 

839 # The sequence ``D = args.defaults`` contains non-None AST expressions, 

840 # so we can use ``None`` as a sentinel value for that to indicate that 

841 # there is no default value for a specific parameter. 

842 # 

843 # Let *p* be the number of positional-only and positional-or-keyword 

844 # arguments. Note that ``0 <= len(D) <= p`` and ``D[0]`` is the default 

845 # value corresponding to a positional-only *or* a positional-or-keyword 

846 # argument. Since a non-default argument cannot follow a default argument, 

847 # the sequence *D* can be completed on the left by adding None sentinels 

848 # so that ``len(D) == p`` and ``D[i]`` is the *i*-th default argument. 

849 defaults = (None,) * defaults_offset + defaults 

850 

851 # construct the parameter list 

852 params: list[Parameter] = [] 

853 

854 # positional-only arguments (introduced in Python 3.8) 

855 for arg, defexpr in zip(args.posonlyargs, defaults): 

856 params.append(_define(Parameter.POSITIONAL_ONLY, arg, code, defexpr=defexpr)) 

857 

858 # normal arguments 

859 for arg, defexpr in zip(args.args, defaults[pos_only_offset:]): 

860 params.append(_define(Parameter.POSITIONAL_OR_KEYWORD, arg, code, defexpr=defexpr)) 

861 

862 # variadic positional argument (no possible default expression) 

863 if args.vararg: 

864 params.append(_define(Parameter.VAR_POSITIONAL, args.vararg, code, defexpr=None)) 

865 

866 # keyword-only arguments 

867 for arg, defexpr in zip(args.kwonlyargs, args.kw_defaults): 

868 params.append(_define(Parameter.KEYWORD_ONLY, arg, code, defexpr=defexpr)) 

869 

870 # variadic keyword argument (no possible default expression) 

871 if args.kwarg: 

872 params.append(_define(Parameter.VAR_KEYWORD, args.kwarg, code, defexpr=None)) 

873 

874 return_annotation = ast_unparse(node.returns, code) or EMPTY 

875 return Signature(params, return_annotation=return_annotation) 

876 

877 

878def _define( 

879 kind: _ParameterKind, 

880 arg: ast.arg, 

881 code: str, 

882 *, 

883 defexpr: ast.expr | None, 

884) -> Parameter: 

885 EMPTY = Parameter.empty 

886 

887 default = EMPTY if defexpr is None else DefaultValue(ast_unparse(defexpr, code)) 

888 annotation = ast_unparse(arg.annotation, code) or EMPTY 

889 return Parameter(arg.arg, kind, default=default, annotation=annotation) 

890 

891 

892def getdoc( 

893 obj: Any, 

894 attrgetter: Callable = safe_getattr, 

895 allow_inherited: bool = False, 

896 cls: Any = None, 

897 name: str | None = None, 

898) -> str | None: 

899 """Get the docstring for the object. 

900 

901 This tries to obtain the docstring for some kind of objects additionally: 

902 

903 * partial functions 

904 * inherited docstring 

905 * inherited decorated methods 

906 """ 

907 if cls and name and isclassmethod(obj, cls, name): 

908 for basecls in getmro(cls): 

909 meth = basecls.__dict__.get(name) 

910 if meth and hasattr(meth, '__func__'): 

911 doc: str | None = getdoc(meth.__func__) 

912 if doc is not None or not allow_inherited: 

913 return doc 

914 

915 doc = _getdoc_internal(obj) 

916 if ispartial(obj) and doc == obj.__class__.__doc__: 

917 return getdoc(obj.func) 

918 elif doc is None and allow_inherited: 

919 if cls and name: 

920 # Check a docstring of the attribute or method from super classes. 

921 for basecls in getmro(cls): 

922 meth = safe_getattr(basecls, name, None) 

923 if meth is not None: 

924 doc = _getdoc_internal(meth) 

925 if doc is not None: 

926 break 

927 

928 if doc is None: 

929 # retry using `inspect.getdoc()` 

930 for basecls in getmro(cls): 

931 meth = safe_getattr(basecls, name, None) 

932 if meth is not None: 

933 doc = inspect.getdoc(meth) 

934 if doc is not None: 

935 break 

936 

937 if doc is None: 

938 doc = inspect.getdoc(obj) 

939 

940 return doc 

941 

942 

943def _getdoc_internal( 

944 obj: Any, attrgetter: Callable[[Any, str, Any], Any] = safe_getattr 

945) -> str | None: 

946 doc = attrgetter(obj, '__doc__', None) 

947 if isinstance(doc, str): 

948 return doc 

949 return None