<div class="gmail_extra"><div class="gmail_quote">On Tue, Sep 15, 2009 at 9:24 PM,  <span dir="ltr"><<a href="mailto:exarkun@twistedmatrix.com" target="_blank">exarkun@twistedmatrix.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">

<div class=""><div class="h5">On 04:25 pm, <a href="mailto:eric.pruitt@gmail.com" target="_blank">eric.pruitt@gmail.com</a> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
I'm bumping this PEP again in hopes of getting some feedback.<br></blockquote></div></div></blockquote><div><br></div><div>This is useful, indeed. ActiveState recipe for this has 10 votes, which is high for ActiveState (and such hardcore topic FWIW).</div>

<div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div class=""><div class="h5"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">


On Tue, Sep 8, 2009 at 23:52, Eric Pruitt <<a href="mailto:eric.pruitt@gmail.com" target="_blank">eric.pruitt@gmail.com</a>> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
PEP: 3145<br>
Title: Asynchronous I/O For subprocess.Popen<br>
Author: (James) Eric Pruitt, Charles R. McCreary, Josiah Carlson<br>
Type: Standards Track<br>
Content-Type: text/plain<br>
Created: 04-Aug-2009<br>
Python-Version: 3.2<br>
<br>
Abstract:<br>
<br>
   In its present form, the subprocess.Popen implementation is prone to<br>
   dead-locking and blocking of the parent Python script while waiting on data<br>
   from the child process.<br>
<br>
Motivation:<br>
<br>
   A search for "python asynchronous subprocess" will turn up numerous<br>
   accounts of people wanting to execute a child process and communicate with<br>
   it from time to time reading only the data that is available instead of<br>
   blocking to wait for the program to produce data [1] [2] [3].  The current<br>
   behavior of the subprocess module is that when a user sends or receives<br>
   data via the stdin, stderr and stdout file objects, dead locks are common<br>
   and documented [4] [5].  While communicate can be used to alleviate some of<br>
   the buffering issues, it will still cause the parent process to block while<br>
   attempting to read data when none is available to be read from the child<br>
   process.<br>
<br>
Rationale:<br>
<br>
   There is a documented need for asynchronous, non-blocking functionality in<br>
   subprocess.Popen [6] [7] [2] [3].  Inclusion of the code would improve the<br>
   utility of the Python standard library that can be used on Unix based and<br>
   Windows builds of Python.  Practically every I/O object in Python has a<br>
   file-like wrapper of some sort.  Sockets already act as such and for<br>
   strings there is StringIO.  Popen can be made to act like a file by simply<br>
   using the methods attached the the subprocess.Popen.stderr, stdout and<br>
   stdin file-like objects.  But when using the read and write methods of<br>
   those options, you do not have the benefit of asynchronous I/O.  In the<br>
   proposed solution the wrapper wraps the asynchronous methods to mimic a<br>
   file object.<br>
<br>
Reference Implementation:<br>
<br>
   I have been maintaining a Google Code repository that contains all of my<br>
   changes including tests and documentation [9] as well as blog detailing<br>
   the problems I have come across in the development process [10].<br>
<br>
   I have been working on implementing non-blocking asynchronous I/O in the<br>
   subprocess.Popen module as well as a wrapper class for subprocess.Popen<br>
   that makes it so that an executed process can take the place of a file by<br>
   duplicating all of the methods and attributes that file objects have.<br>
</blockquote></blockquote>
<br></div></div>
"Non-blocking" and "asynchronous" are actually two different things. From the rest of this PEP, I think only a non-blocking API is being introduced.  I haven't looked beyond the PEP, though, so I might be missing something.</blockquote>

<div><br></div><div>I suggest renaming <a href="http://www.python.org/dev/peps/pep-3145/">http://www.python.org/dev/peps/pep-3145/</a> to 'Non-blocking I/O for subprocess' and continue. IMHO on this stage is where examples with deadlocks that occur with current subprocess implementation are badly needed.</div>

<div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div class="im">
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">


   There are two base functions that have been added to the subprocess.Popen<br>
   class: Popen.send and Popen._recv, each with two separate implementations,<br>
   one for Windows and one for Unix based systems.  The Windows<br>
   implementation uses ctypes to access the functions needed to control pipes<br>
   in the kernel 32 DLL in an asynchronous manner.  On Unix based systems,<br>
   the Python interface for file control serves the same purpose.  The<br>
   different implementations of Popen.send and Popen._recv have identical<br>
   arguments to make code that uses these functions work across multiple<br>
   platforms.<br>
