Skip to content

Commit

Permalink
autospec for spy: remove if else which was in place for python < 3.7 (#…
Browse files Browse the repository at this point in the history
…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).
  • Loading branch information
yesthesoup committed Dec 6, 2023
1 parent 5922e0c commit 7f9c131
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 22 deletions.
10 changes: 2 additions & 8 deletions src/pytest_mock/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,6 @@ def spy(self, obj: object, name: str) -> MockType:
:return: Spy object.
"""
method = getattr(obj, name)
if inspect.isclass(obj) and isinstance(
inspect.getattr_static(obj, name), (classmethod, staticmethod)
):
# Can't use autospec classmethod or staticmethod objects before 3.7
# see: https://bugs.python.org/issue23078
autospec = False
else:
autospec = inspect.ismethod(method) or inspect.isfunction(method)

def wrapper(*args, **kwargs):
spy_obj.spy_return = None
Expand Down Expand Up @@ -175,6 +167,8 @@ async def async_wrapper(*args, **kwargs):
else:
wrapped = functools.update_wrapper(wrapper, method)

autospec = inspect.ismethod(method) or inspect.isfunction(method)

spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec)
spy_obj.spy_return = None
spy_obj.spy_exception = None
Expand Down
37 changes: 23 additions & 14 deletions tests/test_pytest_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,17 +314,37 @@ def bar(self, arg):
assert str(spy.spy_exception) == f"Error with {v}"


def test_instance_method_spy_autospec_true(mocker: MockerFixture) -> None:
def test_instance_class_static_method_spy_autospec_true(mocker: MockerFixture) -> None:
class Foo:
def bar(self, arg):
return arg * 2

@classmethod
def baz(cls, arg):
return arg * 2

@staticmethod
def qux(arg):
return arg * 2

foo = Foo()
spy = mocker.spy(foo, "bar")
instance_method_spy = mocker.spy(foo, "bar")
with pytest.raises(
AttributeError, match="'function' object has no attribute 'fake_assert_method'"
):
spy.fake_assert_method(arg=5)
instance_method_spy.fake_assert_method(arg=5)

class_method_spy = mocker.spy(Foo, "baz")
with pytest.raises(
AttributeError, match="Mock object has no attribute 'fake_assert_method'"
):
class_method_spy.fake_assert_method(arg=5)

static_method_spy = mocker.spy(Foo, "qux")
with pytest.raises(
AttributeError, match="Mock object has no attribute 'fake_assert_method'"
):
static_method_spy.fake_assert_method(arg=5)


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


@skip_pypy
def test_class_method_spy_autospec_false(mocker: MockerFixture) -> None:
class Foo:
@classmethod
def bar(cls, arg):
return arg * 2

spy = mocker.spy(Foo, "bar")
spy.fake_assert_method()


@skip_pypy
def test_class_method_subclass_spy(mocker: MockerFixture) -> None:
class Base:
Expand Down

0 comments on commit 7f9c131

Please sign in to comment.