From 415b48056d9d021e04ec972029040a89a6b13928 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Thu, 7 Jun 2018 16:40:58 -0700 Subject: [PATCH] Before writing object data to stdout, re-open it in binary mode Otherwise, you can hit TypeErrors on Python3. Change-Id: I9a891508886feddac3982ce593bd95130392e035 Closes-Bug: 1775482 --- openstackclient/api/object_store_v1.py | 5 +++-- .../tests/unit/object/v1/test_object_all.py | 22 +++++++++++++++++-- .../notes/bug-1775482-7ed2a9a8765b313e.yaml | 6 +++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/bug-1775482-7ed2a9a8765b313e.yaml diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index 3103352503..d1e5dfaf45 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -378,8 +378,9 @@ class APIv1(api.BaseAPI): ) if response.status_code == 200: if file == '-': - for chunk in response.iter_content(64 * 1024): - sys.stdout.write(chunk) + with os.fdopen(sys.stdout.fileno(), 'wb') as f: + for chunk in response.iter_content(64 * 1024): + f.write(chunk) else: if not os.path.exists(os.path.dirname(file)): if len(os.path.dirname(file)) > 0: diff --git a/openstackclient/tests/unit/object/v1/test_object_all.py b/openstackclient/tests/unit/object/v1/test_object_all.py index 363f2ea21e..08a7534d06 100644 --- a/openstackclient/tests/unit/object/v1/test_object_all.py +++ b/openstackclient/tests/unit/object/v1/test_object_all.py @@ -241,7 +241,25 @@ class TestObjectSave(TestObjectAll): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch('sys.stdout', new=six.BytesIO()) as fake_stdout: + class FakeStdout(six.BytesIO): + def __init__(self): + six.BytesIO.__init__(self) + self.context_manager_calls = [] + + def __enter__(self): + self.context_manager_calls.append('__enter__') + return self + + def __exit__(self, *a): + self.context_manager_calls.append('__exit__') + + with mock.patch('sys.stdout') as fake_stdout, mock.patch( + 'os.fdopen', return_value=FakeStdout()) as fake_fdopen: + fake_stdout.fileno.return_value = 123 self.cmd.take_action(parsed_args) - self.assertEqual(fake_stdout.getvalue(), object_fakes.object_1_content) + self.assertEqual(fake_fdopen.return_value.getvalue(), + object_fakes.object_1_content) + self.assertEqual(fake_fdopen.mock_calls, [mock.call(123, 'wb')]) + self.assertEqual(fake_fdopen.return_value.context_manager_calls, + ['__enter__', '__exit__']) diff --git a/releasenotes/notes/bug-1775482-7ed2a9a8765b313e.yaml b/releasenotes/notes/bug-1775482-7ed2a9a8765b313e.yaml new file mode 100644 index 0000000000..88d9890a76 --- /dev/null +++ b/releasenotes/notes/bug-1775482-7ed2a9a8765b313e.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Re-open stdout in binary mode before writing object data in + ``object save --file -`` command. + [Bug `1775482 `_]