| 92 | |
| 93 | |
| 94 | def __run_subprocess_tty(args, env=None, cwd=None, timeout=None): |
| 95 | import pty |
| 96 | mo, so = pty.openpty() |
| 97 | me, se = pty.openpty() |
| 98 | p = subprocess.Popen(args, stdout=so, stderr=se, env=env, cwd=cwd) |
| 99 | for fd in [so, se]: |
| 100 | os.close(fd) |
| 101 | |
| 102 | select_timeout = 0.04 # seconds |
| 103 | readable = [mo, me] |
| 104 | result = {mo: b'', me: b''} |
| 105 | try: |
| 106 | start_time = time.monotonic() |
| 107 | while readable: |
| 108 | ready, _, _ = select.select(readable, [], [], select_timeout) |
| 109 | for fd in ready: |
| 110 | try: |
| 111 | data = os.read(fd, 512) |
| 112 | except OSError as e: |
| 113 | if e.errno != errno.EIO: |
| 114 | raise |
| 115 | # EIO means EOF on some systems |
| 116 | readable.remove(fd) |
| 117 | else: |
| 118 | if not data: # EOF |
| 119 | readable.remove(fd) |
| 120 | result[fd] += data |
| 121 | if timeout is not None and (time.monotonic() - start_time): |
| 122 | break |
| 123 | finally: |
| 124 | for fd in [mo, me]: |
| 125 | os.close(fd) |
| 126 | if p.poll() is None: |
| 127 | p.kill() |
| 128 | return_code = p.wait() |
| 129 | |
| 130 | stdout = result[mo] |
| 131 | stderr = result[me] |
| 132 | return return_code, stdout, stderr |
| 133 | |
| 134 | |
| 135 | def __run_subprocess(args, env=None, cwd=None, timeout=None): |