classification
Title: Make mock_open return per-file content
Type: enhancement Stage: patch review
Components: Library (Lib) Versions: Python 3.9
process
Status: open Resolution:
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 2019-09-12 11:29 by xtreak.

Pull Requests
URL Status Linked Edit
PR 14933 open Anvil, 2019-07-24 12:52
Messages (6)
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 file1.read() == "data1"

with open("file2") as file2:
    assert file2.read() == "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 triager) 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 file1.read() == "data1"

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

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

        assert file1.read() == ""

    with open("defaultfile") as file4:
        assert file4.read() == "default mock data"
msg352126 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) 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.
History
Date User Action Args
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