20
20
import shutil
21
21
import signal
22
22
import sys
23
+ import typing
23
24
from argparse import ArgumentParser
24
25
from tempfile import TemporaryDirectory
25
26
26
27
27
- async def asyncio_stdin () -> asyncio .StreamReader :
28
- """Wrap sys.stdin in an asyncio StreamReader."""
29
- loop = asyncio .get_event_loop ()
30
- reader = asyncio .StreamReader ()
31
- protocol = asyncio .StreamReaderProtocol (reader )
32
- await loop .connect_read_pipe (lambda : protocol , sys .stdin )
33
- return reader
34
-
35
-
36
- async def asyncio_stdout (file = sys .stdout ) -> asyncio .StreamWriter :
37
- """Wrap an IO stream in an asyncio StreamWriter."""
38
- loop = asyncio .get_event_loop ()
39
- transport , protocol = await loop .connect_write_pipe (
40
- lambda : asyncio .streams .FlowControlMixin (loop = loop ),
41
- os .fdopen (file .fileno (), 'wb' ))
42
- return asyncio .streams .StreamWriter (transport , protocol , None , loop )
43
-
44
-
45
28
async def forward_f (prefix : bytes , f_in : asyncio .StreamReader ,
46
- f_out : asyncio . StreamWriter , cb = None ):
29
+ f_out : typing . BinaryIO , cb = None ):
47
30
"""Forward f_in to f_out with a prefix attached.
48
31
49
32
This function can optionally feed received lines to a callback function.
@@ -54,9 +37,9 @@ async def forward_f(prefix: bytes, f_in: asyncio.StreamReader,
54
37
break
55
38
if cb is not None :
56
39
cb (line )
57
- f_out .write (prefix )
58
- f_out .write (line )
59
- await f_out .drain ()
40
+ f_out .buffer . write (prefix )
41
+ f_out .buffer . write (line )
42
+ f_out .flush ()
60
43
61
44
62
45
async def forward_pipe (pipe_path : str , f_out : asyncio .StreamWriter ):
@@ -72,6 +55,7 @@ async def forward_pipe(pipe_path: str, f_out: asyncio.StreamWriter):
72
55
data = os .read (fd , 1024 )
73
56
if data :
74
57
f_out .write (data )
58
+ await f_out .drain ()
75
59
if not data :
76
60
await asyncio .sleep (0.1 )
77
61
except BlockingIOError :
@@ -80,13 +64,17 @@ async def forward_pipe(pipe_path: str, f_out: asyncio.StreamWriter):
80
64
81
65
async def forward_stdin (f_out : asyncio .StreamWriter ):
82
66
"""Forward stdin to f_out."""
83
- reader = await asyncio_stdin ()
67
+ loop = asyncio .get_event_loop ()
68
+ reader = asyncio .StreamReader ()
69
+ protocol = asyncio .StreamReaderProtocol (reader )
70
+ await loop .connect_read_pipe (lambda : protocol , sys .stdin )
84
71
while True :
85
72
line = await reader .readline ()
86
73
if not line :
87
74
# Exit on Ctrl-D (EOF).
88
75
sys .exit (0 )
89
76
f_out .write (line )
77
+ await f_out .drain ()
90
78
91
79
92
80
class Subprocess :
@@ -109,15 +97,9 @@ async def run(self):
109
97
stdout = asyncio .subprocess .PIPE ,
110
98
stderr = asyncio .subprocess .PIPE )
111
99
# Add the stdout and stderr processing to the event loop.
112
- asyncio .create_task (forward_f (
113
- self .tag ,
114
- self .p .stderr ,
115
- await asyncio_stdout (sys .stderr )))
116
- asyncio .create_task (forward_f (
117
- self .tag ,
118
- self .p .stdout ,
119
- await asyncio_stdout (sys .stdout ),
120
- cb = self ._check_output ))
100
+ asyncio .create_task (forward_f (self .tag , self .p .stderr , sys .stderr ))
101
+ asyncio .create_task (forward_f (self .tag , self .p .stdout , sys .stdout ,
102
+ cb = self ._check_output ))
121
103
122
104
async def send (self , message : str , expected_output : str = None , timeout : float = None ):
123
105
"""Send a message to a process and optionally wait for a response."""
@@ -206,14 +188,6 @@ async def main(args):
206
188
if pipe and not os .path .exists (pipe ):
207
189
os .mkfifo (pipe )
208
190
209
- def terminate (signum , frame ):
210
- admin .terminate ()
211
- bridge .terminate ()
212
- sys .exit (0 )
213
-
214
- signal .signal (signal .SIGINT , terminate )
215
- signal .signal (signal .SIGTERM , terminate )
216
-
217
191
admin , bridge = await asyncio .gather (
218
192
run_admin (
219
193
args .app_admin ,
@@ -235,6 +209,15 @@ def terminate(signum, frame):
235
209
passcode = args .passcode ,
236
210
))
237
211
212
+ def terminate ():
213
+ admin .terminate ()
214
+ bridge .terminate ()
215
+ sys .exit (0 )
216
+
217
+ loop = asyncio .get_event_loop ()
218
+ loop .add_signal_handler (signal .SIGINT , terminate )
219
+ loop .add_signal_handler (signal .SIGTERM , terminate )
220
+
238
221
# Wait a bit for apps to start.
239
222
await asyncio .sleep (1 )
240
223
0 commit comments