Skip to content

Commit 7f9c131

Browse files
authored
autospec for spy: remove if else which was in place for python < 3.7 (#399)
The if/else for the spy feature was there to get around a bug in python that was fixed in 3.7. Now that we only support >= 3.8 this check is no longer needed. This should not really affect users, but if we learn it does, we can add a CHANGELOG later (as discussed in #399).
1 parent 5922e0c commit 7f9c131

File tree

2 files changed

+25
-22
lines changed

2 files changed

+25
-22
lines changed

src/pytest_mock/plugin.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,6 @@ def spy(self, obj: object, name: str) -> MockType:
137137
:return: Spy object.
138138
"""
139139
method = getattr(obj, name)
140-
if inspect.isclass(obj) and isinstance(
141-
inspect.getattr_static(obj, name), (classmethod, staticmethod)
142-
):
143-
# Can't use autospec classmethod or staticmethod objects before 3.7
144-
# see: https://bugs.python.org/issue23078
145-
autospec = False
146-
else:
147-
autospec = inspect.ismethod(method) or inspect.isfunction(method)
148140

149141
def wrapper(*args, **kwargs):
150142
spy_obj.spy_return = None
@@ -175,6 +167,8 @@ async def async_wrapper(*args, **kwargs):
175167
else:
176168
wrapped = functools.update_wrapper(wrapper, method)
177169

170+
autospec = inspect.ismethod(method) or inspect.isfunction(method)
171+
178172
spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec)
179173
spy_obj.spy_return = None
180174
spy_obj.spy_exception = None

tests/test_pytest_mock.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -314,17 +314,37 @@ def bar(self, arg):
314314
assert str(spy.spy_exception) == f"Error with {v}"
315315

316316

317-
def test_instance_method_spy_autospec_true(mocker: MockerFixture) -> None:
317+
def test_instance_class_static_method_spy_autospec_true(mocker: MockerFixture) -> None:
318318
class Foo:
319319
def bar(self, arg):
320320
return arg * 2
321321

322+
@classmethod
323+
def baz(cls, arg):
324+
return arg * 2
325+
326+
@staticmethod
327+
def qux(arg):
328+
return arg * 2
329+
322330
foo = Foo()
323-
spy = mocker.spy(foo, "bar")
331+
instance_method_spy = mocker.spy(foo, "bar")
324332
with pytest.raises(
325333
AttributeError, match="'function' object has no attribute 'fake_assert_method'"
326334
):
327-
spy.fake_assert_method(arg=5)
335+
instance_method_spy.fake_assert_method(arg=5)
336+
337+
class_method_spy = mocker.spy(Foo, "baz")
338+
with pytest.raises(
339+
AttributeError, match="Mock object has no attribute 'fake_assert_method'"
340+
):
341+
class_method_spy.fake_assert_method(arg=5)
342+
343+
static_method_spy = mocker.spy(Foo, "qux")
344+
with pytest.raises(
345+
AttributeError, match="Mock object has no attribute 'fake_assert_method'"
346+
):
347+
static_method_spy.fake_assert_method(arg=5)
328348

329349

330350
def test_spy_reset(mocker: MockerFixture) -> None:
@@ -404,17 +424,6 @@ def bar(cls, arg):
404424
assert spy.spy_return == 20
405425

406426

407-
@skip_pypy
408-
def test_class_method_spy_autospec_false(mocker: MockerFixture) -> None:
409-
class Foo:
410-
@classmethod
411-
def bar(cls, arg):
412-
return arg * 2
413-
414-
spy = mocker.spy(Foo, "bar")
415-
spy.fake_assert_method()
416-
417-
418427
@skip_pypy
419428
def test_class_method_subclass_spy(mocker: MockerFixture) -> None:
420429
class Base:

0 commit comments

Comments
 (0)