Skip to content

Commit a6516f8

Browse files
miss-islingtonRémi Lapeyre
authored andcommitted
bpo-31855: unittest.mock.mock_open() results now respects the argument of read([size]) (GH-11521) (#13152)
unittest.mock.mock_open() results now respects the argument of read([size]) Co-Authored-By: remilapeyre <[email protected]> (cherry picked from commit 11a8832) Co-authored-by: Rémi Lapeyre <[email protected]>
1 parent ffa29b5 commit a6516f8

File tree

3 files changed

+22
-26
lines changed

3 files changed

+22
-26
lines changed

Lib/unittest/mock.py

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
__version__ = '1.0'
2626

2727

28+
import io
2829
import inspect
2930
import pprint
3031
import sys
@@ -2331,25 +2332,12 @@ def __init__(self, spec, spec_set=False, parent=None,
23312332

23322333
file_spec = None
23332334

2334-
def _iterate_read_data(read_data):
2335-
# Helper for mock_open:
2336-
# Retrieve lines from read_data via a generator so that separate calls to
2337-
# readline, read, and readlines are properly interleaved
2338-
sep = b'\n' if isinstance(read_data, bytes) else '\n'
2339-
data_as_list = [l + sep for l in read_data.split(sep)]
2340-
2341-
if data_as_list[-1] == sep:
2342-
# If the last line ended in a newline, the list comprehension will have an
2343-
# extra entry that's just a newline. Remove this.
2344-
data_as_list = data_as_list[:-1]
2345-
else:
2346-
# If there wasn't an extra newline by itself, then the file being
2347-
# emulated doesn't have a newline to end the last line remove the
2348-
# newline that our naive format() added
2349-
data_as_list[-1] = data_as_list[-1][:-1]
23502335

2351-
for line in data_as_list:
2352-
yield line
2336+
def _to_stream(read_data):
2337+
if isinstance(read_data, bytes):
2338+
return io.BytesIO(read_data)
2339+
else:
2340+
return io.StringIO(read_data)
23532341

23542342

23552343
def mock_open(mock=None, read_data=''):
@@ -2364,20 +2352,23 @@ def mock_open(mock=None, read_data=''):
23642352
`read_data` is a string for the `read`, `readline` and `readlines` of the
23652353
file handle to return. This is an empty string by default.
23662354
"""
2355+
_read_data = _to_stream(read_data)
2356+
_state = [_read_data, None]
2357+
23672358
def _readlines_side_effect(*args, **kwargs):
23682359
if handle.readlines.return_value is not None:
23692360
return handle.readlines.return_value
2370-
return list(_state[0])
2361+
return _state[0].readlines(*args, **kwargs)
23712362

23722363
def _read_side_effect(*args, **kwargs):
23732364
if handle.read.return_value is not None:
23742365
return handle.read.return_value
2375-
return type(read_data)().join(_state[0])
2366+
return _state[0].read(*args, **kwargs)
23762367

2377-
def _readline_side_effect():
2368+
def _readline_side_effect(*args, **kwargs):
23782369
yield from _iter_side_effect()
23792370
while True:
2380-
yield type(read_data)()
2371+
yield _state[0].readline(*args, **kwargs)
23812372

23822373
def _iter_side_effect():
23832374
if handle.readline.return_value is not None:
@@ -2397,8 +2388,6 @@ def _iter_side_effect():
23972388
handle = MagicMock(spec=file_spec)
23982389
handle.__enter__.return_value = handle
23992390

2400-
_state = [_iterate_read_data(read_data), None]
2401-
24022391
handle.write.return_value = None
24032392
handle.read.return_value = None
24042393
handle.readline.return_value = None
@@ -2411,7 +2400,7 @@ def _iter_side_effect():
24112400
handle.__iter__.side_effect = _iter_side_effect
24122401

24132402
def reset_data(*args, **kwargs):
2414-
_state[0] = _iterate_read_data(read_data)
2403+
_state[0] = _to_stream(read_data)
24152404
if handle.readline.side_effect == _state[1]:
24162405
# Only reset the side effect if the user hasn't overridden it.
24172406
_state[1] = _readline_side_effect()

Lib/unittest/test/testmock/testwith.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,12 @@ def test_mock_open_read_with_argument(self):
286286
# for mocks returned by mock_open
287287
some_data = 'foo\nbar\nbaz'
288288
mock = mock_open(read_data=some_data)
289-
self.assertEqual(mock().read(10), some_data)
289+
self.assertEqual(mock().read(10), some_data[:10])
290+
self.assertEqual(mock().read(10), some_data[:10])
291+
292+
f = mock()
293+
self.assertEqual(f.read(10), some_data[:10])
294+
self.assertEqual(f.read(10), some_data[10:])
290295

291296

292297
def test_interleaved_reads(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`unittest.mock.mock_open` results now respects the argument of read([size]).
2+
Patch contributed by Rémi Lapeyre.

0 commit comments

Comments
 (0)