Skip to content

Commit 8d86195

Browse files
authored
fix(spy): follow __wrapped__ when getting specs and signatures (#134)
Fixes #133
1 parent 29a3b08 commit 8d86195

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

decoy/spec.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def get_signature(self) -> Optional[inspect.Signature]:
7474
source = self._get_source()
7575

7676
try:
77-
return inspect.signature(source)
77+
return inspect.signature(source, follow_wrapped=True)
7878
except (ValueError, TypeError):
7979
return None
8080

@@ -130,10 +130,13 @@ def get_child_spec(self, name: str) -> "Spec":
130130
elif isinstance(child_source, staticmethod):
131131
child_source = child_source.__func__
132132

133-
elif inspect.isfunction(child_source):
134-
# consume the `self` argument of the method to ensure proper
135-
# signature reporting by wrapping it in a partial
136-
child_source = functools.partial(child_source, None)
133+
else:
134+
child_source = inspect.unwrap(child_source)
135+
136+
if inspect.isfunction(child_source):
137+
# consume the `self` argument of the method to ensure proper
138+
# signature reporting by wrapping it in a partial
139+
child_source = functools.partial(child_source, None)
137140

138141
return Spec(source=child_source, name=child_name, module_name=self._module_name)
139142

tests/common.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Common test interfaces."""
2+
from functools import lru_cache
23
from typing import Any
34

45

@@ -27,6 +28,11 @@ def primitive_property(self) -> str:
2728
"""Get a primitive computed property."""
2829
...
2930

31+
@lru_cache(maxsize=None)
32+
def some_wrapped_method(self, val: str) -> str:
33+
"""Get a thing through a wrapped method."""
34+
...
35+
3036

3137
class SomeNestedClass:
3238
"""Nested testing class."""
@@ -75,17 +81,22 @@ async def __call__(self, val: int) -> int:
7581
...
7682

7783

78-
# NOTE: these `Any`s are forward references for call signature testing purposes
7984
def noop(*args: Any, **kwargs: Any) -> Any:
8085
"""No-op."""
81-
pass
86+
...
8287

8388

8489
def some_func(val: str) -> str:
8590
"""Test function."""
86-
return "can't touch this"
91+
...
8792

8893

8994
async def some_async_func(val: str) -> str:
9095
"""Async test function."""
91-
return "can't touch this"
96+
...
97+
98+
99+
@lru_cache(maxsize=None)
100+
def some_wrapped_func(val: str) -> str:
101+
"""Wrapped test function."""
102+
...

tests/test_spec.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
SomeNestedClass,
1414
some_func,
1515
some_async_func,
16+
some_wrapped_func,
1617
)
1718

1819

@@ -184,6 +185,34 @@ class GetSignatureSpec(NamedTuple):
184185
return_annotation=int,
185186
),
186187
),
188+
GetSignatureSpec(
189+
subject=Spec(source=some_wrapped_func, name=None),
190+
expected_signature=inspect.Signature(
191+
parameters=[
192+
inspect.Parameter(
193+
name="val",
194+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
195+
annotation=str,
196+
)
197+
],
198+
return_annotation=str,
199+
),
200+
),
201+
GetSignatureSpec(
202+
subject=Spec(source=SomeClass, name=None).get_child_spec(
203+
"some_wrapped_method"
204+
),
205+
expected_signature=inspect.Signature(
206+
parameters=[
207+
inspect.Parameter(
208+
name="val",
209+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
210+
annotation=str,
211+
)
212+
],
213+
return_annotation=str,
214+
),
215+
),
187216
],
188217
)
189218
def test_get_signature(
@@ -315,6 +344,13 @@ class GetBindArgsSpec(NamedTuple):
315344
expected_args=("hello",),
316345
expected_kwargs={},
317346
),
347+
GetBindArgsSpec(
348+
subject=Spec(source=some_wrapped_func, name=None),
349+
input_args=(),
350+
input_kwargs={"val": "hello"},
351+
expected_args=("hello",),
352+
expected_kwargs={},
353+
),
318354
],
319355
)
320356
def test_bind_args(

0 commit comments

Comments
 (0)