This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Alternative async subprocesses (pep 3145)
Type: Stage: resolved
Components: Versions: Python 3.4
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: lukasz.langa, martin.panter, neologix, sbt, vstinner
Priority: normal Keywords:

Created on 2013-05-15 21:09 by sbt, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
asyncsubprocess.py sbt, 2013-05-15 21:09
sync_proc_asyncio.py vstinner, 2015-02-13 01:53
Messages (6)
msg189304 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2013-05-15 21:09
In the attached file is an experimental implementation of an AsyncPopen
class.  It should work for Python 3.3, 3.4 on Unix and Windows.  Unlike

    http://code.google.com/p/subprocdev

(see #1191964) this does not depend on using time.sleep() and polling.
It also uses the stdlib's _winapi instead of ctypes.

It lets one do Unix-style multiplexing of stdin, stdout, stderr on
Windows (by using overlapped IO).

Differences from normal Popen:

* File objects created for stdin/stdout/stderr using "...=PIPE" are
  non-blocking.

* There are no options for buffering or for universal line endings
  (or unicode).

* There is an extra method

      select(file_list, timeout=None) -> ready_file_list

  which can be used to wait for stdin, stdout, stderr to be ready.
  file_list should be a sublist of [self.stdin, self.stdout, self.stderr]
  with no None elements.  Note that there is no separation between
  "readers" and "writers" the way there is for the normal select()
  function.  On Unix this is implemented using select() or poll().

* On Windows close() can fail with BlockingIOError.  To prevent this
  one must use select() to wait for self.stdin to be ready.

As an example, communicate() can be reimplemented using select() as follows:

    def communicate(p, input):
        buf = memoryview(input)
        collected = collections.defaultdict(list)
        registered = [f for f in (p.stdin, p.stdout, p.stderr)
                      if f is not None]

        while registered:
            for f in p.select(registered):
                if f is p.stdin:
                    if not buf:
                        f.close()
                        registered.remove(f)
                    else:
                        n = f.write(buf)
                        if n is not None:
                            buf = buf[n:]
                elif f in (p.stdout, p.stderr):
                    s = f.read(8192)
                    if s == b'':
                        f.close()
                        registered.remove(f)
                    elif s is not None:
                        collected[f].append(s)
                else:
                    raise RuntimeError('should not get here')

        return (b''.join(collected[p.stdout]) if p.stdout else None,
                b''.join(collected[p.stderr]) if p.stderr else None)
msg189855 - (view) Author: Charles-François Natali (neologix) * (Python committer) Date: 2013-05-23 09:28
I'm not familiar with windows, but if I understand the patch
correctly, you can only select from a single subprocess at a time,
which is IMO an important limitation.

Also, the fact that close() can fail with BlockingIOError is really a
pain, and makes writing portable code complicated, but I know you
can't do anything about it ;-)

In short, that would be nice, but Windows seems to make it so
limited/complicated that I'm not sure that would be really useful, but
once again I don't use Windows, so I can't make a call.
msg235869 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-02-13 01:27
As Tulip and subprocdev, starting such project outside the Python stdlib may help to get feedback, find and fix bugs faster. What do you think?
msg235870 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-02-13 01:53
Subprocess support of asyncio has nice features and is efficient:

- async read from stdout and stderr
- async write into stdin
- async wait for the process exit
- async communicate()
- timeout on any async operation
- support running multiple child processes in parallel
- epoll, kqueue, devpoll or IOCP selector
- On POSIX, fast child watcher waiting for SIGCHLD signal

If possible, I would prefer to not write a second "async subprocess".

It's possible to write a blocking API on top of asyncio using loop.run_until_complete().

sync_proc_asyncio.py: incomplete proof-of-concept (no working select() method).
msg366794 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2020-04-19 17:21
Should this still be open given that PEP 3145 is withdrawn and asyncio subprocesses have been used in production for five Python releases now?
msg366820 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2020-04-20 12:52
Well, this issue is basically inactive for 5 years. It doesn't sound like a common feature request. I close it.
History
Date User Action Args
2022-04-11 14:57:45adminsetgithub: 62186
2020-04-20 12:52:05vstinnersetstatus: open -> closed
resolution: rejected
messages: + msg366820

stage: resolved
2020-04-19 17:21:35lukasz.langasetnosy: + lukasz.langa
messages: + msg366794
2015-02-13 01:53:32vstinnersetfiles: + sync_proc_asyncio.py

messages: + msg235870
2015-02-13 01:27:03vstinnersetmessages: + msg235869
2015-02-13 01:15:41martin.pantersetnosy: + martin.panter
2013-05-23 09:28:33neologixsetmessages: + msg189855
2013-05-17 21:44:54vstinnersetnosy: + vstinner
2013-05-15 22:39:05pitrousetnosy: + neologix
2013-05-15 21:09:59sbtcreate