</blockquote></blockquote>
<br></div>
Why does the method for non-blocking read from a pipe start with an "_"? This is the convention (widely used) for a private API.  The name also doesn't suggest that this is the non-blocking version of reading. Similarly, the name "send" doesn't suggest that this is the non-blocking version of writing.</blockquote>

<div><br></div><div>The implementation is based on <a href="http://code.activestate.com/recipes/440554/">http://code.activestate.com/recipes/440554/</a> which is more clearly illustrates integrated functionality.</div><div>

<br></div><div>_recv() is a private base function, which is takes stdout or stderr as parameter. Corresponding user-level functions to read from stdout and stderr are .recv() and .recv_err() </div><div><br></div><div>I thought about renaming API to .asyncread() and .asyncwrite(), but that may mean that you call method and then result asynchronously start to fill some buffer, which is not the case here.</div>

<div><br></div><div>Then I thought about .check_read() and .check_write(), literally meaning 'check and read' or 'check and return' for non-blocking calls if there is nothing. But then again, poor naming convention of subprocess uses .check_output() for blocking read until command completes.</div>

<div><br></div><div>Currently, subversion doesn't have .read and .write methods. It may be the best option:</div><div>  .write(what)  to pipe more stuff into input buffer of child process.</div><div>  .read(from)  where `from` is either subprocess.STDOUT or STDERR</div>

<div>Both functions should be marked as non-blocking in docs and returning None if pipe is closed.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">

<div class="im">
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">


   When calling the Popen._recv function, it requires the pipe name be<br>
   passed as an argument so there exists the Popen.recv function that passes<br>
   selects stdout as the pipe for Popen._recv by default.  Popen.recv_err<br>
   selects stderr as the pipe by default. "Popen.recv" and "Popen.recv_err"<br>
   are much easier to read and understand than "Popen._recv('stdout' ..." and<br>
   "Popen._recv('stderr' ..." respectively.<br>
</blockquote></blockquote>
<br></div>
What about reading from other file descriptors?  subprocess.Popen allows arbitrary file descriptors to be used.  Is there any provision here for reading and writing non-blocking from or to those?</blockquote><div><br></div>

<div>On Windows it is WriteFile/ReadFile and PeekNamedPipe. On Linux it is select. Of course a test is needed, but why it should not just work?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">

<div class="im">
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">


   Since the Popen._recv function does not wait on data to be produced<br>
   before returning a value, it may return empty bytes.  Popen.asyncread<br>
   handles this issue by returning all data read over a given time<br>
   interval.<br>
</blockquote></blockquote>
<br></div>
Oh.  Popen.asyncread?   What's that?  This is the first time the PEP mentions it.</blockquote><div><br></div><div>I guess that's for blocking read with timeout.</div><div>Among the most popular questions about Python it is the question number ~500.</div>

<div><a href="http://stackoverflow.com/questions/1191374/subprocess-with-timeout">http://stackoverflow.com/questions/1191374/subprocess-with-timeout</a><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">

<div class="im">
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">


   The ProcessIOWrapper class uses the asyncread and asyncwrite functions to<br>
   allow a process to act like a file so that there are no blocking issues<br>
   that can arise from using the stdout and stdin file objects produced from<br>
   a subprocess.Popen call.<br>
</blockquote></blockquote>
<br></div>
What's the ProcessIOWrapper class?  And what's the asyncwrite function? Again, this is the first time it's mentioned.<br></blockquote><div><br></div><div>Oh. That's a wrapper to access subprocess pipes with familiar file API. It is interesting:</div>

<div><a href="http://code.google.com/p/subprocdev/source/browse/subprocess.py?name=python3k">http://code.google.com/p/subprocdev/source/browse/subprocess.py?name=python3k</a><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">


So, to sum up, I think my main comment is that the PEP seems to be missing a significant portion of the details of what it's actually proposing.  I suspect that this information is present in the implementation, which I have not looked at, but it probably belongs in the PEP.<br>


<br>
Jean-Paul<br></blockquote><div><br></div><div>Writing PEPs is definitely a job, and a hard one for developers. Too bad a good idea *and* implementation (tests needed) is put on hold, because there is nobody, who can help with that part.</div>

<div><br></div><div>IMHO PEP needs to expand on user stories even if there is significant amount of cited sources, a practical summary and problem illustration by examples are missing.</div></div></div>