From 7a5b8c88affd00fe34249b16fdc826b0d5c051d9 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Wed, 12 Jul 2017 19:49:59 +0300 Subject: [PATCH] Wrap stdout/stderr to avoid "Not enough space" in Python 2 on Windows 7 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 --- click/_compat.py | 1 + click/_winconsole.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/click/_compat.py b/click/_compat.py index 2b43412c4..33167876c 100644 --- a/click/_compat.py +++ b/click/_compat.py @@ -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 diff --git a/click/_winconsole.py b/click/_winconsole.py index f1d5e28ca..e846b1765 100644 --- a/click/_winconsole.py +++ b/click/_winconsole.py @@ -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)), @@ -209,6 +233,10 @@ 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) @@ -216,6 +244,10 @@ def _get_text_stdout(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)