@@ -1134,82 +1134,6 @@ def test_input(self):
11341134 sys .stdout = savestdout
11351135 fp .close ()
11361136
1137- @unittest .skipUnless (pty , "the pty and signal modules must be available" )
1138- def check_input_tty (self , prompt , terminal_input , stdio_encoding = None ):
1139- if not sys .stdin .isatty () or not sys .stdout .isatty ():
1140- self .skipTest ("stdin and stdout must be ttys" )
1141- r , w = os .pipe ()
1142- try :
1143- pid , fd = pty .fork ()
1144- except (OSError , AttributeError ) as e :
1145- os .close (r )
1146- os .close (w )
1147- self .skipTest ("pty.fork() raised {}" .format (e ))
1148- if pid == 0 :
1149- # Child
1150- try :
1151- # Make sure we don't get stuck if there's a problem
1152- signal .alarm (2 )
1153- os .close (r )
1154- # Check the error handlers are accounted for
1155- if stdio_encoding :
1156- sys .stdin = io .TextIOWrapper (sys .stdin .detach (),
1157- encoding = stdio_encoding ,
1158- errors = 'surrogateescape' )
1159- sys .stdout = io .TextIOWrapper (sys .stdout .detach (),
1160- encoding = stdio_encoding ,
1161- errors = 'replace' )
1162- with open (w , "w" ) as wpipe :
1163- print ("tty =" , sys .stdin .isatty () and sys .stdout .isatty (), file = wpipe )
1164- print (ascii (input (prompt )), file = wpipe )
1165- except :
1166- traceback .print_exc ()
1167- finally :
1168- # We don't want to return to unittest...
1169- os ._exit (0 )
1170- # Parent
1171- os .close (w )
1172- os .write (fd , terminal_input + b"\r \n " )
1173- # Get results from the pipe
1174- with open (r , "r" ) as rpipe :
1175- lines = []
1176- while True :
1177- line = rpipe .readline ().strip ()
1178- if line == "" :
1179- # The other end was closed => the child exited
1180- break
1181- lines .append (line )
1182- # Check the result was got and corresponds to the user's terminal input
1183- if len (lines ) != 2 :
1184- # Something went wrong, try to get at stderr
1185- with open (fd , "r" , encoding = "ascii" , errors = "ignore" ) as child_output :
1186- self .fail ("got %d lines in pipe but expected 2, child output was:\n %s"
1187- % (len (lines ), child_output .read ()))
1188- os .close (fd )
1189- # Check we did exercise the GNU readline path
1190- self .assertIn (lines [0 ], {'tty = True' , 'tty = False' })
1191- if lines [0 ] != 'tty = True' :
1192- self .skipTest ("standard IO in should have been a tty" )
1193- input_result = eval (lines [1 ]) # ascii() -> eval() roundtrip
1194- if stdio_encoding :
1195- expected = terminal_input .decode (stdio_encoding , 'surrogateescape' )
1196- else :
1197- expected = terminal_input .decode (sys .stdin .encoding ) # what else?
1198- self .assertEqual (input_result , expected )
1199-
1200- def test_input_tty (self ):
1201- # Test input() functionality when wired to a tty (the code path
1202- # is different and invokes GNU readline if available).
1203- self .check_input_tty ("prompt" , b"quux" )
1204-
1205- def test_input_tty_non_ascii (self ):
1206- # Check stdin/stdout encoding is used when invoking GNU readline
1207- self .check_input_tty ("prompté" , b"quux\xe9 " , "utf-8" )
1208-
1209- def test_input_tty_non_ascii_unicode_errors (self ):
1210- # Check stdin/stdout error handler is used when invoking GNU readline
1211- self .check_input_tty ("prompté" , b"quux\xe9 " , "ascii" )
1212-
12131137 # test_int(): see test_int.py for tests of built-in function int().
12141138
12151139 def test_repr (self ):
@@ -1564,6 +1488,116 @@ def test_construct_singletons(self):
15641488 self .assertRaises (TypeError , tp , 1 , 2 )
15651489 self .assertRaises (TypeError , tp , a = 1 , b = 2 )
15661490
1491+ @unittest .skipUnless (pty , "the pty and signal modules must be available" )
1492+ class PtyTests (unittest .TestCase ):
1493+ """Tests that use a pseudo terminal to guarantee stdin and stdout are
1494+ terminals in the test environment"""
1495+
1496+ def fork (self ):
1497+ try :
1498+ return pty .fork ()
1499+ except (OSError , AttributeError ) as e :
1500+ self .skipTest ("pty.fork() raised {}" .format (e ))
1501+
1502+ def check_input_tty (self , prompt , terminal_input , stdio_encoding = None ):
1503+ if not sys .stdin .isatty () or not sys .stdout .isatty ():
1504+ self .skipTest ("stdin and stdout must be ttys" )
1505+ r , w = os .pipe ()
1506+ try :
1507+ pid , fd = self .fork ()
1508+ except :
1509+ os .close (r )
1510+ os .close (w )
1511+ raise
1512+ if pid == 0 :
1513+ # Child
1514+ try :
1515+ # Make sure we don't get stuck if there's a problem
1516+ signal .alarm (2 )
1517+ os .close (r )
1518+ # Check the error handlers are accounted for
1519+ if stdio_encoding :
1520+ sys .stdin = io .TextIOWrapper (sys .stdin .detach (),
1521+ encoding = stdio_encoding ,
1522+ errors = 'surrogateescape' )
1523+ sys .stdout = io .TextIOWrapper (sys .stdout .detach (),
1524+ encoding = stdio_encoding ,
1525+ errors = 'replace' )
1526+ with open (w , "w" ) as wpipe :
1527+ print ("tty =" , sys .stdin .isatty () and sys .stdout .isatty (), file = wpipe )
1528+ print (ascii (input (prompt )), file = wpipe )
1529+ except :
1530+ traceback .print_exc ()
1531+ finally :
1532+ # We don't want to return to unittest...
1533+ os ._exit (0 )
1534+ # Parent
1535+ os .close (w )
1536+ os .write (fd , terminal_input + b"\r \n " )
1537+ # Get results from the pipe
1538+ with open (r , "r" ) as rpipe :
1539+ lines = []
1540+ while True :
1541+ line = rpipe .readline ().strip ()
1542+ if line == "" :
1543+ # The other end was closed => the child exited
1544+ break
1545+ lines .append (line )
1546+ # Check the result was got and corresponds to the user's terminal input
1547+ if len (lines ) != 2 :
1548+ # Something went wrong, try to get at stderr
1549+ with open (fd , "r" , encoding = "ascii" , errors = "ignore" ) as child_output :
1550+ self .fail ("got %d lines in pipe but expected 2, child output was:\n %s"
1551+ % (len (lines ), child_output .read ()))
1552+ os .close (fd )
1553+ # Check we did exercise the GNU readline path
1554+ self .assertIn (lines [0 ], {'tty = True' , 'tty = False' })
1555+ if lines [0 ] != 'tty = True' :
1556+ self .skipTest ("standard IO in should have been a tty" )
1557+ input_result = eval (lines [1 ]) # ascii() -> eval() roundtrip
1558+ if stdio_encoding :
1559+ expected = terminal_input .decode (stdio_encoding , 'surrogateescape' )
1560+ else :
1561+ expected = terminal_input .decode (sys .stdin .encoding ) # what else?
1562+ self .assertEqual (input_result , expected )
1563+
1564+ def test_input_tty (self ):
1565+ # Test input() functionality when wired to a tty (the code path
1566+ # is different and invokes GNU readline if available).
1567+ self .check_input_tty ("prompt" , b"quux" )
1568+
1569+ def test_input_tty_non_ascii (self ):
1570+ # Check stdin/stdout encoding is used when invoking GNU readline
1571+ self .check_input_tty ("prompté" , b"quux\xe9 " , "utf-8" )
1572+
1573+ def test_input_tty_non_ascii_unicode_errors (self ):
1574+ # Check stdin/stdout error handler is used when invoking GNU readline
1575+ self .check_input_tty ("prompté" , b"quux\xe9 " , "ascii" )
1576+
1577+ def test_input_no_stdout_fileno (self ):
1578+ # Issue #24402: If stdin is the original terminal but stdout.fileno()
1579+ # fails, do not use the original stdout file descriptor
1580+ pid , pty = self .fork ()
1581+ if pid : # Parent process
1582+ # Ideally this should read and write concurrently using select()
1583+ # or similar, to avoid the possibility of a deadlock.
1584+ os .write (pty , b"quux\r " )
1585+ _ , status = os .waitpid (pid , 0 )
1586+ output = os .read (pty , 3000 ).decode ("ascii" , "backslashreplace" )
1587+ os .close (pty )
1588+ self .assertEqual (status , 0 , output )
1589+ else : # Child process
1590+ try :
1591+ self .assertTrue (sys .stdin .isatty (), "stdin not a terminal" )
1592+ sys .stdout = io .StringIO () # Does not support fileno()
1593+ input ("prompt" )
1594+ self .assertEqual (sys .stdout .getvalue (), "prompt" )
1595+ os ._exit (0 ) # Success!
1596+ except :
1597+ sys .excepthook (* sys .exc_info ())
1598+ finally :
1599+ os ._exit (1 ) # Failure
1600+
15671601class TestSorted (unittest .TestCase ):
15681602
15691603 def test_basic (self ):
0 commit comments