Skip to content

Commit 6bd7f40

Browse files
authored
fix(spy): allow spy creation when type hints cannot be resolved (#10)
Fixes #8
1 parent a2f1724 commit 6bd7f40

File tree

2 files changed

+35
-6
lines changed

2 files changed

+35
-6
lines changed

decoy/spy.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,20 @@ def __getattr__(self, name: str) -> Any:
7777
child_spec = None
7878

7979
if isclass(self._spec):
80-
hints = get_type_hints(self._spec) # type: ignore[arg-type]
81-
child_spec = getattr(
82-
self._spec,
83-
name,
84-
hints.get(name),
85-
)
80+
try:
81+
# NOTE(mc, 2021-01-05): `get_type_hints` may fail at runtime,
82+
# e.g. if a type is subscriptable according to mypy but not
83+
# according to Python, `get_type_hints` will raise.
84+
# Rather than fail to create a spy with an inscrutable error,
85+
# gracefully fallback to a specification-less spy.
86+
hints = get_type_hints(self._spec) # type: ignore[arg-type]
87+
child_spec = getattr(
88+
self._spec,
89+
name,
90+
hints.get(name),
91+
)
92+
except Exception:
93+
pass
8694

8795
if isinstance(child_spec, property):
8896
hints = get_type_hints(child_spec.fget)

tests/test_spy.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,27 @@ class _SomeClass:
248248
]
249249

250250

251+
@pytest.mark.filterwarnings("ignore:'NoneType' object is not subscriptable")
252+
async def test_create_nested_spy_using_non_runtime_type_hints() -> None:
253+
"""It should gracefully degrade if type hints cannot be resolved."""
254+
255+
class _SomeClass:
256+
_property: "None[str]"
257+
258+
calls = []
259+
spy = create_spy(spec=_SomeClass, handle_call=lambda c: calls.append(c))
260+
spy._property.do_something(7, eight=8, nine=9)
261+
262+
assert calls == [
263+
SpyCall(
264+
spy_id=id(spy._property.do_something),
265+
spy_name="_SomeClass._property.do_something",
266+
args=(7,),
267+
kwargs={"eight": 8, "nine": 9},
268+
),
269+
]
270+
271+
251272
async def test_spy_returns_handler_value() -> None:
252273
"""The spy should return the value from its call handler when called."""
253274
call_count = 0

0 commit comments

Comments
 (0)