| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 588b761 commit 7aca9cb
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,13 +1,5 @@ | |||
| 1 | 1 | prefix pseudo-tty | |
| 2 | 2 | ||
| 3 | - [$system==aix] | ||
| 4 | - # being investigated under https://github.com/nodejs/node/issues/9728 | ||
| 5 | - test-tty-wrap : FAIL, PASS | ||
| 6 | - # https://github.com/nodejs/build/issues/1820#issuecomment-505998851 | ||
| 7 | - # https://github.com/nodejs/node/pull/28469 | ||
| 8 | - console-dumb-tty: SKIP | ||
| 9 | - test-fatal-error: SKIP | ||
| 10 | - | ||
| 11 | 3 | [$system==solaris] | |
| 12 | 4 | # https://github.com/nodejs/node/pull/16225 - `ioctl(fd, TIOCGWINSZ)` seems | |
| 13 | 5 | # to fail with EINVAL on SmartOS when `fd` is a pty from python's pty module. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,98 @@ | |||
| 1 | + import errno | ||
| 2 | + import os | ||
| 3 | + import pty | ||
| 4 | + import select | ||
| 5 | + import signal | ||
| 6 | + import sys | ||
| 7 | + import termios | ||
| 8 | + | ||
| 9 | + STDIN = 0 | ||
| 10 | + STDOUT = 1 | ||
| 11 | + STDERR = 2 | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + def pipe(sfd, dfd): | ||
| 15 | + try: | ||
| 16 | + data = os.read(sfd, 256) | ||
| 17 | + except OSError as e: | ||
| 18 | + if e.errno != errno.EIO: | ||
| 19 | + raise | ||
| 20 | + return True # EOF | ||
| 21 | + | ||
| 22 | + if not data: | ||
| 23 | + return True # EOF | ||
| 24 | + | ||
| 25 | + if dfd == STDOUT: | ||
| 26 | + # Work around platform quirks. Some platforms echo ^D as \x04 | ||
| 27 | + # (AIX, BSDs) and some don't (Linux). | ||
| 28 | + filt = lambda c: ord(c) > 31 or c in '\t\n\r\f' | ||
| 29 | + data = filter(filt, data) | ||
| 30 | + | ||
| 31 | + while data: | ||
| 32 | + try: | ||
| 33 | + n = os.write(dfd, data) | ||
| 34 | + except OSError as e: | ||
| 35 | + if e.errno != errno.EIO: | ||
| 36 | + raise | ||
| 37 | + return True # EOF | ||
| 38 | + data = data[n:] | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + if __name__ == '__main__': | ||
| 42 | + argv = sys.argv[1:] | ||
| 43 | + | ||
| 44 | + # Make select() interruptable by SIGCHLD. | ||
| 45 | + signal.signal(signal.SIGCHLD, lambda nr, _: None) | ||
| 46 | + | ||
| 47 | + master_fd, slave_fd = pty.openpty() | ||
| 48 | + assert master_fd > STDIN | ||
| 49 | + | ||
| 50 | + mode = termios.tcgetattr(slave_fd) | ||
| 51 | + # Don't translate \n to \r\n. | ||
| 52 | + mode[1] = mode[1] & ~termios.ONLCR # oflag | ||
| 53 | + # Disable ECHOCTL. It's a BSD-ism that echoes e.g. \x04 as ^D but it | ||
| 54 | + # doesn't work on platforms like AIX and Linux. I checked Linux's tty | ||
| 55 | + # driver and it's a no-op, the driver is just oblivious to the flag. | ||
| 56 | + mode[3] = mode[3] & ~termios.ECHOCTL # lflag | ||
| 57 | + termios.tcsetattr(slave_fd, termios.TCSANOW, mode) | ||
| 58 | + | ||
| 59 | + pid = os.fork() | ||
| 60 | + if not pid: | ||
| 61 | + os.setsid() | ||
| 62 | + os.close(master_fd) | ||
| 63 | + | ||
| 64 | + # Ensure the pty is a controlling tty. | ||
| 65 | + name = os.ttyname(slave_fd) | ||
| 66 | + fd = os.open(name, os.O_RDWR) | ||
| 67 | + os.dup2(fd, slave_fd) | ||
| 68 | + os.close(fd) | ||
| 69 | + | ||
| 70 | + os.dup2(slave_fd, STDIN) | ||
| 71 | + os.dup2(slave_fd, STDOUT) | ||
| 72 | + os.dup2(slave_fd, STDERR) | ||
| 73 | + | ||
| 74 | + if slave_fd > STDERR: | ||
| 75 | + os.close(slave_fd) | ||
| 76 | + | ||
| 77 | + os.execve(argv[0], argv, os.environ) | ||
| 78 | + raise Exception('unreachable') | ||
| 79 | + | ||
| 80 | + os.close(slave_fd) | ||
| 81 | + | ||
| 82 | + fds = [STDIN, master_fd] | ||
| 83 | + while fds: | ||
| 84 | + try: | ||
| 85 | + rfds, _, _ = select.select(fds, [], []) | ||
| 86 | + except select.error as e: | ||
| 87 | + if e[0] != errno.EINTR: | ||
| 88 | + raise | ||
| 89 | + if pid == os.waitpid(pid, os.WNOHANG)[0]: | ||
| 90 | + break | ||
| 91 | + | ||
| 92 | + if STDIN in rfds: | ||
| 93 | + if pipe(STDIN, master_fd): | ||
| 94 | + fds.remove(STDIN) | ||
| 95 | + | ||
| 96 | + if master_fd in rfds: | ||
| 97 | + if pipe(master_fd, STDOUT): | ||
| 98 | + break | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1 +1,2 @@ | |||
| 1 | 1 | Hello! | |
| 2 | + | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1 +1,2 @@ | |||
| 1 | + Hello! | ||
| 1 | 2 | <Buffer 48 65 6c 6c 6f 21 0a> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,11 +27,13 @@ | |||
| 27 | 27 | ||
| 28 | 28 | import test | |
| 29 | 29 | import os | |
| 30 | - from os.path import join, exists, basename, isdir | ||
| 30 | + from os.path import join, exists, basename, dirname, isdir | ||
| 31 | 31 | import re | |
| 32 | + import sys | ||
| 32 | 33 | import utils | |
| 33 | 34 | ||
| 34 | 35 | FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)") | |
| 36 | + PTY_HELPER = join(dirname(__file__), 'pty_helper.py') | ||
| 35 | 37 | ||
| 36 | 38 | class TTYTestCase(test.TestCase): | |
| 37 | 39 | ||
@@ -105,17 +107,18 @@ def GetSource(self): | |||
| 105 | 107 | + open(self.expected).read()) | |
| 106 | 108 | ||
| 107 | 109 | def RunCommand(self, command, env): | |
| 108 | - input = None | ||
| 110 | + fd = None | ||
| 109 | 111 | if self.input is not None and exists(self.input): | |
| 110 | - input = open(self.input).read() | ||
| 112 | + fd = os.open(self.input, os.O_RDONLY) | ||
| 111 | 113 | full_command = self.context.processor(command) | |
| 114 | + full_command = [sys.executable, PTY_HELPER] + full_command | ||
| 112 | 115 | output = test.Execute(full_command, | |
| 113 | 116 | self.context, | |
| 114 | 117 | self.context.GetTimeout(self.mode), | |
| 115 | 118 | env, | |
| 116 | - faketty=True, | ||
| 117 | - input=input) | ||
| 118 | - self.Cleanup() | ||
| 119 | + stdin=fd) | ||
| 120 | + if fd is not None: | ||
| 121 | + os.close(fd) | ||
| 119 | 122 | return test.TestOutput(self, | |
| 120 | 123 | full_command, | |
| 121 | 124 | output, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -640,15 +640,10 @@ def RunProcess(context, timeout, args, **rest): | |||
| 640 | 640 | prev_error_mode = Win32SetErrorMode(error_mode); | |
| 641 | 641 | Win32SetErrorMode(error_mode | prev_error_mode); | |
| 642 | 642 | ||
| 643 | - faketty = rest.pop('faketty', False) | ||
| 644 | - pty_out = rest.pop('pty_out') | ||
| 645 | - | ||
| 646 | 643 | process = subprocess.Popen( | |
| 647 | 644 | args = popen_args, | |
| 648 | 645 | **rest | |
| 649 | 646 | ) | |
| 650 | - if faketty: | ||
| 651 | - os.close(rest['stdout']) | ||
| 652 | 647 | if utils.IsWindows() and context.suppress_dialogs and prev_error_mode != SEM_INVALID_VALUE: | |
| 653 | 648 | Win32SetErrorMode(prev_error_mode) | |
| 654 | 649 | # Compute the end time - if the process crosses this limit we | |
@@ -660,28 +655,6 @@ def RunProcess(context, timeout, args, **rest): | |||
| 660 | 655 | # loop and keep track of whether or not it times out. | |
| 661 | 656 | exit_code = None | |
| 662 | 657 | sleep_time = INITIAL_SLEEP_TIME | |
| 663 | - output = '' | ||
| 664 | - if faketty: | ||
| 665 | - while True: | ||
| 666 | - if time.time() >= end_time: | ||
| 667 | - # Kill the process and wait for it to exit. | ||
| 668 | - KillTimedOutProcess(context, process.pid) | ||
| 669 | - exit_code = process.wait() | ||
| 670 | - timed_out = True | ||
| 671 | - break | ||
| 672 | - | ||
| 673 | - # source: http://stackoverflow.com/a/12471855/1903116 | ||
| 674 | - # related: http://stackoverflow.com/q/11165521/1903116 | ||
| 675 | - try: | ||
| 676 | - data = os.read(pty_out, 9999) | ||
| 677 | - except OSError as e: | ||
| 678 | - if e.errno != errno.EIO: | ||
| 679 | - raise | ||
| 680 | - break # EIO means EOF on some systems | ||
| 681 | - else: | ||
| 682 | - if not data: # EOF | ||
| 683 | - break | ||
| 684 | - output += data | ||
| 685 | 658 | ||
| 686 | 659 | while exit_code is None: | |
| 687 | 660 | if (not end_time is None) and (time.time() >= end_time): | |
@@ -695,7 +668,7 @@ def RunProcess(context, timeout, args, **rest): | |||
| 695 | 668 | sleep_time = sleep_time * SLEEP_TIME_FACTOR | |
| 696 | 669 | if sleep_time > MAX_SLEEP_TIME: | |
| 697 | 670 | sleep_time = MAX_SLEEP_TIME | |
| 698 | - return (process, exit_code, timed_out, output) | ||
| 671 | + return (process, exit_code, timed_out) | ||
| 699 | 672 | ||
| 700 | 673 | ||
| 701 | 674 | def PrintError(str): | |
@@ -717,29 +690,12 @@ def CheckedUnlink(name): | |||
| 717 | 690 | PrintError("os.unlink() " + str(e)) | |
| 718 | 691 | break | |
| 719 | 692 | ||
| 720 | - def Execute(args, context, timeout=None, env={}, faketty=False, disable_core_files=False, input=None): | ||
| 721 | - if faketty: | ||
| 722 | - import pty | ||
| 723 | - (out_master, fd_out) = pty.openpty() | ||
| 724 | - fd_in = fd_err = fd_out | ||
| 725 | - pty_out = out_master | ||
| 726 | - | ||
| 727 | - if input is not None: | ||
| 728 | - # Before writing input data, disable echo so the input doesn't show | ||
| 729 | - # up as part of the output. | ||
| 730 | - import termios | ||
| 731 | - attr = termios.tcgetattr(fd_in) | ||
| 732 | - attr[3] = attr[3] & ~termios.ECHO | ||
| 733 | - termios.tcsetattr(fd_in, termios.TCSADRAIN, attr) | ||
| 734 | - | ||
| 735 | - os.write(pty_out, input) | ||
| 736 | - os.write(pty_out, '\x04') # End-of-file marker (Ctrl+D) | ||
| 737 | - else: | ||
| 738 | - (fd_out, outname) = tempfile.mkstemp() | ||
| 739 | - (fd_err, errname) = tempfile.mkstemp() | ||
| 740 | - fd_in = 0 | ||
| 741 | - pty_out = None | ||
| 693 | + def Execute(args, context, timeout=None, env=None, disable_core_files=False, stdin=None): | ||
| 694 | + (fd_out, outname) = tempfile.mkstemp() | ||
| 695 | + (fd_err, errname) = tempfile.mkstemp() | ||
| 742 | 696 | ||
| 697 | + if env is None: | ||
| 698 | + env = {} | ||
| 743 | 699 | env_copy = os.environ.copy() | |
| 744 | 700 | ||
| 745 | 701 | # Remove NODE_PATH | |
@@ -758,28 +714,22 @@ def disableCoreFiles(): | |||
| 758 | 714 | resource.setrlimit(resource.RLIMIT_CORE, (0,0)) | |
| 759 | 715 | preexec_fn = disableCoreFiles | |
| 760 | 716 | ||
| 761 | - (process, exit_code, timed_out, output) = RunProcess( | ||
| 717 | + (process, exit_code, timed_out) = RunProcess( | ||
| 762 | 718 | context, | |
| 763 | 719 | timeout, | |
| 764 | 720 | args = args, | |
| 765 | - stdin = fd_in, | ||
| 721 | + stdin = stdin, | ||
| 766 | 722 | stdout = fd_out, | |
| 767 | 723 | stderr = fd_err, | |
| 768 | 724 | env = env_copy, | |
| 769 | - faketty = faketty, | ||
| 770 | - pty_out = pty_out, | ||
| 771 | 725 | preexec_fn = preexec_fn | |
| 772 | 726 | ) | |
| 773 | - if faketty: | ||
| 774 | - os.close(out_master) | ||
| 775 | - errors = '' | ||
| 776 | - else: | ||
| 777 | - os.close(fd_out) | ||
| 778 | - os.close(fd_err) | ||
| 779 | - output = file(outname).read() | ||
| 780 | - errors = file(errname).read() | ||
| 781 | - CheckedUnlink(outname) | ||
| 782 | - CheckedUnlink(errname) | ||
| 727 | + os.close(fd_out) | ||
| 728 | + os.close(fd_err) | ||
| 729 | + output = open(outname).read() | ||
| 730 | + errors = open(errname).read() | ||
| 731 | + CheckedUnlink(outname) | ||
| 732 | + CheckedUnlink(errname) | ||
| 783 | 733 | ||
| 784 | 734 | return CommandOutput(exit_code, timed_out, output, errors) | |
| 785 | 735 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments