@@ -23,6 +23,19 @@ class SpyConfig(NamedTuple):
23
23
is_async : bool = False
24
24
25
25
26
+ def _get_type_hints (obj : Any ) -> Dict [str , Any ]:
27
+ """Get type hints for an object, if possible.
28
+
29
+ The builtin `typing.get_type_hints` may fail at runtime,
30
+ e.g. if a type is subscriptable according to mypy but not
31
+ according to Python.
32
+ """
33
+ try :
34
+ return get_type_hints (obj )
35
+ except Exception :
36
+ return {}
37
+
38
+
26
39
class BaseSpy :
27
40
"""Spy object base class.
28
41
@@ -88,23 +101,15 @@ def __getattr__(self, name: str) -> Any:
88
101
89
102
if isclass (self ._spec ):
90
103
try :
91
- # NOTE: `get_type_hints` may fail at runtime,
92
- # e.g. if a type is subscriptable according to mypy but not
93
- # according to Python, `get_type_hints` will raise.
94
- # Rather than fail to create a spy with an inscrutable error,
95
- # gracefully fallback to a specification-less spy.
96
- hints = get_type_hints (self ._spec )
97
- child_spec = getattr (
98
- self ._spec ,
99
- name ,
100
- hints .get (name ),
101
- )
104
+ child_hint = _get_type_hints (self ._spec ).get (name )
102
105
except Exception :
103
- pass
106
+ child_hint = None
107
+
108
+ child_spec = getattr (self ._spec , name , child_hint )
104
109
105
110
if isinstance (child_spec , property ):
106
- hints = get_type_hints (child_spec .fget )
107
- child_spec = hints . get ( "return" )
111
+ child_spec = _get_type_hints (child_spec .fget ). get ( "return" )
112
+
108
113
elif isclass (self ._spec ) and isfunction (child_spec ):
109
114
# `iscoroutinefunction` does not work for `partial` on Python < 3.8
110
115
# check before we wrap it
0 commit comments