|
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