Title: Make mock_open return per-file content
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9
Status: closed Resolution: duplicate
Dependencies: Superseder:
Assigned To: Nosy List: Anvil, cjw296, mariocj89, michael.foord, xtreak
Priority: normal Keywords: patch

Created on 2019-07-24 12:46 by Anvil, last changed 2020-01-24 08:28 by cjw296. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 14933 closed Anvil, 2019-07-24 12:52
Messages (8)
msg348386 - (view) Author: Damien Nadé (Anvil) * Date: 2019-07-24 12:46
Let's say I have a function that opens 2 files and compare them. mock_open would not allow me to test this case, as it would return the same data for both files (through its read_data argument).

I want to be able to do this in a mocked-open context:
with open("file1") as file1:
    assert == "data1"

with open("file2") as file2:
    assert == "data2"
msg348575 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2019-07-28 13:25
Can you suggest an API for doing this?
msg348599 - (view) Author: Damien Nadé (Anvil) * Date: 2019-07-29 06:40
I've submitted a patch to mock_open, but if you think it's a bad approach, I'm open to discussion.
msg348653 - (view) Author: Damien Nadé (Anvil) * Date: 2019-07-29 12:09
To summarize the change I'm proposing, I'd say that instead of having mock_open creating closures (functions inside a specific context), mock_open now creates objects that will hold the contexts. Each context would be filename-dedicated and would include specific 'read_data' and linked MagikMock object.

So the change is fully transparent compared to the currently-existing mock_open. It just brings a little extra something, in the way of an extra parameter to mock_open : 

mock_open(mock, read_data, data)

While the name 'data' obviously can (and should) be replaced before merge (I dont have a "good" name in mind ATM, feel free to suggest), I'm suggesting in my PR that it could be a {<file name>: <read data>} mapping.

e.g: If i call

mock_open(read_data="default read data", data={"/foo": "abc", "/asdf/bar": "xyz"})

Then, after mock.patching open, I can assert the following:

assert open("random file").read() == "default read data"
assert open("/foo").read() == "abc"
assert open("/bar").read() == "xyz"

And so far, that's all I'm proposing... But I'm really open to suggestion on that.
msg351182 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-09-05 07:11
Using a side_effect is one way to achieve this currently if I understand the use case correctly. For every open side_effect will be called and hence it will return a new mock_open with read_data for the given filename used inside the context. This can handle nested calls since each will have it's own mock_open object. PR seems to do similar approach to refactor out to created predefined mock_open per filename and to return objects from a dictionary.

from unittest.mock import mock_open, patch

DEFAULT_MOCK_DATA = "default mock data"
data_dict = {"file1": "data1",
             "file2": "data2"}

def open_side_effect(name):
    return mock_open(read_data=data_dict.get(name, DEFAULT_MOCK_DATA))()

with patch(f"{__name__}.open", side_effect=open_side_effect):
    with open("file1") as file1:
        assert == "data1"

        with open("file2") as file2:
            assert == "data2"

            with open("file1") as file3:
                assert == "d"

        assert == ""

    with open("defaultfile") as file4:
        assert == "default mock data"
msg352126 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-09-12 11:29
Given that the enhancement can only land on 3.9 I guess it will be a good recipe on returning mock_open data for per file to be added to the unittest examples page.
msg360594 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2020-01-24 08:26
I'm not sure I like the API feel after this change: two parameters that can't be used at the same time.

As Karthikeyan points out, this can be achieved using side_effect. Personally, I'd prefer a PR that adds a unit test showing that this approach works, and an example included in the docs as suggested above.

Also, be aware that there can be better options for mocking files. and would be my recommendation.
msg360595 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2020-01-24 08:28
Closing in favour of issue38157.
Date User Action Args
2020-01-24 08:28:39cjw296setstatus: open -> closed
resolution: duplicate
messages: + msg360595

stage: patch review -> resolved
2020-01-24 08:26:24cjw296setmessages: + msg360594
2019-09-12 11:29:48xtreaksetmessages: + msg352126
2019-09-05 07:11:42xtreaksetmessages: + msg351182
2019-07-29 12:09:33Anvilsetmessages: + msg348653
2019-07-29 06:40:07Anvilsetmessages: + msg348599
2019-07-28 13:25:29michael.foordsetmessages: + msg348575
2019-07-24 17:11:42xtreaksetnosy: + cjw296, michael.foord, mariocj89, xtreak

versions: - Python 3.7, Python 3.8
2019-07-24 12:52:27Anvilsetkeywords: + patch
stage: patch review
pull_requests: + pull_request14705
2019-07-24 12:46:58Anvilcreate