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