Skip to content

Commit

Permalink
Wrap stdout/stderr to avoid "Not enough space" in Python 2 on Windows 7
Browse files Browse the repository at this point in the history
When you set the standard streams to binary you seem to only be able to
write up to some limit in a single call to write. So we wrap the streams
and write in chunks to avoid this.

A similar hack was also implemented in Python 3 to work around this. But
that hack doesn't seem to exist in Python 2.

This might have unintended side effects due to replacing the standard
stream objects.

Fixes #825
  • Loading branch information
segevfiner committed Feb 7, 2018
1 parent 9bd66b3 commit 7a5b8c8
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 0 deletions.
1 change: 1 addition & 0 deletions click/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ def func():
return rv
rv = wrapper_func()
try:
stream = src_func() # In case wrapper_func() modified the stream
cache[stream] = rv
except Exception:
pass
Expand Down
32 changes: 32 additions & 0 deletions click/_winconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,30 @@ def __repr__(self):
)


class StreamWrapper(object):
"""
Wraps a stream (such as stdout), acting as a transparent proxy for all
attribute access apart from method 'write()' which we wrap to write in
limited chunks due to a Windows limitation on binary console streams.
"""
def __init__(self, wrapped):
# double-underscore everything to prevent clashes with names of
# attributes on the wrapped stream object.
self.__wrapped = wrapped

def __getattr__(self, name):
return getattr(self.__wrapped, name)

def write(self, text):
total_to_write = len(text)
written = 0

while written < total_to_write:
to_write = min(total_to_write - written, MAX_BYTES_WRITTEN)
self.__wrapped.write(text[written:written+to_write])
written += to_write


def _get_text_stdin(buffer_stream):
text_stream = _NonClosingTextIOWrapper(
io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)),
Expand All @@ -209,13 +233,21 @@ def _get_text_stdin(buffer_stream):


def _get_text_stdout(buffer_stream):
if PY2:
buffer_stream = StreamWrapper(buffer_stream)
sys.stdout = StreamWrapper(sys.stdout)

text_stream = _NonClosingTextIOWrapper(
_WindowsConsoleWriter(STDOUT_HANDLE),
'utf-16-le', 'strict', line_buffering=True)
return ConsoleStream(text_stream, buffer_stream)


def _get_text_stderr(buffer_stream):
if PY2:
buffer_stream = StreamWrapper(buffer_stream)
sys.stderr = StreamWrapper(sys.stderr)

text_stream = _NonClosingTextIOWrapper(
_WindowsConsoleWriter(STDERR_HANDLE),
'utf-16-le', 'strict', line_buffering=True)
Expand Down

0 comments on commit 7a5b8c8

Please sign in to comment.