Skip to content

mocker.spy does not work on non-function objects in Python 2 #157

@pbasista

Description

@pbasista

With pytest-mock version 1.11.0, the non-function objects cannot be spied on using mocker.spy in Python 2. For example:

uut.py:

class A(object):
    def __call__(self):
        pass


a = A()


def modify_a():
    a()

test_uut.py:

import uut
    

def test_modify_a(mocker):
    mocker.spy(uut, "a")
    uut.modify_a()
    uut.a.call_count == 1

In Python 2, the test fails with the following exception:

        for attr in assigned:
>           setattr(wrapper, attr, getattr(wrapped, attr))
E           AttributeError: 'A' object has no attribute '__name__'

/usr/lib/python2.7/functools.py:33: AttributeError

The same test works in Python 3. The reason why the behavior is different between Python versions is the different implementation of functools.wraps in Python 2 and Python 3. In Python 3, the attributes which are supposed to be set on the wrapper object but are missing on the original object are simply skipped:

https://github.com/python/cpython/blob/3.7/Lib/functools.py#L52-L58

However, in Python 2, all the attributes which are supposed to be set on the wrapper object must also be present on the original object:

https://github.com/python/cpython/blob/2.7/Lib/functools.py#L32-L33

This prevents the standard call to functools.wraps without extra parameters in Python 2 from being able to wrap the non-function objects that do not have attributes like __name__, which are by default expected to be present on the original (wrapped) object.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions