-
-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
mock_open is not compatible with read(n) (and pickle.load) #76036
Comments
mock.mock_open works as expected when reading the entire file (read()) or when reading a single line (readline()), but it seems to not support reading a number of bytes (read(n)). These work as expected: from mock import mock_open, patch
# works: consume entire "file"
with patch('__main__.open', mock_open(read_data='bibble')) as m:
with open('foo') as h:
result = h.read()
# works: consume one line
with patch('__main__.open', mock_open(read_data='bibble\nbobble')) as m:
with open('foo') as h:
result = h.readline()
But trying to read only a few bytes fails--mock_open returns the entire read_data instead: # consume first 3 bytes of the "file"
with patch('__main__.open', mock_open(read_data='bibble')) as m:
with open('foo') as h:
result = h.read(3)
Output: Traceback (most recent call last):
File "/tmp/t.py", line 25, in <module>
assert result == 'bib', 'result of read: {}'.format(result)
AssertionError: result of read: bibble The unfortunate effect of this is that mock_open cannot be used with pickle.load. with open('/path/to/file.pkl', 'rb') as f:
x = pickle.load(f) # this requires f.read(1) to work |
Confirmed that the behavior exists in Python 3.6 as well. |
Internally mock_open implementation uses line based iteration [0] to keep track of the state change between read calls. So readline too ignores the argument. There is bpo-25690 for an alternate mock_open implementation but the code change is large and adds a lot of features. A simpler approach would be to use StringIO or BytesIO to keep track of state changes and they provide read, readline and readlines API. mock_open docs mentions about using a customized mock for complex cases but I don't know if worthy enough to make this enhancement given the internal implementation change to support the API or to document this behavior. Looking further there also seems to be a test case for it [1] which will fail if this is fixed since this returns all characters instead of first 10 like using open().read(10). def test_mock_open_read_with_argument(self):
# At one point calling read with an argument was broken
# for mocks returned by mock_open
some_data = 'foo\nbar\nbaz'
mock = mock_open(read_data=some_data)
self.assertEqual(mock().read(10), some_data) $ echo -n 'foo\nbar\nbaz' > /tmp/a.txt
$ ./python.exe
Python 3.8.0a0 (heads/master:f5107dfd42, Dec 16 2018, 13:41:57)
[Clang 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('/tmp/a.txt') as f:
... actual = f.read(10)
... actual, len(actual)
...
('foo\nbar\nba', 10)
>>> with open('/tmp/a.txt') as f:
... from unittest.mock import mock_open
... mock = mock_open(read_data=f.read())
... mock_data = mock().read(10)
... mock_data, len(mock_data)
...
('foo\nbar\nbaz', 11) [0] Line 2349 in f5107df
[1]
|
The off-by-one error in a test added for an unrelated issue (bpo-17467) makes me think @michael.foord just made a mistake in the test.
I opened a new PR to fix this issue: #11521 |
PR was merged and backported to 3.7. So I am closing this as fixed. Thanks Rémi for the patch. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: