#!/usr/bin/env python # BASED ON: # # subprocess - Subprocesses with accessible I/O streams # # For more information about this module, see PEP 324. # # This module should remain compatible with Python 2.2, see PEP 291. # # Copyright (c) 2003-2005 by Peter Astrand # # Licensed to PSF under a Contributor Agreement. # See http://www.python.org/2.4/license for licensing details. import sys import os import traceback import gc import errno import fcntl import pickle def _execute_child(): """Execute program (POSIX version)""" pid = None # For transferring possible exec failure from child to parent errpipe_read, errpipe_write = os.pipe() try: try: gc_was_enabled = gc.isenabled() # Disable gc to avoid bug where gc -> file_dealloc -> # write to stderr -> hang. http://bugs.python.org/issue1336 gc.disable() try: pid = os.fork() except: if gc_was_enabled: gc.enable() raise if pid == 0: # Child try: #os.execvp(executable, [executable]) raise Exception("FAKE execvp Exception") except: exc_type, exc_value, tb = sys.exc_info() # Save the traceback and attach it to the exception object exc_lines = traceback.format_exception(exc_type, exc_value, tb) exc_value.child_traceback = ''.join(exc_lines) print >> sys.stderr, "stderr: child_traceback: ", \ exc_value, exc_value.child_traceback print >> sys.stderr, "errpipe_write (%r) FCNTL FLAGS: %r" % ( errpipe_write, fcntl.fcntl(errpipe_write, fcntl.F_GETFL, 0)) os.write(errpipe_write, pickle.dumps(exc_value)) # This exitcode won't be reported to applications, so it # really doesn't matter what we return. os._exit(255) # Parent if gc_was_enabled: gc.enable() except: print >> sys.stderr, "stderr: self pid: ", pid, traceback.format_exc() raise finally: # be sure the FD is closed no matter what os.close(errpipe_write) # Wait for exec to fail or succeed; possibly raising exception # Exception limited to 1M fcntl.fcntl(errpipe_read, fcntl.F_SETFL, fcntl.fcntl(errpipe_read, fcntl.F_GETFL, 0) | os.O_NONBLOCK) print >> sys.stderr, "errpipe_read (%r) FCNTL FLAGS: %r" % ( errpipe_read, fcntl.fcntl(errpipe_read, fcntl.F_GETFL, 0)) data = "" rSize = 1048576 while True: while True: try: print >> sys.stderr, "os.read(%r, %r)" % (errpipe_read, rSize), "self pid: ", pid newData = os.read(errpipe_read, rSize) except OSError, e: if e.errno in [errno.EINTR, errno.EWOULDBLOCK, errno.EAGAIN]: print >> sys.stderr, traceback.format_exc() continue raise else: break if not newData: break rSize -= len(newData) data += newData #break except: #print >> sys.stderr, "stderr: self pid: ", pid, traceback.format_exc() raise finally: # be sure the FD is closed no matter what os.close(errpipe_read) if data != "": print >> sys.stderr, "os.waitpid: self pid=", pid child_exception = pickle.loads(data) raise child_exception def test(): for i in range(10): print >> sys.stderr, 'run {0}'.format(i) try: _execute_child() except OSError as ose: if ose.errno == errno.EINVAL: print >> sys.stderr, "EINVAL FROM TEST: ", traceback.format_exc() except Exception as e: pass #print >> sys.stderr, traceback.format_exc() #if hasattr(e, "child_traceback"): # print >> sys.stderr, "child_traceback: ", e.child_traceback test()