##// END OF EJS Templates
windows: always work around EINVAL in case of broken pipe for stdout / stderr...
Manuel Jacob -
r45708:a37f290a default
parent child Browse files
Show More
@@ -119,18 +119,25 b' if pycompat.ispy3:'
119 119 # a silly wrapper to make a bytes stream backed by a unicode one.
120 120 stdin = sys.stdin.buffer
121 121 stdout = _make_write_all(sys.stdout.buffer)
122 stderr = _make_write_all(sys.stderr.buffer)
123 if pycompat.iswindows:
124 # Work around Windows bugs.
125 stdout = platform.winstdout(stdout)
126 stderr = platform.winstdout(stderr)
122 127 if isatty(stdout):
123 128 # The standard library doesn't offer line-buffered binary streams.
124 129 stdout = make_line_buffered(stdout)
125 stderr = _make_write_all(sys.stderr.buffer)
126 130 else:
127 131 # Python 2 uses the I/O streams provided by the C library.
128 132 stdin = sys.stdin
129 133 stdout = sys.stdout
134 stderr = sys.stderr
135 if pycompat.iswindows:
136 # Work around Windows bugs.
137 stdout = platform.winstdout(stdout)
138 stderr = platform.winstdout(stderr)
130 139 if isatty(stdout):
131 140 if pycompat.iswindows:
132 # Work around size limit when writing to console.
133 stdout = platform.winstdout(stdout)
134 141 # The Windows C runtime library doesn't support line buffering.
135 142 stdout = make_line_buffered(stdout)
136 143 else:
@@ -138,7 +145,6 b' else:'
138 145 # replace a TTY destined stdout with a pipe destined stdout (e.g.
139 146 # pager), we want line buffering.
140 147 stdout = os.fdopen(stdout.fileno(), 'wb', 1)
141 stderr = sys.stderr
142 148
143 149
144 150 findexe = platform.findexe
@@ -197,6 +197,7 b' class winstdout(object):'
197 197
198 198 def __init__(self, fp):
199 199 self.fp = fp
200 self.throttle = not pycompat.ispy3 and fp.isatty()
200 201
201 202 def __getattr__(self, key):
202 203 return getattr(self.fp, key)
@@ -208,13 +209,16 b' class winstdout(object):'
208 209 pass
209 210
210 211 def write(self, s):
212 if not pycompat.ispy3:
213 self.softspace = 0
211 214 try:
215 if not self.throttle:
216 return self.fp.write(s)
212 217 # This is workaround for "Not enough space" error on
213 218 # writing large size of data to console.
214 219 limit = 16000
215 220 l = len(s)
216 221 start = 0
217 self.softspace = 0
218 222 while start < l:
219 223 end = start + limit
220 224 self.fp.write(s[start:end])
@@ -13,7 +13,7 b' import sys'
13 13 import tempfile
14 14 import unittest
15 15
16 from mercurial import pycompat
16 from mercurial import pycompat, util
17 17
18 18
19 19 if pycompat.ispy3:
@@ -71,6 +71,34 b' with os.fdopen('
71 71 '''
72 72
73 73
74 TEST_BROKEN_PIPE_CHILD_SCRIPT = r'''
75 import os
76 import pickle
77
78 from mercurial import dispatch
79 from mercurial.utils import procutil
80
81 dispatch.initstdio()
82 procutil.stdin.read(1) # wait until parent process closed pipe
83 try:
84 procutil.{stream}.write(b'test')
85 procutil.{stream}.flush()
86 except EnvironmentError as e:
87 with os.fdopen(
88 os.open(
89 {err_fn!r},
90 os.O_WRONLY
91 | getattr(os, 'O_BINARY', 0)
92 | getattr(os, 'O_TEMPORARY', 0),
93 ),
94 'wb',
95 ) as err_f:
96 pickle.dump(e, err_f)
97 # Exit early to suppress further broken pipe errors at interpreter shutdown.
98 os._exit(0)
99 '''
100
101
74 102 @contextlib.contextmanager
75 103 def _closing(fds):
76 104 try:
@@ -148,11 +176,15 b' class TestStdio(unittest.TestCase):'
148 176 check_output,
149 177 python_args=[],
150 178 post_child_check=None,
179 stdin_generator=None,
151 180 ):
152 181 assert stream in ('stdout', 'stderr')
153 with rwpair_generator() as (stream_receiver, child_stream), open(
154 os.devnull, 'rb'
155 ) as child_stdin:
182 if stdin_generator is None:
183 stdin_generator = open(os.devnull, 'rb')
184 with rwpair_generator() as (
185 stream_receiver,
186 child_stream,
187 ), stdin_generator as child_stdin:
156 188 proc = subprocess.Popen(
157 189 [sys.executable] + python_args + ['-c', child_script],
158 190 stdin=child_stdin,
@@ -295,6 +327,37 b' class TestStdio(unittest.TestCase):'
295 327 def test_large_write_stderr_ptys_unbuffered(self):
296 328 self._test_large_write('stderr', _ptys, python_args=['-u'])
297 329
330 def _test_broken_pipe(self, stream):
331 assert stream in ('stdout', 'stderr')
332
333 def check_output(stream_receiver, proc):
334 os.close(stream_receiver)
335 proc.stdin.write(b'x')
336 proc.stdin.close()
337
338 def post_child_check():
339 err = util.pickle.load(err_f)
340 self.assertEqual(err.errno, errno.EPIPE)
341 self.assertEqual(err.strerror, "Broken pipe")
342
343 with tempfile.NamedTemporaryFile('rb') as err_f:
344 self._test(
345 TEST_BROKEN_PIPE_CHILD_SCRIPT.format(
346 stream=stream, err_fn=err_f.name
347 ),
348 stream,
349 _pipes,
350 check_output,
351 post_child_check=post_child_check,
352 stdin_generator=util.nullcontextmanager(subprocess.PIPE),
353 )
354
355 def test_broken_pipe_stdout(self):
356 self._test_broken_pipe('stdout')
357
358 def test_broken_pipe_stderr(self):
359 self._test_broken_pipe('stderr')
360
298 361
299 362 if __name__ == '__main__':
300 363 import silenttestrunner
General Comments 0
You need to be logged in to leave comments. Login now