|
@@ -40,8 +40,10 @@ import subprocess2
|
|
|
|
|
|
if sys.version_info.major == 2:
|
|
if sys.version_info.major == 2:
|
|
from cStringIO import StringIO
|
|
from cStringIO import StringIO
|
|
|
|
+ string_type = basestring
|
|
else:
|
|
else:
|
|
from io import StringIO
|
|
from io import StringIO
|
|
|
|
+ string_type = str
|
|
|
|
|
|
|
|
|
|
RETRY_MAX = 3
|
|
RETRY_MAX = 3
|
|
@@ -371,6 +373,10 @@ class Annotated(Wrapper):
|
|
def write(self, out):
|
|
def write(self, out):
|
|
index = getattr(threading.currentThread(), 'index', 0)
|
|
index = getattr(threading.currentThread(), 'index', 0)
|
|
if not index and not self.__include_zero:
|
|
if not index and not self.__include_zero:
|
|
|
|
+ # Store as bytes to ensure Unicode characters get output correctly.
|
|
|
|
+ if isinstance(out, bytes):
|
|
|
|
+ out = out.decode('utf-8')
|
|
|
|
+
|
|
# Unindexed threads aren't buffered.
|
|
# Unindexed threads aren't buffered.
|
|
return self._wrapped.write(out)
|
|
return self._wrapped.write(out)
|
|
|
|
|
|
@@ -380,28 +386,32 @@ class Annotated(Wrapper):
|
|
# Strings are immutable, requiring to keep a lock for the whole dictionary
|
|
# Strings are immutable, requiring to keep a lock for the whole dictionary
|
|
# otherwise. Using an array is faster than using a dummy object.
|
|
# otherwise. Using an array is faster than using a dummy object.
|
|
if not index in self.__output_buffers:
|
|
if not index in self.__output_buffers:
|
|
- obj = self.__output_buffers[index] = ['']
|
|
|
|
|
|
+ obj = self.__output_buffers[index] = [b'']
|
|
else:
|
|
else:
|
|
obj = self.__output_buffers[index]
|
|
obj = self.__output_buffers[index]
|
|
finally:
|
|
finally:
|
|
self.lock.release()
|
|
self.lock.release()
|
|
|
|
|
|
|
|
+ # Store as bytes to ensure Unicode characters get output correctly.
|
|
|
|
+ if isinstance(out, string_type):
|
|
|
|
+ out = out.encode('utf-8')
|
|
|
|
+
|
|
# Continue lockless.
|
|
# Continue lockless.
|
|
obj[0] += out
|
|
obj[0] += out
|
|
while True:
|
|
while True:
|
|
# TODO(agable): find both of these with a single pass.
|
|
# TODO(agable): find both of these with a single pass.
|
|
- cr_loc = obj[0].find('\r')
|
|
|
|
- lf_loc = obj[0].find('\n')
|
|
|
|
|
|
+ cr_loc = obj[0].find(b'\r')
|
|
|
|
+ lf_loc = obj[0].find(b'\n')
|
|
if cr_loc == lf_loc == -1:
|
|
if cr_loc == lf_loc == -1:
|
|
break
|
|
break
|
|
elif cr_loc == -1 or (lf_loc >= 0 and lf_loc < cr_loc):
|
|
elif cr_loc == -1 or (lf_loc >= 0 and lf_loc < cr_loc):
|
|
- line, remaining = obj[0].split('\n', 1)
|
|
|
|
|
|
+ line, remaining = obj[0].split(b'\n', 1)
|
|
if line:
|
|
if line:
|
|
- self._wrapped.write('%d>%s\n' % (index, line))
|
|
|
|
|
|
+ self._wrapped.write('%d>%s\n' % (index, line.decode('utf-8')))
|
|
elif lf_loc == -1 or (cr_loc >= 0 and cr_loc < lf_loc):
|
|
elif lf_loc == -1 or (cr_loc >= 0 and cr_loc < lf_loc):
|
|
- line, remaining = obj[0].split('\r', 1)
|
|
|
|
|
|
+ line, remaining = obj[0].split(b'\r', 1)
|
|
if line:
|
|
if line:
|
|
- self._wrapped.write('%d>%s\r' % (index, line))
|
|
|
|
|
|
+ self._wrapped.write('%d>%s\r' % (index, line.decode('utf-8')))
|
|
obj[0] = remaining
|
|
obj[0] = remaining
|
|
|
|
|
|
def flush(self):
|
|
def flush(self):
|
|
@@ -423,7 +433,7 @@ class Annotated(Wrapper):
|
|
# Don't keep the lock while writting. Will append \n when it shouldn't.
|
|
# Don't keep the lock while writting. Will append \n when it shouldn't.
|
|
for orphan in orphans:
|
|
for orphan in orphans:
|
|
if orphan[1]:
|
|
if orphan[1]:
|
|
- self._wrapped.write('%d>%s\n' % (orphan[0], orphan[1]))
|
|
|
|
|
|
+ self._wrapped.write('%d>%s\n' % (orphan[0], orphan[1].decode('utf-8')))
|
|
return self._wrapped.flush()
|
|
return self._wrapped.flush()
|
|
|
|
|
|
|
|
|