|
23 | 23 | """ |
24 | 24 | import ast |
25 | 25 | import code |
| 26 | +import contextlib |
26 | 27 | import inspect |
27 | | -import subprocess |
| 28 | +import os |
| 29 | +import select |
28 | 30 | import shlex |
| 31 | +import subprocess |
29 | 32 | import sys |
30 | 33 | import traceback |
31 | | -from typing import Tuple, Union, IO |
| 34 | +from typing import Generator, Tuple, Union, IO |
32 | 35 |
|
33 | 36 |
|
34 | 37 | def cli() -> None: |
@@ -56,38 +59,46 @@ def cli() -> None: |
56 | 59 | run_zxpy(filename, module) |
57 | 60 |
|
58 | 61 |
|
59 | | -def create_shell_process(command: str) -> IO[bytes]: |
60 | | - """Creates a shell process, returning its stdout to read data from.""" |
| 62 | +@contextlib.contextmanager |
| 63 | +def create_shell_process(command: str) -> Generator[IO[bytes], None, None]: |
| 64 | + """Creates a shell process, yielding its stdout to read data from.""" |
61 | 65 | process = subprocess.Popen( |
62 | 66 | command, |
63 | 67 | stdout=subprocess.PIPE, |
64 | 68 | stderr=subprocess.STDOUT, |
65 | 69 | shell=True, |
66 | 70 | ) |
| 71 | + assert process.stdout is not None |
| 72 | + yield process.stdout |
| 73 | + |
67 | 74 | process.wait() |
| 75 | + process.stdout.close() |
68 | 76 | if process.returncode != 0: |
69 | 77 | raise ChildProcessError(process.returncode) |
70 | 78 |
|
71 | | - assert process.stdout is not None |
72 | | - return process.stdout |
73 | | - |
74 | 79 |
|
75 | 80 | def run_shell(command: str) -> str: |
76 | 81 | """This is indirectly run when doing ~'...'""" |
77 | | - stdout = create_shell_process(command) |
78 | | - return stdout.read().decode() |
| 82 | + with create_shell_process(command) as stdout: |
| 83 | + output = stdout.read().decode() |
| 84 | + return output |
79 | 85 |
|
80 | 86 |
|
81 | 87 | def run_shell_print(command: str) -> None: |
82 | 88 | """Version of `run_shell` that prints out the response instead of returning a string.""" |
83 | | - stdout = create_shell_process(command) |
84 | | - while True: |
85 | | - char = stdout.read(1) |
86 | | - if not char: |
87 | | - break |
88 | | - |
89 | | - sys.stdout.buffer.write(char) |
90 | | - sys.stdout.flush() |
| 89 | + with create_shell_process(command) as stdout: |
| 90 | + os.set_blocking(stdout.fileno(), False) |
| 91 | + |
| 92 | + poll = select.poll() |
| 93 | + poll.register(stdout, select.POLLIN) |
| 94 | + while True: |
| 95 | + [(_, code)] = poll.poll() |
| 96 | + if code == select.POLLHUP: |
| 97 | + break |
| 98 | + |
| 99 | + text = stdout.read() |
| 100 | + sys.stdout.buffer.write(text) |
| 101 | + sys.stdout.buffer.flush() |
91 | 102 |
|
92 | 103 |
|
93 | 104 | def run_shell_alternate(command: str) -> Tuple[str, str, int]: |
|
0 commit comments