@@ -955,6 +955,54 @@ def test_close_fds_0_1_2(self):
955955 # all standard fds closed.
956956 self .check_close_std_fds ([0 , 1 , 2 ])
957957
958+ def test_remapping_std_fds (self ):
959+ # open up some temporary files
960+ temps = [mkstemp () for i in range (3 )]
961+ try :
962+ temp_fds = [fd for fd , fname in temps ]
963+
964+ # unlink the files -- we won't need to reopen them
965+ for fd , fname in temps :
966+ os .unlink (fname )
967+
968+ # write some data to what will become stdin, and rewind
969+ os .write (temp_fds [1 ], b"STDIN" )
970+ os .lseek (temp_fds [1 ], 0 , 0 )
971+
972+ # move the standard file descriptors out of the way
973+ saved_fds = [os .dup (fd ) for fd in range (3 )]
974+ try :
975+ # duplicate the file objects over the standard fd's
976+ for fd , temp_fd in enumerate (temp_fds ):
977+ os .dup2 (temp_fd , fd )
978+
979+ # now use those files in the "wrong" order, so that subprocess
980+ # has to rearrange them in the child
981+ p = subprocess .Popen ([sys .executable , "-c" ,
982+ 'import sys; got = sys.stdin.read();'
983+ 'sys.stdout.write("got %s"%got); sys.stderr.write("err")' ],
984+ stdin = temp_fds [1 ],
985+ stdout = temp_fds [2 ],
986+ stderr = temp_fds [0 ])
987+ p .wait ()
988+ finally :
989+ # restore the original fd's underneath sys.stdin, etc.
990+ for std , saved in enumerate (saved_fds ):
991+ os .dup2 (saved , std )
992+ os .close (saved )
993+
994+ for fd in temp_fds :
995+ os .lseek (fd , 0 , 0 )
996+
997+ out = os .read (temp_fds [2 ], 1024 )
998+ err = support .strip_python_stderr (os .read (temp_fds [0 ], 1024 ))
999+ self .assertEqual (out , b"got STDIN" )
1000+ self .assertEqual (err , b"err" )
1001+
1002+ finally :
1003+ for fd in temp_fds :
1004+ os .close (fd )
1005+
9581006 def test_surrogates_error_message (self ):
9591007 def prepare ():
9601008 raise ValueError ("surrogate:\uDCff " )
0 commit comments