diff options
author | Adrian Likins <alikins@redhat.com> | 2016-08-19 18:11:24 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-19 18:11:24 -0400 |
commit | 7d41f623ddabdccdcdb4ea46877cb0c5de67abed (patch) | |
tree | 99d975b31e8db71e4898ea50dba157053d738c4e /lib/ansible/compat | |
parent | 235eab6609ed42fde171e45be266619be6e2a9fd (diff) | |
download | ansible-7d41f623ddabdccdcdb4ea46877cb0c5de67abed.tar.gz |
Move py34 mock_open compat to compat/test/mock (#17157)
test/units/plugins/action/test_action.py had code
for handling a bug in python 3.4's mock_open that
causes errors when reading binary data.
Moved to compat/tests/mock.py so other tests can
use it by default.
Diffstat (limited to 'lib/ansible/compat')
-rw-r--r-- | lib/ansible/compat/tests/mock.py | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/lib/ansible/compat/tests/mock.py b/lib/ansible/compat/tests/mock.py index 0614391c4b..c4bda61ccb 100644 --- a/lib/ansible/compat/tests/mock.py +++ b/lib/ansible/compat/tests/mock.py @@ -22,6 +22,7 @@ __metaclass__ = type ''' Compat module for Python3.x's unittest.mock module ''' +import sys # Python 2.7 @@ -36,3 +37,82 @@ except ImportError: from mock import * except ImportError: print('You need the mock library installed on python2.x to run tests') + + +# Prior to 3.4.4, mock_open cannot handle binary read_data +if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): + file_spec = None + + def _iterate_read_data(read_data): + # Helper for mock_open: + # Retrieve lines from read_data via a generator so that separate calls to + # readline, read, and readlines are properly interleaved + sep = b'\n' if isinstance(read_data, bytes) else '\n' + data_as_list = [l + sep for l in read_data.split(sep)] + + if data_as_list[-1] == sep: + # If the last line ended in a newline, the list comprehension will have an + # extra entry that's just a newline. Remove this. + data_as_list = data_as_list[:-1] + else: + # If there wasn't an extra newline by itself, then the file being + # emulated doesn't have a newline to end the last line remove the + # newline that our naive format() added + data_as_list[-1] = data_as_list[-1][:-1] + + for line in data_as_list: + yield line + + def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` methoddline`, and `readlines` of the + file handle to return. This is an empty string by default. + """ + def _readlines_side_effect(*args, **kwargs): + if handle.readlines.return_value is not None: + return handle.readlines.return_value + return list(_data) + + def _read_side_effect(*args, **kwargs): + if handle.read.return_value is not None: + return handle.read.return_value + return type(read_data)().join(_data) + + def _readline_side_effect(): + if handle.readline.return_value is not None: + while True: + yield handle.readline.return_value + for line in _data: + yield line + + global file_spec + if file_spec is None: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.__enter__.return_value = handle + + _data = _iterate_read_data(read_data) + + handle.write.return_value = None + handle.read.return_value = None + handle.readline.return_value = None + handle.readlines.return_value = None + + handle.read.side_effect = _read_side_effect + handle.readline.side_effect = _readline_side_effect() + handle.readlines.side_effect = _readlines_side_effect + + mock.return_value = handle + return mock |