Mutable Call Arguments in Python

[programming]

@2021-08-25,*2021-12-05

I spent a bit more time on this issue than I'd like to admit, but here's what was going on and what the issue was...
I wanted to test that the following class correctly split up the records, and called the
process
function in proper batches
class EventHandler:
    def __init__(self, processor, process_after):
        self.processor = processor
        self.process_after = process_after

    def handle(self, records):
        seen = 0
        batch = []
        for record in records:
            if seen == self.process_after:
                self.processor.process(batch)
                seen = 0
                batch.clear()
            seen = seen + 1
            batch.append(record)
        if batch:
            self.processor.process(batch)
            seen = 0
            batch.clear()
I ended up with an initial test that looked like this:
from unittest import mock

from src import handler


def test_handler():
    mock_processor = mock.Mock()
    h = handler.EventHandler(processor=mock_processor, process_after=2)

    h.handle(["1", "2", "3", "4"])

    assert mock_processor.process.call_count == 2

That worked just fine, but then, I wanted to assert that the specific calls to
process
contained the correct arguments. So I added the following assertions:
    assert mock_processor.process.call_args_list[0][0] == (["1", "2"],)
    assert mock_processor.process.call_args_list[1][0] == (["3", "4"],)
In theory this should have worked just fine, but pytest reported that the calls were actually empty...
    def test_handler():
        mock_processor = mock.Mock()
        h = handler.EventHandler(processor=mock_processor, process_after=2)

        h.handle(["1", "2", "3", "4"])

        assert mock_processor.process.call_count == 2

>       assert mock_processor.process.call_args_list[0][0] == (["1", "2"],)
E       AssertionError: assert ([],) == (['1', '2'],)
E         At index 0 diff: [] != ['1', '2']
E         Use -v to get the full diff
This is where I scratched my head, I even added print statements just before I called
process
and it printed the correct call args. After a few hours of adding breakpoints and stepping through the code, I realized the issue had to do with mutable lists. Particularly the call to
batch.clear()

I replaced the call to
batch.clear()
with
batch = []
and once I did that pytest reported success.
    platform darwin -- Python 3.8.6, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /Users/beeceej/Code/beeceej/mutable_call_args
collected 1 item

tests/handler_test.py .                                                                                                                                                                                          [100%]

== 1 passed in 0.05s ==
Note to self,
Mock#call_args_list
are mutable, so if your application code mutates params, you won't be able make assertions against them.
...Thanks Python...