Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d06fa47

Browse files
committed
Merged revisions 73825-73826 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r73825 | gregory.p.smith | 2009-07-03 18:49:29 -0700 (Fri, 03 Jul 2009) | 9 lines Use select.poll() in subprocess, when available, rather than select() so that it does not fail when file descriptors are large. Fixes issue3392. Patch largely contributed by Frank Chu (fpmc) with some improvements by me. See http://bugs.python.org/issue3392. ........ r73826 | gregory.p.smith | 2009-07-03 18:55:11 -0700 (Fri, 03 Jul 2009) | 2 lines news entry for r73825 ........ Candidate for backporting to release31-maint as it is a bug fix and changes no public API.
1 parent b970b86 commit d06fa47

3 files changed

Lines changed: 120 additions & 30 deletions

File tree

Lib/subprocess.py

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ class pywintypes:
371371
error = IOError
372372
else:
373373
import select
374+
_has_poll = hasattr(select, 'poll')
374375
import errno
375376
import fcntl
376377
import pickle
@@ -383,6 +384,11 @@ class pywintypes:
383384
except:
384385
MAXFD = 256
385386

387+
# When select or poll has indicated that the file is writable,
388+
# we can write up to _PIPE_BUF bytes without risk of blocking.
389+
# POSIX defines PIPE_BUF as >= 512.
390+
_PIPE_BUF = getattr(select, 'PIPE_BUF', 512)
391+
386392
_active = []
387393

388394
def _cleanup():
@@ -1173,19 +1179,103 @@ def wait(self):
11731179

11741180

11751181
def _communicate(self, input):
1176-
read_set = []
1177-
write_set = []
1178-
stdout = None # Return
1179-
stderr = None # Return
1180-
11811182
if self.stdin:
11821183
# Flush stdio buffer. This might block, if the user has
11831184
# been writing to .stdin in an uncontrolled fashion.
11841185
self.stdin.flush()
1185-
if input:
1186-
write_set.append(self.stdin)
1187-
else:
1186+
if not input:
11881187
self.stdin.close()
1188+
1189+
if _has_poll:
1190+
stdout, stderr = self._communicate_with_poll(input)
1191+
else:
1192+
stdout, stderr = self._communicate_with_select(input)
1193+
1194+
# All data exchanged. Translate lists into strings.
1195+
if stdout is not None:
1196+
stdout = b''.join(stdout)
1197+
if stderr is not None:
1198+
stderr = b''.join(stderr)
1199+
1200+
# Translate newlines, if requested.
1201+
# This also turns bytes into strings.
1202+
if self.universal_newlines:
1203+
if stdout is not None:
1204+
stdout = self._translate_newlines(stdout,
1205+
self.stdout.encoding)
1206+
if stderr is not None:
1207+
stderr = self._translate_newlines(stderr,
1208+
self.stderr.encoding)
1209+
1210+
self.wait()
1211+
return (stdout, stderr)
1212+
1213+
1214+
def _communicate_with_poll(self, input):
1215+
stdout = None # Return
1216+
stderr = None # Return
1217+
fd2file = {}
1218+
fd2output = {}
1219+
1220+
poller = select.poll()
1221+
def register_and_append(file_obj, eventmask):
1222+
poller.register(file_obj.fileno(), eventmask)
1223+
fd2file[file_obj.fileno()] = file_obj
1224+
1225+
def close_unregister_and_remove(fd):
1226+
poller.unregister(fd)
1227+
fd2file[fd].close()
1228+
fd2file.pop(fd)
1229+
1230+
if self.stdin and input:
1231+
register_and_append(self.stdin, select.POLLOUT)
1232+
1233+
select_POLLIN_POLLPRI = select.POLLIN | select.POLLPRI
1234+
if self.stdout:
1235+
register_and_append(self.stdout, select_POLLIN_POLLPRI)
1236+
fd2output[self.stdout.fileno()] = stdout = []
1237+
if self.stderr:
1238+
register_and_append(self.stderr, select_POLLIN_POLLPRI)
1239+
fd2output[self.stderr.fileno()] = stderr = []
1240+
1241+
input_offset = 0
1242+
while fd2file:
1243+
try:
1244+
ready = poller.poll()
1245+
except select.error as e:
1246+
if e.args[0] == errno.EINTR:
1247+
continue
1248+
raise
1249+
1250+
# XXX Rewrite these to use non-blocking I/O on the
1251+
# file objects; they are no longer using C stdio!
1252+
1253+
for fd, mode in ready:
1254+
if mode & select.POLLOUT:
1255+
chunk = input[input_offset : input_offset + _PIPE_BUF]
1256+
input_offset += os.write(fd, chunk)
1257+
if input_offset >= len(input):
1258+
close_unregister_and_remove(fd)
1259+
elif mode & select_POLLIN_POLLPRI:
1260+
data = os.read(fd, 4096)
1261+
if not data:
1262+
close_unregister_and_remove(fd)
1263+
fd2output[fd].append(data)
1264+
else:
1265+
# Ignore hang up or errors.
1266+
close_unregister_and_remove(fd)
1267+
1268+
return (stdout, stderr)
1269+
1270+
1271+
def _communicate_with_select(self, input):
1272+
read_set = []
1273+
write_set = []
1274+
stdout = None # Return
1275+
stderr = None # Return
1276+
1277+
if self.stdin and input:
1278+
write_set.append(self.stdin)
11891279
if self.stdout:
11901280
read_set.append(self.stdout)
11911281
stdout = []
@@ -1206,10 +1296,7 @@ def _communicate(self, input):
12061296
# file objects; they are no longer using C stdio!
12071297

