|
5 | 5 | import_module('termios')
|
6 | 6 |
|
7 | 7 | import errno
|
8 |
| -import pty |
9 | 8 | import os
|
| 9 | +import pty |
| 10 | +import tty |
10 | 11 | import sys
|
11 | 12 | import select
|
12 | 13 | import signal
|
@@ -123,12 +124,6 @@ def handle_sig(self, sig, frame):
|
123 | 124 |
|
124 | 125 | @staticmethod
|
125 | 126 | def handle_sighup(signum, frame):
|
126 |
| - # bpo-38547: if the process is the session leader, os.close(master_fd) |
127 |
| - # of "master_fd, slave_name = pty.master_open()" raises SIGHUP |
128 |
| - # signal: just ignore the signal. |
129 |
| - # |
130 |
| - # NOTE: the above comment is from an older version of the test; |
131 |
| - # master_open() is not being used anymore. |
132 | 127 | pass
|
133 | 128 |
|
134 | 129 | @expectedFailureIfStdinIsTTY
|
@@ -190,13 +185,6 @@ def test_openpty(self):
|
190 | 185 | self.assertEqual(_get_term_winsz(slave_fd), new_stdin_winsz,
|
191 | 186 | "openpty() failed to set slave window size")
|
192 | 187 |
|
193 |
| - # Solaris requires reading the fd before anything is returned. |
194 |
| - # My guess is that since we open and close the slave fd |
195 |
| - # in master_open(), we need to read the EOF. |
196 |
| - # |
197 |
| - # NOTE: the above comment is from an older version of the test; |
198 |
| - # master_open() is not being used anymore. |
199 |
| - |
200 | 188 | # Ensure the fd is non-blocking in case there's nothing to read.
|
201 | 189 | blocking = os.get_blocking(master_fd)
|
202 | 190 | try:
|
@@ -324,22 +312,40 @@ def test_master_read(self):
|
324 | 312 |
|
325 | 313 | self.assertEqual(data, b"")
|
326 | 314 |
|
| 315 | + def test_spawn_doesnt_hang(self): |
| 316 | + pty.spawn([sys.executable, '-c', 'print("hi there")']) |
| 317 | + |
327 | 318 | class SmallPtyTests(unittest.TestCase):
|
328 | 319 | """These tests don't spawn children or hang."""
|
329 | 320 |
|
330 | 321 | def setUp(self):
|
331 | 322 | self.orig_stdin_fileno = pty.STDIN_FILENO
|
332 | 323 | self.orig_stdout_fileno = pty.STDOUT_FILENO
|
| 324 | + self.orig_pty_close = pty.close |
| 325 | + self.orig_pty__copy = pty._copy |
| 326 | + self.orig_pty_fork = pty.fork |
333 | 327 | self.orig_pty_select = pty.select
|
| 328 | + self.orig_pty_setraw = pty.setraw |
| 329 | + self.orig_pty_tcgetattr = pty.tcgetattr |
| 330 | + self.orig_pty_tcsetattr = pty.tcsetattr |
| 331 | + self.orig_pty_waitpid = pty.waitpid |
334 | 332 | self.fds = [] # A list of file descriptors to close.
|
335 | 333 | self.files = []
|
336 | 334 | self.select_rfds_lengths = []
|
337 | 335 | self.select_rfds_results = []
|
| 336 | + self.tcsetattr_mode_setting = None |
338 | 337 |
|
339 | 338 | def tearDown(self):
|
340 | 339 | pty.STDIN_FILENO = self.orig_stdin_fileno
|
341 | 340 | pty.STDOUT_FILENO = self.orig_stdout_fileno
|
| 341 | + pty.close = self.orig_pty_close |
| 342 | + pty._copy = self.orig_pty__copy |
| 343 | + pty.fork = self.orig_pty_fork |
342 | 344 | pty.select = self.orig_pty_select
|
| 345 | + pty.setraw = self.orig_pty_setraw |
| 346 | + pty.tcgetattr = self.orig_pty_tcgetattr |
| 347 | + pty.tcsetattr = self.orig_pty_tcsetattr |
| 348 | + pty.waitpid = self.orig_pty_waitpid |
343 | 349 | for file in self.files:
|
344 | 350 | try:
|
345 | 351 | file.close()
|
@@ -367,6 +373,14 @@ def _mock_select(self, rfds, wfds, xfds, timeout=0):
|
367 | 373 | self.assertEqual(self.select_rfds_lengths.pop(0), len(rfds))
|
368 | 374 | return self.select_rfds_results.pop(0), [], []
|
369 | 375 |
|
| 376 | + def _make_mock_fork(self, pid): |
| 377 | + def mock_fork(): |
| 378 | + return (pid, 12) |
| 379 | + return mock_fork |
| 380 | + |
| 381 | + def _mock_tcsetattr(self, fileno, opt, mode): |
| 382 | + self.tcsetattr_mode_setting = mode |
| 383 | + |
370 | 384 | def test__copy_to_each(self):
|
371 | 385 | """Test the normal data case on both master_fd and stdin."""
|
372 | 386 | read_from_stdout_fd, mock_stdout_fd = self._pipe()
|
@@ -407,20 +421,41 @@ def test__copy_eof_on_all(self):
|
407 | 421 | socketpair[1].close()
|
408 | 422 | os.close(write_to_stdin_fd)
|
409 | 423 |
|
410 |
| - # Expect two select calls, the last one will cause IndexError |
411 | 424 | pty.select = self._mock_select
|
412 | 425 | self.select_rfds_lengths.append(2)
|
413 | 426 | self.select_rfds_results.append([mock_stdin_fd, masters[0]])
|
414 | 427 | # We expect that both fds were removed from the fds list as they
|
415 | 428 | # both encountered an EOF before the second select call.
|
416 | 429 | self.select_rfds_lengths.append(0)
|
417 | 430 |
|
418 |
| - with self.assertRaises(IndexError): |
419 |
| - pty._copy(masters[0]) |
| 431 | + # We expect the function to return without error. |
| 432 | + self.assertEqual(pty._copy(masters[0]), None) |
| 433 | + |
| 434 | + def test__restore_tty_mode_normal_return(self): |
| 435 | + """Test that spawn resets the tty mode no when _copy returns normally.""" |
| 436 | + |
| 437 | + # PID 1 is returned from mocked fork to run the parent branch |
| 438 | + # of code |
| 439 | + pty.fork = self._make_mock_fork(1) |
| 440 | + |
| 441 | + status_sentinel = object() |
| 442 | + pty.waitpid = lambda _1, _2: [None, status_sentinel] |
| 443 | + pty.close = lambda _: None |
| 444 | + |
| 445 | + pty._copy = lambda _1, _2, _3: None |
| 446 | + |
| 447 | + mode_sentinel = object() |
| 448 | + pty.tcgetattr = lambda fd: mode_sentinel |
| 449 | + pty.tcsetattr = self._mock_tcsetattr |
| 450 | + pty.setraw = lambda _: None |
| 451 | + |
| 452 | + self.assertEqual(pty.spawn([]), status_sentinel, "pty.waitpid process status not returned by pty.spawn") |
| 453 | + self.assertEqual(self.tcsetattr_mode_setting, mode_sentinel, "pty.tcsetattr not called with original mode value") |
420 | 454 |
|
421 | 455 |
|
422 | 456 | def tearDownModule():
|
423 | 457 | reap_children()
|
424 | 458 |
|
| 459 | + |
425 | 460 | if __name__ == "__main__":
|
426 | 461 | unittest.main()
|
0 commit comments