I was in the middle of migrating some code and associated tests from python 2 to python 3 (specifically 2.7 to 3.7) and I came across this weird behavior that I'd like to understand:
try:
from unittest import mock # python 3
except ImportError:
import mock # python 2
import os
import signal
def do_signal(mock_type):
def side_effect(signum, frame):
print('Called for ' + mock_type.__name__)
handler = mock_type(side_effect=side_effect)
signal.signal(signal.SIGTERM, handler)
os.kill(os.getpid(), signal.SIGTERM)
print(handler.call_count)
do_signal(mock.Mock)
do_signal(mock.MagicMock)
Here I use a mock.Mock or mock.MagicMock as a signal handler, and I print out the number of times the mock was called after sending the signal to the current process.
In python 2.7 this prints out:
Called for Mock
1
Called for MagicMock
1
But for python3.7 it prints out:
Called for Mock
1
0
For python 3 the MagicMock doesn't seem to be called at all. Reversing the order of the do_signal calls doesn't change this.
What behavior differences between Mock and MagicMock could explain this, and why would the behavior change from python 2 to 3? I know that mock was added to the standard library in python 3 as unittest.mock, so I'm curious whether something changed there.
in python 3, enumerations were added for signals
for example, you can call:
and the first thing
signal.signalattempts to do is convert the handle to an integer from an enum by callingint(...)since
MagicMocksupports all of the magic methods, it also supports__int__you can see this if you peek at the magic method calls:
since the MagicMock has
__int__:that reassigns the
SIGTERMhandler to signal 1 (SIGHUPon my platform)