12081298
if self.stdin in wlist:
1209-
# When select has indicated that the file is writable,
1210-
# we can write up to PIPE_BUF bytes without risk
1211-
# blocking. POSIX defines PIPE_BUF >= 512
1212-
chunk = input[input_offset : input_offset + 512]
1299+
chunk = input[input_offset : input_offset + _PIPE_BUF]
12131300
bytes_written = os.write(self.stdin.fileno(), chunk)
12141301
input_offset += bytes_written
12151302
if input_offset >= len(input):
@@ -1230,25 +1317,9 @@ def _communicate(self, input):
12301317
read_set.remove(self.stderr)
12311318
stderr.append(data)
12321319

1233-
# All data exchanged. Translate lists into strings.
1234-
if stdout is not None:
1235-
stdout = b"".join(stdout)
1236-
if stderr is not None:
1237-
stderr = b"".join(stderr)
1238-
1239-
# Translate newlines, if requested.
1240-
# This also turns bytes into strings.
1241-
if self.universal_newlines:
1242-
if stdout is not None:
1243-
stdout = self._translate_newlines(stdout,
1244-
self.stdout.encoding)
1245-
if stderr is not None:
1246-
stderr = self._translate_newlines(stderr,
1247-
self.stderr.encoding)
1248-
1249-
self.wait()
12501320
return (stdout, stderr)
12511321

1322+
12521323
def send_signal(self, sig):
12531324
"""Send a signal to the process
12541325
"""

Lib/test/test_subprocess.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,8 +798,24 @@ def test_getoutput(self):
798798
if dir is not None:
799799
os.rmdir(dir)
800800

801+
802+
unit_tests = [ProcessTestCase, CommandTests]
803+
804+
if subprocess._has_poll:
805+
class ProcessTestCaseNoPoll(ProcessTestCase):
806+
def setUp(self):
807+
subprocess._has_poll = False
808+
ProcessTestCase.setUp(self)
809+
810+
def tearDown(self):
811+
subprocess._has_poll = True
812+
ProcessTestCase.tearDown(self)
813+
814+
unit_tests.append(ProcessTestCaseNoPoll)
815+
816+
801817
def test_main():
802-
support.run_unittest(ProcessTestCase, CommandTests)
818+
support.run_unittest(*unit_tests)
803819
support.reap_children()
804820

805821
if __name__ == "__main__":

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ C-API
3434
Library
3535
-------
3636

37+
- Issue #3392: The subprocess communicate() method no longer fails in select()
38+
when file descriptors are large; communicate() now uses poll() when possible.
39+
3740
- Issue #6369: Fix an RLE decompression bug in the binhex module.
3841

3942
- Issue #6344: Fixed a crash of mmap.read() when passed a negative argument.

0 commit comments

Comments
 (0)