Skip to content

Commit

Permalink
Encode layer output if needed as UTF-8 before writing it to a buffer …
Browse files Browse the repository at this point in the history
…that explicitly (only) accepts bytes.

Update the test case to make this explicit. There are three test failures without doing the encoding.

Fixes #80
  • Loading branch information
jamadden committed Nov 23, 2018
1 parent 138a8c9 commit 48dda91
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 6 deletions.
4 changes: 3 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
4.9.2 (unreleased)
==================

- Nothing changed yet.
- Fix ``TypeError: a bytes-like object is required, not 'str'``
running tests in parallel on Python 3. See `issue 80
<https://github.com/zopefoundation/zope.testrunner/issues/80>`_.


4.9.1 (2018-11-21)
Expand Down
3 changes: 3 additions & 0 deletions src/zope/testrunner/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ def resume_tests(script_parts, options, features, layers, failures, errors,
current_result = next(results_iter)
last_layer_intermediate_output = None
output = None
# Get an object that (only) accepts bytes
stdout = _get_output_buffer(sys.stdout)
while ready_threads or running_threads:
while len(running_threads) < options.processes and ready_threads:
Expand All @@ -723,6 +724,8 @@ def resume_tests(script_parts, options, features, layers, failures, errors,
('[Parallel tests running in '
'%s:\n ' % (layer_name,)).encode('utf-8'))
last_layer_intermediate_output = layer_name
if not isinstance(output, bytes):
output = output.encode('utf-8')
stdout.write(output)
# Display results in the order they would have been displayed, had the
# work not been done in parallel.
Expand Down
27 changes: 22 additions & 5 deletions src/zope/testrunner/tests/testrunner-layers-buff.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,32 @@ First, we wrap stdout with an object that instruments it. It notes the time at
which a given line was written.

>>> import os, sys, datetime
>>> class RecordingStreamWrapper:
>>> class RecordingStreamWrapper(object):
... def __init__(self, wrapped):
... self.record = []
... self.wrapped = wrapped
... @property
... def buffer(self):
... # runner._get_output_buffer attempts to write b''
... # to us, and when that fails (see write()), accesses our .buffer directly.
... # That object deals in bytes.
... wrapper = self
... class buffer(object):
... def write(self, data):
... assert isinstance(data, bytes)
... wrapper.write(data.decode('utf-8'))
... def writelines(self, lines):
... for line in lines:
... self.write(line)
... def flush(self):
... wrapper.flush()
... return buffer()
... def write(self, out):
... # Not very accurate, but it will do as long as we don't
... # actually need to be binary-clean.
... # sys.stdout deals with native strings;
... # and raises TypeError for other things. We must do
... # the same.
... if not isinstance(out, str):
... out = out.decode('utf-8')
... raise TypeError
... self.record.append((out, datetime.datetime.now()))
... self.wrapped.write(out)
... def writelines(self, lines):
Expand Down Expand Up @@ -80,7 +97,7 @@ more than a second after the second suite ran.
... if time-last_time >= pause:
... # We paused!
... print('PAUSE FOUND BETWEEN THESE LINES:')
... print(''.join([last_line, line, '-'*70]))
... print(''.join([last_line, line, '-' * 70]))
... last_line, last_time = line, time

>>> assert_progressive_output() # doctest: +ELLIPSIS
Expand Down

0 comments on commit 48dda91

Please sign in to comment.