Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signal connected to unittest.Mock and MagicMock does not work in Python 3.8 #106

Closed
Homeopatch opened this issue Jul 13, 2023 · 1 comment

Comments

@Homeopatch
Copy link

After upgrading Blinker from 1.5 to 1.6.2, tests in my project started to fail.

In short, I found this issue and also suggesting a workaround. Whole test case for reproducing this issue is here.


My test cases are checking if a signal was called. I use MagicMock to check if and how signals are called. I also used spec parameter of MagicMock, because plain initialization did not work for some other compatibility issues, you'll see it below

Here is an equivalent of what I have in my tests:

def foo(sender, **kwargs):
    """Stub function for spec parameter"""
    pass

# Test case code
mock = MagicMock(spec=foo)
self.signal.connect(mock, sender=self)
self.signal.send(self) # RuntimeError: Cannot send to a coroutine function here
# see this for details: https://bugs.python.org/issue37251
mock.assert_called_once_with(self)
Stack trace
Error
Traceback (most recent call last):
  File "/home/denis/devel/python_scripts/test_blinker.py", line 24, in test_magicmock_spec
    self.signal.send(self)  # RuntimeError: Cannot send to a coroutine function here
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/base.py", line 298, in send
    raise RuntimeError("Cannot send to a coroutine function")
RuntimeError: Cannot send to a coroutine function

So Blinker received async support after the upgrade. The check
asyncio.iscoroutinefunction(func) has been added to send(), where func is my MagicMock. This is indeed a problem
of Python, rather than Blinker, I found this ticket here: https://bugs.python.org/issue37251

So I've tried to get rid of spec

But receive another error, at connect()

def test_magicmock(self):
    mock = MagicMock()
    self.signal.connect(mock, sender=self)  # AttributeError: __self__
    self.signal.send(self)
    mock.assert_called_once_with(self)
Stack trace
Error
Traceback (most recent call last):
  File "/home/denis/devel/python_scripts/test_blinker.py", line 30, in test_magicmock
    self.signal.connect(mock, sender=self)  # AttributeError: __self__
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/base.py", line 131, in connect
    receiver_ref = reference(receiver, self._cleanup_receiver)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_utilities.py", line 80, in reference
    weak = callable_reference(object, callback)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_utilities.py", line 91, in callable_reference
    return BoundMethodWeakref(target=object, on_delete=callback)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_saferef.py", line 124, in __new__
    key = cls.calculate_key(target)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_saferef.py", line 190, in calculate_key
    return (id(get_self(target)), id(get_func(target)))
  File "/usr/lib/python3.8/unittest/mock.py", line 639, in __getattr__
    raise AttributeError(name)
AttributeError: __self__

Plain MagicMock does not work. The same for Mock.

Workaround

Have a callable class and pass it to spec parameter:

class FooStub:
    """Stub class for spec"""
    def __call__(self, sender, **kwargs):
        pass

    ...
def test_magicmock_stub(self):
    # This test passes
    mock = MagicMock(spec=FooStub)
    self.signal.connect(mock, sender=self)
    self.signal.send(self)
    mock.assert_called_once_with(self)

Also works for Mock.

See the code here

@davidism
Copy link
Member

Closing as this seems like something Python needs to fix.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 11, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

2 participants