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

Skip to content

Commit ab85ff3

Browse files
committed
Issue #12591: Improve support of "universal newlines" in the subprocess
module: the piped streams can now be properly read from or written to. (this was broken due to the 2.x to 3.x transition; communicate() support is still sketchy)
1 parent e96ec68 commit ab85ff3

3 files changed

Lines changed: 52 additions & 17 deletions

File tree

Lib/subprocess.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def __init__(self, args, bufsize=0, executable=None,
721721
if p2cwrite != -1:
722722
self.stdin = io.open(p2cwrite, 'wb', bufsize)
723723
if self.universal_newlines:
724-
self.stdin = io.TextIOWrapper(self.stdin)
724+
self.stdin = io.TextIOWrapper(self.stdin, write_through=True)
725725
if c2pread != -1:
726726
self.stdout = io.open(c2pread, 'rb', bufsize)
727727
if universal_newlines:

Lib/test/test_subprocess.py

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -474,44 +474,75 @@ def test_writes_before_communicate(self):
474474
def test_universal_newlines(self):
475475
p = subprocess.Popen([sys.executable, "-c",
476476
'import sys,os;' + SETBINARY +
477-
'sys.stdout.write("line1\\n");'
477+
'sys.stdout.write(sys.stdin.readline());'
478478
'sys.stdout.flush();'
479479
'sys.stdout.write("line2\\n");'
480480
'sys.stdout.flush();'
481-
'sys.stdout.write("line3\\r\\n");'
481+
'sys.stdout.write(sys.stdin.read());'
482482
'sys.stdout.flush();'
483-
'sys.stdout.write("line4\\r");'
483+
'sys.stdout.write("line4\\n");'
484484
'sys.stdout.flush();'
485-
'sys.stdout.write("\\nline5");'
485+
'sys.stdout.write("line5\\r\\n");'
486486
'sys.stdout.flush();'
487-
'sys.stdout.write("\\nline6");'],
487+
'sys.stdout.write("line6\\r");'
488+
'sys.stdout.flush();'
489+
'sys.stdout.write("\\nline7");'
490+
'sys.stdout.flush();'
491+
'sys.stdout.write("\\nline8");'],
492+
stdin=subprocess.PIPE,
488493
stdout=subprocess.PIPE,
489494
universal_newlines=1)
495+
p.stdin.write("line1\n")
496+
self.assertEqual(p.stdout.readline(), "line1\n")
497+
p.stdin.write("line3\n")
498+
p.stdin.close()
490499
self.addCleanup(p.stdout.close)
491-
stdout = p.stdout.read()
492-
self.assertEqual(stdout, "line1\nline2\nline3\nline4\nline5\nline6")
500+
self.assertEqual(p.stdout.readline(),
501+
"line2\n")
502+
self.assertEqual(p.stdout.read(6),
503+
"line3\n")
504+
self.assertEqual(p.stdout.read(),
505+
"line4\nline5\nline6\nline7\nline8")
493506

494507
def test_universal_newlines_communicate(self):
495508
# universal newlines through communicate()
496509
p = subprocess.Popen([sys.executable, "-c",
497510
'import sys,os;' + SETBINARY +
498-
'sys.stdout.write("line1\\n");'
499-
'sys.stdout.flush();'
500511
'sys.stdout.write("line2\\n");'
501512
'sys.stdout.flush();'
502-
'sys.stdout.write("line3\\r\\n");'
513+
'sys.stdout.write("line4\\n");'
514+
'sys.stdout.flush();'
515+
'sys.stdout.write("line5\\r\\n");'
503516
'sys.stdout.flush();'
504-
'sys.stdout.write("line4\\r");'
517+
'sys.stdout.write("line6\\r");'
505518
'sys.stdout.flush();'
506-
'sys.stdout.write("\\nline5");'
519+
'sys.stdout.write("\\nline7");'
507520
'sys.stdout.flush();'
508-
'sys.stdout.write("\\nline6");'],
509-
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
521+
'sys.stdout.write("\\nline8");'],
522+
stderr=subprocess.PIPE,
523+
stdout=subprocess.PIPE,
510524
universal_newlines=1)
511525
self.addCleanup(p.stdout.close)
512526
self.addCleanup(p.stderr.close)
527+
# BUG: can't give a non-empty stdin because it breaks both the
528+
# select- and poll-based communicate() implementations.
513529
(stdout, stderr) = p.communicate()
514-
self.assertEqual(stdout, "line1\nline2\nline3\nline4\nline5\nline6")
530+
self.assertEqual(stdout,
531+
"line2\nline4\nline5\nline6\nline7\nline8")
532+
533+
def test_universal_newlines_communicate_stdin(self):
534+
# universal newlines through communicate(), with only stdin
535+
p = subprocess.Popen([sys.executable, "-c",
536+
'import sys,os;' + SETBINARY + '''\nif True:
537+
s = sys.stdin.readline()
538+
assert s == "line1\\n", repr(s)
539+
s = sys.stdin.read()
540+
assert s == "line3\\n", repr(s)
541+
'''],
542+
stdin=subprocess.PIPE,
543+
universal_newlines=1)
544+
(stdout, stderr) = p.communicate("line1\nline3\n")
545+
self.assertEqual(p.returncode, 0)
515546

516547
def test_no_leaking(self):
517548
# Make sure we leak no resources
@@ -1584,7 +1615,8 @@ def test_main():
15841615
ProcessTestCaseNoPoll,
15851616
HelperFunctionTests,
15861617
CommandsWithSpaces,
1587-
ContextManagerTests)
1618+
ContextManagerTests,
1619+
)
15881620

15891621
support.run_unittest(*unit_tests)
15901622
support.reap_children()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ Core and Builtins
3737
Library
3838
-------
3939

40+
- Issue #12591: Improve support of "universal newlines" in the subprocess
41+
module: the piped streams can now be properly read from or written to.
42+
4043
- Issue #12591: Allow io.TextIOWrapper to work with raw IO objects (without
4144
a read1() method), and add an undocumented *write_through* parameter to
4245
mandate unbuffered writes.

0 commit comments

Comments
 (0)