|
19 | 19 | import typing |
20 | 20 | from contextlib import contextmanager |
21 | 21 | from copy import copy |
22 | | -from functools import lru_cache, reduce, partial, wraps |
| 22 | +from functools import cached_property, lru_cache, reduce, partial, wraps |
23 | 23 | from itertools import tee, groupby |
24 | | -from types import ModuleType |
| 24 | +from types import FunctionType, ModuleType |
25 | 25 | from typing import ( # noqa: F401 |
26 | 26 | cast, Any, Callable, Dict, Generator, Iterable, List, Literal, Mapping, NewType, |
27 | 27 | Optional, Set, Tuple, Type, TypeVar, Union, |
@@ -421,11 +421,21 @@ def _is_descriptor(obj): |
421 | 421 | inspect.ismemberdescriptor(obj)) |
422 | 422 |
|
423 | 423 |
|
424 | | -def _unwrap_descriptor(obj): |
| 424 | +def _unwrap_descriptor(dobj): |
| 425 | + obj = dobj.obj |
425 | 426 | if isinstance(obj, property): |
426 | 427 | return (getattr(obj, 'fget', False) or |
427 | 428 | getattr(obj, 'fset', False) or |
428 | 429 | getattr(obj, 'fdel', obj)) |
| 430 | + if isinstance(obj, cached_property): |
| 431 | + return obj.func |
| 432 | + if isinstance(obj, FunctionType): |
| 433 | + return obj |
| 434 | + if inspect.ismemberdescriptor(obj) or obj.__class__.__name__ == '_tuplegetter': |
| 435 | + class_name = dobj.qualname.rsplit('.', 1)[0] |
| 436 | + obj = getattr(dobj.module.obj, class_name) |
| 437 | + return obj |
| 438 | + # XXX: Follow descriptor protocol? Already proved buggy in conditions above |
429 | 439 | return getattr(obj, '__get__', obj) |
430 | 440 |
|
431 | 441 |
|
@@ -550,7 +560,7 @@ def source(self) -> str: |
550 | 560 | available, an empty string. |
551 | 561 | """ |
552 | 562 | try: |
553 | | - lines, _ = inspect.getsourcelines(_unwrap_descriptor(self.obj)) |
| 563 | + lines, _ = inspect.getsourcelines(_unwrap_descriptor(self)) |
554 | 564 | except (ValueError, TypeError, OSError): |
555 | 565 | return '' |
556 | 566 | return inspect.cleandoc(''.join(['\n'] + lines)) |
@@ -1402,7 +1412,7 @@ def return_annotation(self, *, link=None) -> str: |
1402 | 1412 | # global variables |
1403 | 1413 | lambda: _get_type_hints(not self.cls and self.module.obj)[self.name], |
1404 | 1414 | # properties |
1405 | | - lambda: inspect.signature(_unwrap_descriptor(self.obj)).return_annotation, |
| 1415 | + lambda: inspect.signature(_unwrap_descriptor(self)).return_annotation, |
1406 | 1416 | # Use raw annotation strings in unmatched forward declarations |
1407 | 1417 | lambda: cast(Class, self.cls).obj.__annotations__[self.name], |
1408 | 1418 | # Extract annotation from the docstring for C builtin function |
|
0 commit comments