On 24 January 2014 11:26, Victor Stinner <[email protected]> wrote:
> Hi,
>
> I now understood that protocols should be avoided, they are low-level,
> stream reader/writer should be preferred (high-level API). Ok, but
> StreamReader and StreamWriter cannot be used with subprocess because
> it's not possible to choose the protocols used for stdin, stdout and
> stderr: WriteSubprocessPipeProto and ReadSubprocessPipeProto are used.
>
IMHO, loop.subprocess_exec should take up to 3 protocol factories, not just
one, for this precise reason. As it is now, this API is really ugly IMHO.
I didn't want to sound negative, and I was just going to ignore this issue.
But if you are going to redesign tulip subprocess APIs, then let me chip
in what it should look like, at high level:
1. Get rid of loop.subprocess_xxx() completely. We don't need it (but
see 3.). Instead use subprocess.Popen as normal.
2. Create utility functions streams.connect_read_pipe() and
streams.connect_write_pipe(), as found in examples/child_process.py.
3. Create a utility function (or loop method) that takes a Popen object
and returns a future that completes when the process has terminated (using
SIGCHLD and whatnot).
I haven't considered whether the above design works in Windows or not. I
just don't have enough Windows experience. But I think it is much cleaner,
since you can use the subprocess.Popen API that we are all used to, and
just have special care if you need to redirect stdxxx. Getting a
notification when a process is terminated is an orthogonal issue, you don't
need to put this into the subprocess function.
So here's a hypothetical example:
def program_exited(future):
print("program with pid {} exited with code {}".format(future.proc.pid,
future.exitcode))
@asyncio.coroutine
def doit(loop)
proc = subprocess.Popen("cat", stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
stdin = asyncio.connect_write_pipe(proc.stdout, loop=loop) # returns a
StreamWriter
stdout = asyncio.connect_read_pipe(proc.stdout, loop=loop) # returns a
StreamReader
completed = loop.watch_subprocess(proc)
completed.add_done_callback(program_exited)
stdin.writelines([b"hello", b"world"])
while 1:
line = yield from stdout.readline()
if not line:
break
print(line)
Notice that the programmer doesn't need to be aware of protocols or
transports. If protocols and transports are a lower level layer, then we
need a higher level API that completely hides them.
Just my 2 cents.
--
Gustavo J. A. M. Carneiro
Gambit Research LLC
"The universe is always one step beyond logic." -- Frank Herbert