title: How can I write to a pipe with a timeout in Python 2.7?
body: <p>I'm opening pipes to communicate with a child process in Python. The communication is bidirectional and continuous over the lifetime of both processes.</p>
  
  <pre><code>import os
  r, w = os.pipe()
  # fork, exec, etc.
  child_stdin = os.fdopen(w, "w")
  </code></pre>
  
  <p>Writing to a pipe can block indefinitely if the other side is full. On macOS and Linux, the default pipe buffer is 65535 bytes, and trying to push anything more will block:</p>
  
  <pre><code>import os
  r, w = os.pipe()
  f = os.fdopen(w, "w")
  f.write("\0" * 65535)
  f.write("\0")  # blocks indefinitely
  </code></pre>
  
  <p>Using <code>select.select</code> to test if the pipe is ready works and it allows you to specify a timeout, however all it tells you is that the pipe can receive some data, not that it can necessarily receive the amount of data that you wish to send at once. For instance, this will also block indefinitely:</p>
  
  <pre><code>import os
  import select
  r, w = os.pipe()
  f = os.fdopen(w, "w")
  f.write("\0" * 65534)
  select.select((), (f,), ())
  # select returned, therefore we can write?...
  f.write("\0\0")  # blocks indefinitely
  </code></pre>
  
  <p>You can set the pipe to be non-blocking and use that as a building block for timeouts (<code>select</code>, write some, <code>select</code> again until ready, write some again, etc). However <code>f.write</code> doesn't return anything, and as such you don't know how much data was successfully written out:</p>
  
  <pre><code>import os
  import fcntl
  r, w = os.pipe()
  fcntl.fcntl(w, fcntl.F_SETFL, fcntl.fcntl(w, fcntl.F_GETFL) | os.O_NONBLOCK))
  f = os.fdopen(w, "w")
  f.write("\0" * 65535)
  f.write("\0")  # returns immediately without writing anything
  # no idea how much data was actually written :/
  </code></pre>
  
  <p>So what is the proper way to write to a pipe with a timeout in Python 2.7? Should I drop down to <code>os.write</code> at this point?</p>
  
  <p>The solution would have to work on both macOS and Linux.</p>