Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ca647ef

Browse files
committed
Issue #21750: Further fixup to be styled like other mock APIs.
1 parent 80e4f30 commit ca647ef

4 files changed

Lines changed: 73 additions & 42 deletions

File tree

Doc/library/unittest.mock.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,9 +1995,11 @@ mock_open
19951995
:meth:`~io.IOBase.readline`, and :meth:`~io.IOBase.readlines` methods
19961996
of the file handle to return. Calls to those methods will take data from
19971997
*read_data* until it is depleted. The mock of these methods is pretty
1998-
simplistic. If you need more control over the data that you are feeding to
1999-
the tested code you will need to customize this mock for yourself.
2000-
*read_data* is an empty string by default.
1998+
simplistic: every time the *mock* is called, the *read_data* is rewound to
1999+
the start. If you need more control over the data that you are feeding to
2000+
the tested code you will need to customize this mock for yourself. When that
2001+
is insufficient, one of the in-memory filesystem packages on `PyPI
2002+
<https://pypi.python.org/pypi>`_ can offer a realistic filesystem for testing.
20012003

20022004
Using :func:`open` as a context manager is a great way to ensure your file handles
20032005
are closed properly and is becoming common::

Lib/unittest/mock.py

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,6 +2278,24 @@ def mock_open(mock=None, read_data=''):
22782278
`read_data` is a string for the `read` methoddline`, and `readlines` of the
22792279
file handle to return. This is an empty string by default.
22802280
"""
2281+
def _readlines_side_effect(*args, **kwargs):
2282+
if handle.readlines.return_value is not None:
2283+
return handle.readlines.return_value
2284+
return list(_state[0])
2285+
2286+
def _read_side_effect(*args, **kwargs):
2287+
if handle.read.return_value is not None:
2288+
return handle.read.return_value
2289+
return ''.join(_state[0])
2290+
2291+
def _readline_side_effect():
2292+
if handle.readline.return_value is not None:
2293+
while True:
2294+
yield handle.readline.return_value
2295+
for line in _state[0]:
2296+
yield line
2297+
2298+
22812299
global file_spec
22822300
if file_spec is None:
22832301
import _io
@@ -2286,42 +2304,31 @@ def mock_open(mock=None, read_data=''):
22862304
if mock is None:
22872305
mock = MagicMock(name='open', spec=open)
22882306

2289-
def make_handle(*args, **kwargs):
2290-
# Arg checking is handled by __call__
2291-
def _readlines_side_effect(*args, **kwargs):
2292-
if handle.readlines.return_value is not None:
2293-
return handle.readlines.return_value
2294-
return list(_data)
2295-
2296-
def _read_side_effect(*args, **kwargs):
2297-
if handle.read.return_value is not None:
2298-
return handle.read.return_value
2299-
return ''.join(_data)
2300-
2301-
def _readline_side_effect():
2302-
if handle.readline.return_value is not None:
2303-
while True:
2304-
yield handle.readline.return_value
2305-
for line in _data:
2306-
yield line
2307-
2308-
handle = MagicMock(spec=file_spec)
2309-
handle.__enter__.return_value = handle
2310-
2311-
_data = _iterate_read_data(read_data)
2312-
2313-
handle.write.return_value = None
2314-
handle.read.return_value = None
2315-
handle.readline.return_value = None
2316-
handle.readlines.return_value = None
2317-
2318-
handle.read.side_effect = _read_side_effect
2319-
handle.readline.side_effect = _readline_side_effect()
2320-
handle.readlines.side_effect = _readlines_side_effect
2321-
_check_and_set_parent(mock, handle, None, '()')
2322-
return handle
2323-
2324-
mock.side_effect = make_handle
2307+
handle = MagicMock(spec=file_spec)
2308+
handle.__enter__.return_value = handle
2309+
2310+
_state = [_iterate_read_data(read_data), None]
2311+
2312+
handle.write.return_value = None
2313+
handle.read.return_value = None
2314+
handle.readline.return_value = None
2315+
handle.readlines.return_value = None
2316+
2317+
handle.read.side_effect = _read_side_effect
2318+
_state[1] = _readline_side_effect()
2319+
handle.readline.side_effect = _state[1]
2320+
handle.readlines.side_effect = _readlines_side_effect
2321+
2322+
def reset_data(*args, **kwargs):
2323+
_state[0] = _iterate_read_data(read_data)
2324+
if handle.readline.side_effect == _state[1]:
2325+
# Only reset the side effect if the user hasn't overridden it.
2326+
_state[1] = _readline_side_effect()
2327+
handle.readline.side_effect = _state[1]
2328+
return DEFAULT
2329+
2330+
mock.side_effect = reset_data
2331+
mock.return_value = handle
23252332
return mock
23262333

23272334

Lib/unittest/test/testmock/testmock.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import copy
22
import sys
3+
import tempfile
34

45
import unittest
56
from unittest.test.testmock.support import is_instance
@@ -1329,8 +1330,29 @@ def test_magic_methods_mock_calls(self):
13291330
def test_mock_open_reuse_issue_21750(self):
13301331
mocked_open = mock.mock_open(read_data='data')
13311332
f1 = mocked_open('a-name')
1333+
f1_data = f1.read()
13321334
f2 = mocked_open('another-name')
1333-
self.assertEqual(f1.read(), f2.read())
1335+
f2_data = f2.read()
1336+
self.assertEqual(f1_data, f2_data)
1337+
1338+
def test_mock_open_write(self):
1339+
# Test exception in file writing write()
1340+
mock_namedtemp = mock.mock_open(mock.MagicMock(name='JLV'))
1341+
with mock.patch('tempfile.NamedTemporaryFile', mock_namedtemp):
1342+
mock_filehandle = mock_namedtemp.return_value
1343+
mock_write = mock_filehandle.write
1344+
mock_write.side_effect = OSError('Test 2 Error')
1345+
def attempt():
1346+
tempfile.NamedTemporaryFile().write('asd')
1347+
self.assertRaises(OSError, attempt)
1348+
1349+
def test_mock_open_alter_readline(self):
1350+
mopen = mock.mock_open(read_data='foo\nbarn')
1351+
mopen.return_value.readline.side_effect = lambda *args:'abc'
1352+
first = mopen().readline()
1353+
second = mopen().readline()
1354+
self.assertEqual('abc', first)
1355+
self.assertEqual('abc', second)
13341356

13351357
def test_mock_parents(self):
13361358
for Klass in Mock, MagicMock:

Lib/unittest/test/testmock/testwith.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,15 @@ def test_mock_open(self):
141141

142142
def test_mock_open_context_manager(self):
143143
mock = mock_open()
144+
handle = mock.return_value
144145
with patch('%s.open' % __name__, mock, create=True):
145146
with open('foo') as f:
146147
f.read()
147148

148149
expected_calls = [call('foo'), call().__enter__(), call().read(),
149150
call().__exit__(None, None, None)]
150151
self.assertEqual(mock.mock_calls, expected_calls)
151-
# mock_open.return_value is no longer static, because
152-
# readline support requires that it mutate state
152+
self.assertIs(f, handle)
153153

154154
def test_mock_open_context_manager_multiple_times(self):
155155
mock = mock_open()

0 commit comments

Comments
 (0)