Process.py
上传用户:lswyart
上传日期:2008-06-12
资源大小:3441k
文件大小:102k
源码类别:

杀毒

开发平台:

Visual C++

  1. #!/usr/bin/env python
  2. # Copyright (c) 2002-2003 ActiveState
  3. # Permission is hereby granted, free of charge, to any person obtaining a
  4. # copy of this software and associated documentation files (the
  5. # "Software"), to deal in the Software without restriction, including
  6. # without limitation the rights to use, copy, modify, merge, publish,
  7. # distribute, sublicense, and/or sell copies of the Software, and to
  8. # permit persons to whom the Software is furnished to do so, subject to
  9. # the following conditions:
  10. # The above copyright notice and this permission notice shall be included
  11. # in all copies or substantial portions of the Software.
  12. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  13. # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  15. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  16. # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  17. # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  18. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. # Thanks to Trent Mick: http://starship.python.net/crew/tmick/
  20. # Changed by alch:
  21. # 15 03 2004 - Added priority parameter 
  22. #              to ProcessProxy.__init__()
  23. #            - Added int() to WaitForSingleObject calls
  24. #              to overvcome
  25. #              DeprecationWarning: integer argument expected, got float
  26. #            - Changed (hacked...) win9x bit in _fixupCommand, as it was 
  27. #              not working properly. This need a proper analysis and fix
  28. r"""
  29.     Python interface for process control.
  30.     This module defines three Process classes for spawning,
  31.     communicating and control processes. They are: Process, ProcessOpen,
  32.     ProcessProxy. All of the classes allow one to specify the command (cmd),
  33.     starting working directory (cwd), and environment to create for the
  34.     new process (env) and to "wait" for termination of the child and
  35.     "kill" the child.
  36.     Process:
  37.         Use this class to simply launch a process (either a GUI app or a
  38.         console app in a new console) with which you do not intend to
  39.         communicate via it std handles.
  40.     ProcessOpen:
  41.         Think of this as a super version of Python's os.popen3() method.
  42.         This spawns the given command and sets up pipes for
  43.         stdin/stdout/stderr which can then be used to communicate with
  44.         the child.
  45.     ProcessProxy:
  46.         This is a heavy-weight class that, similar to ProcessOpen,
  47.         spawns the given commands and sets up pipes to the child's
  48.         stdin/stdout/stderr. However, it also starts three threads to
  49.         proxy communication between each of the child's and parent's std
  50.         handles. At the parent end of this communication are, by
  51.         default, IOBuffer objects. You may specify your own objects here
  52.         (usually sub-classing from IOBuffer, which handles some
  53.         synchronization issues for you). The result is that it is
  54.         possible to have your own IOBuffer instance that gets, say, a
  55.         .write() "event" for every write that the child does on its
  56.         stdout.
  57.         Understanding ProcessProxy is pretty complex. Some examples
  58.         below attempt to help show some uses. Here is a diagram of the
  59.         comminucation:
  60.                             <parent process>
  61.                ,---->->->------'   ^   `------>->->----,
  62.                |                   |                   v
  63.            IOBuffer             IOBuffer            IOBuffer
  64.            (p.stdout)           (p.stderr)          (p.stdin)
  65.                |                   |                   |
  66.            _OutFileProxy        _OutFileProxy       _InFileProxy
  67.            thread               thread              thread
  68.                |                   ^                   |
  69.                `----<-<-<------,   |   ,------<-<-<----'
  70.                             <child process>
  71.     Usage:
  72.         import process
  73.         p = process.<Process class>(cmd='echo hi', ...)
  74.         #... use the various methods and attributes
  75.     Examples:
  76.       A simple 'hello world':
  77.         >>> import process
  78.         >>> p = process.ProcessOpen(['echo', 'hello'])
  79.         >>> p.stdout.read()
  80.         'hellorn'
  81.         >>> p.wait()   # .wait() returns the child's exit status
  82.         0
  83.       Redirecting the stdout handler:
  84.         >>> import sys
  85.         >>> p = process.ProcessProxy(['echo', 'hello'], stdout=sys.stdout)
  86.         hello
  87.       Using stdin (need to use ProcessProxy here because it defaults to
  88.       text-mode translation on Windows, ProcessOpen does not support
  89.       this):
  90.         >>> p = process.ProcessProxy(['sort'])
  91.         >>> p.stdin.write('5n')
  92.         >>> p.stdin.write('2n')
  93.         >>> p.stdin.write('7n')
  94.         >>> p.stdin.close()
  95.         >>> p.stdout.read()
  96.         '2n5n7n'
  97.       Specifying environment variables:
  98.         >>> p = process.ProcessOpen(['perl', '-e', 'print $ENV{FOO}'])
  99.         >>> p.stdout.read()
  100.         ''
  101.         >>> p = process.ProcessOpen(['perl', '-e', 'print $ENV{FOO}'],
  102.         ...                         env={'FOO':'bar'})
  103.         >>> p.stdout.read()
  104.         'bar'
  105.       Killing a long running process (On Linux, to poll you must use
  106.       p.wait(os.WNOHANG)):
  107.         >>> p = ProcessOpen(['perl', '-e', 'while (1) {}'])
  108.         >>> try:
  109.         ...     p.wait(os.WNOHANG)  # poll to see if is process still running
  110.         ... except ProcessError, ex:
  111.         ...     if ex.errno == ProcessProxy.WAIT_TIMEOUT:
  112.         ...             print "process is still running"
  113.         ...
  114.         process is still running
  115.         >>> p.kill(42)
  116.         >>> p.wait()
  117.         42
  118.       Providing objects for stdin/stdout/stderr:
  119.         XXX write this, mention IOBuffer subclassing.
  120. """
  121. #TODO:
  122. #   - Discuss the decision to NOT have the stdout/stderr _OutFileProxy's
  123. #     wait for process termination before closing stdin. It will just
  124. #     close stdin when stdout is seen to have been closed. That is
  125. #     considered Good Enough (tm). Theoretically it would be nice to
  126. #     only abort the stdin proxying when the process terminates, but
  127. #     watching for process termination in any of the parent's thread
  128. #     adds the undesired condition that the parent cannot exit with the
  129. #     child still running. That sucks.
  130. #     XXX Note that I don't even know if the current stdout proxy even
  131. #         closes the stdin proxy at all.
  132. #   - DavidA: if I specify "unbuffered" for my stdin handler (in the
  133. #     ProcessProxy constructor) then the stdin IOBuffer should do a
  134. #     fparent.read() rather than a fparent.readline(). TrentM: can I do
  135. #     that? What happens?
  136. #
  137. import os
  138. import sys
  139. import threading
  140. import types
  141. import pprint
  142. if sys.platform.startswith("win"):
  143.     import msvcrt
  144.     import win32api
  145.     import win32file
  146.     import win32pipe
  147.     import pywintypes
  148.     import win32process
  149.     import win32event
  150.     # constants pulled from win32con to save memory
  151.     VER_PLATFORM_WIN32_WINDOWS = 1
  152.     CTRL_BREAK_EVENT = 1
  153.     SW_SHOWDEFAULT = 10
  154.     WM_CLOSE = 0x10
  155.     DUPLICATE_SAME_ACCESS = 2
  156. else:
  157.     import signal
  158. #---- exceptions
  159. class ProcessError(Exception):
  160.     def __init__(self, msg, errno=-1):
  161.         Exception.__init__(self, msg)
  162.         self.errno = errno    
  163. #---- internal logging facility
  164. class Logger:
  165.     DEBUG, INFO, WARN, ERROR, FATAL = range(5)
  166.     def __init__(self, name, level=None, streamOrFileName=sys.stderr):
  167.         self.name = name
  168.         if level is None:
  169.             self.level = self.WARN
  170.         else:
  171.             self.level = level
  172.         if type(streamOrFileName) == types.StringType:
  173.             self.stream = open(streamOrFileName, 'w')
  174.             self._opennedStream = 1
  175.         else:
  176.             self.stream = streamOrFileName
  177.             self._opennedStream = 0
  178.     def __del__(self):
  179.         if self._opennedStream:
  180.             self.stream.close()
  181.     def _getLevelName(self, level):
  182.         levelNameMap = {
  183.             self.DEBUG: "DEBUG",
  184.             self.INFO: "INFO",
  185.             self.WARN: "WARN",
  186.             self.ERROR: "ERROR",
  187.             self.FATAL: "FATAL",
  188.         }
  189.         return levelNameMap[level]
  190.     def log(self, level, msg, *args):
  191.         if level < self.level:
  192.             return
  193.         message = "%s: %s:" % (self.name, self._getLevelName(level).lower())
  194.         message = message + (msg % args) + "n"
  195.         self.stream.write(message)
  196.         try:
  197.             self.stream.flush()
  198.         except:
  199.             # no flush or _closed implemented
  200.             # not to worry
  201.             pass
  202.     def debug(self, msg, *args):
  203.         self.log(self.DEBUG, msg, *args)
  204.     def info(self, msg, *args):
  205.         self.log(self.INFO, msg, *args)
  206.     def warn(self, msg, *args):
  207.         self.log(self.WARN, msg, *args)
  208.     def error(self, msg, *args):
  209.         self.log(self.ERROR, msg, *args)
  210.     def fatal(self, msg, *args):
  211.         self.log(self.FATAL, msg, *args)
  212. # Loggers:
  213. #   - 'log' to log normal process handling
  214. #   - 'logres' to track system resource life
  215. #   - 'logfix' to track wait/kill proxying in _ThreadFixer
  216. __DEBUG = False
  217. if not __DEBUG:   # normal/production usage
  218.     log = Logger("process", Logger.WARN)    
  219. else:   # development/debugging usage
  220.     log = Logger("process", Logger.DEBUG, sys.stdout)
  221. if not __DEBUG:   # normal/production usage
  222.     logres = Logger("process.res", Logger.WARN)
  223. else:   # development/debugging usage
  224.     logres = Logger("process.res", Logger.DEBUG, sys.stdout)
  225. if not __DEBUG:   # normal/production usage
  226.     logfix = Logger("process.waitfix", Logger.WARN)
  227. else:   # development/debugging usage
  228.     logfix = Logger("process.waitfix", Logger.DEBUG, sys.stdout)
  229.     
  230. #---- globals
  231. _version_ = (0, 5, 0)
  232. # List of registered processes (see _(un)registerProcess).
  233. _processes = []
  234. #---- internal support routines
  235. def _escapeArg(arg):
  236.     """Escape the given command line argument for the shell."""
  237.     #XXX There is a probably more that we should escape here.
  238.     return arg.replace('"', r'"')
  239. def _joinArgv(argv):
  240.     r"""Join an arglist to a string appropriate for running.
  241.         >>> import os
  242.         >>> _joinArgv(['foo', 'bar "baz'])
  243.         'foo "bar \"baz"'
  244.     """
  245.     cmdstr = ""
  246.     for arg in argv:
  247.         if ' ' in arg or ';' in arg:
  248.             cmdstr += '"%s"' % _escapeArg(arg)
  249.         else:
  250.             cmdstr += _escapeArg(arg)
  251.         cmdstr += ' '
  252.     if cmdstr.endswith(' '): cmdstr = cmdstr[:-1]  # strip trailing space
  253.     return cmdstr
  254. def _getPathFromEnv(env):
  255.     """Return the PATH environment variable or None.
  256.     Do the right thing for case sensitivity per platform.
  257.     XXX Icky. This guarantee of proper case sensitivity of environment
  258.         variables should be done more fundamentally in this module.
  259.     """
  260.     if sys.platform.startswith("win"):
  261.         for key in env.keys():
  262.             if key.upper() == "PATH":
  263.                 return env[key]
  264.         else:
  265.             return None
  266.     else:
  267.         if env.has_key("PATH"):
  268.             return env["PATH"]
  269.         else:
  270.             return None
  271. def _whichFirstArg(cmd, env=None):
  272.     """Return the given command ensuring that the first arg (the command to
  273.     launch) is a full path to an existing file.
  274.     Raise a ProcessError if no such executable could be found.
  275.     """    
  276.     # Parse out the first arg.
  277.     if cmd.startswith('"'):
  278.         # The .replace() is to ensure it does not mistakenly find the
  279.         # second '"' in, say (escaped quote):
  280.         #           "C:foo"bar" arg1 arg2
  281.         idx = cmd.replace('\"', 'XX').find('"', 1)
  282.         if idx == -1:
  283.             raise ProcessError("Malformed command: %r" % cmd)
  284.         first, rest = cmd[1:idx], cmd[idx+1:]
  285.         rest = rest.lstrip()
  286.     else:
  287.         if ' ' in cmd:
  288.             first, rest = cmd.split(' ', 1)
  289.         else:
  290.             first, rest = cmd, ""
  291.     # Ensure the first arg is a valid path to the appropriate file.
  292.     import which
  293.     if os.sep in first:
  294.         altpath = [os.path.dirname(first)]
  295.         firstbase = os.path.basename(first)
  296.         candidates = list(which.which(firstbase, path=altpath))
  297.     elif env:
  298.         altpath = _getPathFromEnv(env)
  299.         if altpath:
  300.             candidates = list(which.which(first, altpath.split(os.pathsep)))
  301.         else:
  302.             candidates = list(which.which(first))
  303.     else:
  304.         candidates = list(which.which(first))
  305.     if candidates:
  306.         return _joinArgv( [candidates[0]] ) + ' ' + rest
  307.     else:
  308.         raise ProcessError("Could not find an appropriate leading command "
  309.                            "for: %r" % cmd)
  310. if sys.platform.startswith("win"):
  311.     def _SaferCreateProcess(appName,        # app name
  312.                             cmd,            # command line
  313.                             processSA,      # process security attributes
  314.                             threadSA,       # thread security attributes
  315.                             inheritHandles, # are handles are inherited
  316.                             creationFlags,  # creation flags
  317.                             env,            # environment
  318.                             cwd,            # current working directory
  319.                             si):            # STARTUPINFO pointer
  320.         """If CreateProcess fails from environment type inconsistency then
  321.         fix that and try again.
  322.         win32process.CreateProcess requires that all environment keys and
  323.         values either be all ASCII or all unicode. Try to remove this burden
  324.         from the user of process.py.
  325.         """
  326.         isWin9x = win32api.GetVersionEx()[3] == VER_PLATFORM_WIN32_WINDOWS
  327.         # On Win9x all keys and values of 'env' must be ASCII (XXX
  328.         # Actually this is probably only true if the Unicode support
  329.         # libraries, which are not installed by default, are not
  330.         # installed). On other Windows flavours all keys and values of
  331.         # 'env' must all be ASCII *or* all Unicode. We will try to
  332.         # automatically convert to the appropriate type, issuing a
  333.         # warning if such an automatic conversion is necessary.
  334.         #XXX Komodo 2.0 Beta 1 hack. This requirement should be
  335.         #    pushed out to Komodo code using process.py. Or should it?
  336.         if isWin9x and env:
  337.             aenv = {}
  338.             for key, value in env.items():
  339.                 aenv[str(key)] = str(value)
  340.             env = aenv
  341.         log.debug("""
  342. _SaferCreateProcess(appName=%r,
  343.                     cmd=%r,
  344.                     env=%r,
  345.                     cwd=%r)
  346.     os.getcwd(): %r
  347. """, appName, cmd, env, cwd, os.getcwd())
  348.         try:
  349.             hProcess, hThread, processId, threadId
  350.                 = win32process.CreateProcess(appName, cmd, processSA,
  351.                                              threadSA, inheritHandles,
  352.                                              creationFlags, env, cwd, si)
  353.         except TypeError, ex:            
  354.             if ex.args == ('All dictionary items must be strings, or all must be unicode',):
  355.                 # Try again with an all unicode environment.
  356.                 #XXX Would be nice if didn't have to depend on the error
  357.                 #    string to catch this.
  358.                 #XXX Removing this warning for 2.3 release. See bug
  359.                 #    23215. The right fix is to correct the PHPAppInfo
  360.                 #    stuff to heed the warning.
  361.                 #import warnings
  362.                 #warnings.warn('env: ' + str(ex), stacklevel=4)
  363.                 if isWin9x and env:
  364.                     aenv = {}
  365.                     try:
  366.                         for key, value in env.items():
  367.                             aenv[str(key)] = str(value)
  368.                     except UnicodeError, ex:
  369.                         raise ProcessError(str(ex))
  370.                     env = aenv
  371.                 elif env:
  372.                     uenv = {}
  373.                     for key, val in env.items():
  374.                         uenv[unicode(key)] = unicode(val)
  375.                     env = uenv
  376.                 hProcess, hThread, processId, threadId
  377.                     = win32process.CreateProcess(appName, cmd, processSA,
  378.                                                  threadSA, inheritHandles,
  379.                                                  creationFlags, env, cwd,
  380.                                                  si)
  381.             else:
  382.                 raise
  383.         return hProcess, hThread, processId, threadId
  384. # Maintain references to all spawned ProcessProxy objects to avoid hangs.
  385. #   Otherwise, if the user lets the a ProcessProxy object go out of
  386. #   scope before the process has terminated, it is possible to get a
  387. #   hang (at least it *used* to be so when we had the
  388. #   win32api.CloseHandle(<stdin handle>) call in the __del__() method).
  389. #   XXX Is this hang possible on Linux as well?
  390. # A reference is removed from this list when the process's .wait or
  391. # .kill method is called.
  392. # XXX Should an atexit() handler be registered to kill all curently
  393. #     running processes? Else *could* get hangs, n'est ce pas?
  394. def _registerProcess(process):
  395.     global _processes
  396.     log.info("_registerprocess(process=%r)", process)
  397.     # Clean up zombie processes.
  398.     #   If the user does not call .wait() or .kill() on processes then
  399.     #   the ProcessProxy object will not get cleaned up until Python
  400.     #   exits and _processes goes out of scope. Under heavy usage that
  401.     #   is a big memory waste. Cleaning up here alleviates that.
  402.     for p in _processes[:]: # use copy of _process, because we may modifiy it
  403.         try:
  404.             # poll to see if is process still running
  405.             if sys.platform.startswith("win"):
  406.                 timeout = 0
  407.             else:
  408.                 timeout = os.WNOHANG
  409.             p.wait(timeout)
  410.             _unregisterProcess(p)
  411.         except ProcessError, ex:
  412.             if ex.errno == ProcessProxy.WAIT_TIMEOUT:
  413.                 pass
  414.             else:
  415.                 raise
  416.     _processes.append(process)
  417. def _unregisterProcess(process):
  418.     global _processes
  419.     log.info("_unregisterProcess(process=%r)", process)
  420.     try:
  421.         _processes.remove(process)
  422.         del process
  423.     except ValueError:
  424.         pass
  425. def _fixupCommand(cmd, env=None):
  426.     """Fixup the command string so it is launchable via CreateProcess.
  427.     One cannot just launch, say "python", via CreateProcess. A full path
  428.     to an executable is required. In general there are two choices:
  429.         1. Launch the command string via the shell. The shell will find
  430.            the fullpath to the appropriate executable. This shell will
  431.            also be able to execute special shell commands, like "dir",
  432.            which don't map to an actual executable.
  433.         2. Find the fullpath to the appropriate executable manually and
  434.            launch that exe.
  435.     Option (1) is preferred because you don't have to worry about not
  436.     exactly duplicating shell behaviour and you get the added bonus of
  437.     being able to launch "dir" and friends.
  438.     However, (1) is not always an option. Doing so when the shell is
  439.     command.com (as on all Win9x boxes) or when using WinNT's cmd.exe,
  440.     problems are created with .kill() because these shells seem to eat
  441.     up Ctrl-C's and Ctrl-Break's sent via
  442.     win32api.GenerateConsoleCtrlEvent().  Strangely this only happens
  443.     when spawn via this Python interface. For example, Ctrl-C get
  444.     through to hang.exe here:
  445.       C:> ...w9xpopen.exe "C:WINDOWSCOMMAND.COM /c hang.exe"
  446.       ^C
  447.     but not here:
  448.       >>> p = ProcessOpen('hang.exe')
  449.       # This results in the same command to CreateProcess as
  450.       # above.
  451.       >>> p.kill()
  452.     Hence, for these platforms we fallback to option (2).  Cons:
  453.       - cannot spawn shell commands like 'dir' directly
  454.       - cannot spawn batch files
  455.     """
  456.     if sys.platform.startswith("win"):
  457.         # Fixup the command string to spawn.  (Lifted from
  458.         # posixmodule.c::_PyPopenCreateProcess() with some modifications)
  459.         
  460.         # alch 01-04-2004
  461.         # should it be cmd.exe on nt+?
  462.         comspec = os.environ.get("COMSPEC", None)
  463.         win32Version = win32api.GetVersion()
  464.         if comspec is None:
  465.             raise ProcessError("Cannot locate a COMSPEC environment "
  466.                                "variable to use as the shell")
  467.         # Explicitly check if we are using COMMAND.COM.  If we
  468.         # are then use the w9xpopen hack.
  469.         elif (win32Version & 0x80000000L == 0) and
  470.              (win32Version &        0x5L >= 5):             
  471.                 if os.path.basename(comspec).lower() != "cmd.exe":
  472.                     # 2000/XP and not using command.com.
  473.                     if '"' in cmd or "'" in cmd:
  474.                         cmd = comspec + ' /c "%s"' % cmd
  475.                     else:
  476.                         cmd = comspec + ' /c ' + cmd        
  477.         elif (win32Version & 0x80000000L == 0) and
  478.              (win32Version &        0x5L  < 5):
  479.              if os.path.basename(comspec).lower() != "cmd.exe":
  480.                  pass
  481.         # alch - Removed 01.04.2003 - doesn't work
  482.         # my commands are always well-formed                
  483. ##            # NT and not using command.com.            
  484. ##            try:
  485. ##                cmd = _whichFirstArg(cmd, env)
  486. ##            except ProcessError:
  487. ##                raise ProcessError("Could not find a suitable executable "
  488. ##                    "to launch for '%s'. On WinNT you must manually prefix "
  489. ##                    "shell commands and batch files with 'cmd.exe /c' to "
  490. ##                    "have the shell run them." % cmd)
  491.         else:
  492.             # Oh gag, we're on Win9x and/or using COMMAND.COM. Use the
  493.             # workaround listed in KB: Q150956
  494.             modulehandle = 0
  495.             # added by alch for binary builds
  496.             if hasattr(sys, "frozen"):
  497.                 if sys.frozen == "dll":            
  498.                     modulehandle = sys.frozendllhandle                                                        
  499.             w9xpopen = os.path.join(
  500.                 os.path.dirname(win32api.GetModuleFileName(modulehandle)),
  501.                 'w9xpopen.exe')
  502.             if not os.path.exists(w9xpopen):
  503.                 # Eeek - file-not-found - possibly an embedding
  504.                 # situation - see if we can locate it in sys.exec_prefix
  505.                 w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix),
  506.                                         'w9xpopen.exe')
  507.                 if not os.path.exists(w9xpopen):
  508.                     raise ProcessError(
  509.                         "Can not locate 'w9xpopen.exe' which is needed "
  510.                         "for ProcessOpen to work with your shell or "
  511.                         "platform.")
  512.             ## This would be option (1):
  513.             #cmd = '%s "%s /c %s"'
  514.             #      % (w9xpopen, comspec, cmd.replace('"', '\"'))
  515.             
  516.             # Changed alch 15-03-2004
  517.             # Why do we need to replace " with "? w9xpopen seems to not like that
  518.             # and _whichfirstarg doesn't quite work
  519.             # I decided not to call it as command I use are always well formed
  520.             cmd = '%s %s' % (w9xpopen, cmd)
  521. ##            try:
  522. ##                cmd = _whichFirstArg(cmd, env)
  523. ##            except ProcessError:
  524. ##                raise ProcessError("Could not find a suitable executable "
  525. ##                    "to launch for '%s'. On Win9x you must manually prefix "
  526. ##                    "shell commands and batch files with 'command.com /c' "
  527. ##                    "to have the shell run them." % cmd)
  528. ##            
  529. ##            cmd = '%s "%s"' % (w9xpopen, cmd.replace('"', '\"'))            
  530.     return cmd
  531. class _FileWrapper:
  532.     """Wrap a system file object, hiding some nitpicky details.
  533.     This class provides a Python file-like interface to either a Python
  534.     file object (pretty easy job), a file descriptor, or an OS-specific
  535.     file handle (e.g.  Win32 handles to file objects on Windows). Any or
  536.     all of these object types may be passed to this wrapper. If more
  537.     than one is specified this wrapper prefers to work with certain one
  538.     in this order:
  539.         - file descriptor (because usually this allows for
  540.           return-immediately-on-read-if-anything-available semantics and
  541.           also provides text mode translation on Windows)
  542.         - OS-specific handle (allows for the above read semantics)
  543.         - file object (buffering can cause difficulty for interacting
  544.           with spawned programs)
  545.     It also provides a place where related such objects can be kept
  546.     alive together to prevent premature ref-counted collection. (E.g. on
  547.     Windows a Python file object may be associated with a Win32 file
  548.     handle. If the file handle is not kept alive the Python file object
  549.     will cease to function.)
  550.     """
  551.     def __init__(self, file=None, descriptor=None, handle=None):
  552.         self._file = file
  553.         self._descriptor = descriptor
  554.         self._handle = handle
  555.         self._closed = 0
  556.         if self._descriptor is not None or self._handle is not None:
  557.             self._lineBuf = "" # to support .readline()
  558.     def __del__(self):
  559.         self.close()
  560.     def __getattr__(self, name):
  561.         """Forward to the underlying file object."""
  562.         if self._file is not None:
  563.             return getattr(self._file, name)
  564.         else:
  565.             raise ProcessError("no file object to pass '%s' attribute to"
  566.                                % name)
  567.     def _win32Read(self, nBytes):
  568.         try:
  569.             log.info("[%s] _FileWrapper.read: waiting for read on pipe",
  570.                      id(self))
  571.             errCode, text = win32file.ReadFile(self._handle, nBytes)
  572.         except pywintypes.error, ex:
  573.             # Ignore errors for now, like "The pipe is being closed.",
  574.             # etc. XXX There *may* be errors we don't want to avoid.
  575.             log.info("[%s] _FileWrapper.read: error reading from pipe: %s",
  576.                      id(self), ex)
  577.             return ""
  578.         assert errCode == 0,
  579.                "Why is 'errCode' from ReadFile non-zero? %r" % errCode
  580.         if not text:
  581.             # Empty text signifies that the pipe has been closed on
  582.             # the parent's end.
  583.             log.info("[%s] _FileWrapper.read: observed close of parent",
  584.                      id(self))
  585.             # Signal the child so it knows to stop listening.
  586.             self.close()
  587.             return ""
  588.         else:
  589.             log.info("[%s] _FileWrapper.read: read %d bytes from pipe: %r",
  590.                      id(self), len(text), text)
  591.         return text
  592.     def read(self, nBytes=-1):
  593.         # nBytes <= 0 means "read everything"
  594.         #   Note that we are changing the "read everything" cue to
  595.         #   include 0, because actually doing
  596.         #   win32file.ReadFile(<handle>, 0) results in every subsequent
  597.         #   read returning 0, i.e. it shuts down the pipe.
  598.         if self._descriptor is not None:
  599.             if nBytes <= 0:
  600.                 text, self._lineBuf = self._lineBuf, ""
  601.                 while 1:
  602.                     t = os.read(self._descriptor, 4092)
  603.                     if not t:
  604.                         break
  605.                     else:
  606.                         text += t
  607.             else:
  608.                 if len(self._lineBuf) >= nBytes:
  609.                     text, self._lineBuf =
  610.                         self._lineBuf[:nBytes], self._lineBuf[nBytes:]
  611.                 else:
  612.                     nBytesToGo = nBytes - len(self._lineBuf)
  613.                     text = self._lineBuf + os.read(self._descriptor,
  614.                                                    nBytesToGo)
  615.                     self._lineBuf = ""
  616.             return text
  617.         elif self._handle is not None:
  618.             if nBytes <= 0:
  619.                 text, self._lineBuf = self._lineBuf, ""
  620.                 while 1:
  621.                     t = self._win32Read(4092)
  622.                     if not t:
  623.                         break
  624.                     else:
  625.                         text += t
  626.             else:
  627.                 if len(self._lineBuf) >= nBytes:
  628.                     text, self._lineBuf =
  629.                         self._lineBuf[:nBytes], self._lineBuf[nBytes:]
  630.                 else:
  631.                     nBytesToGo = nBytes - len(self._lineBuf)
  632.                     text, self._lineBuf =
  633.                         self._lineBuf + self._win32Read(nBytesToGo), ""
  634.             return text
  635.         elif self._file is not None:
  636.             return self._file.read(nBytes)
  637.         else:
  638.             raise "FileHandle.read: no handle to read with"
  639.     def readline(self):
  640.         if self._descriptor is not None or self._handle is not None:
  641.             while 1:
  642.                 #XXX This is not portable to the Mac.
  643.                 idx = self._lineBuf.find('n')
  644.                 if idx != -1:
  645.                     line, self._lineBuf =
  646.                         self._lineBuf[:idx+1], self._lineBuf[idx+1:]
  647.                     break
  648.                 else:
  649.                     lengthBefore = len(self._lineBuf)
  650.                     t = self.read(4092)
  651.                     if len(t) <= lengthBefore: # no new data was read
  652.                         line, self._lineBuf = self._lineBuf, ""
  653.                         break
  654.                     else:
  655.                         self._lineBuf += t
  656.             return line
  657.         elif self._file is not None:
  658.             return self._file.readline()
  659.         else:
  660.             raise "FileHandle.readline: no handle to read with"
  661.     def readlines(self):
  662.         if self._descriptor is not None or self._handle is not None:
  663.             lines = []
  664.             while 1:
  665.                 line = self.readline()
  666.                 if line:
  667.                     lines.append(line)
  668.                 else:
  669.                     break
  670.             return lines
  671.         elif self._file is not None:
  672.             return self._file.readlines()
  673.         else:
  674.             raise "FileHandle.readline: no handle to read with"
  675.     def write(self, text):
  676.         if self._descriptor is not None:
  677.             os.write(self._descriptor, text)
  678.         elif self._handle is not None:
  679.             try:
  680.                 errCode, nBytesWritten = win32file.WriteFile(self._handle, text)
  681.             except pywintypes.error, ex:
  682.                 # Ingore errors like "The pipe is being closed.", for
  683.                 # now.
  684.                 log.info("[%s] _FileWrapper.write: error writing to pipe, "
  685.                          "ignored", id(self))
  686.                 return
  687.             assert errCode == 0,
  688.                    "Why is 'errCode' from WriteFile non-zero? %r" % errCode
  689.             if not nBytesWritten:
  690.                 # No bytes written signifies that the pipe has been
  691.                 # closed on the child's end.
  692.                 log.info("[%s] _FileWrapper.write: observed close of pipe",
  693.                          id(self))
  694.                 return
  695.             else:
  696.                 log.info("[%s] _FileWrapper.write: wrote %d bytes to pipe: %r",
  697.                          id(self), len(text), text)
  698.         elif self._file is not None:
  699.             self._file.write(text)
  700.         else:
  701.             raise "FileHandle.write: nothing to write with"
  702.     def close(self):
  703.         """Close all associated file objects and handles."""
  704.         log.debug("[%s] _FileWrapper.close()", id(self))
  705.         if not self._closed:
  706.             self._closed = 1
  707.             if self._file is not None:
  708.                 log.debug("[%s] _FileWrapper.close: close file", id(self))
  709.                 self._file.close()
  710.                 log.debug("[%s] _FileWrapper.close: done file close", id(self))
  711.             if self._descriptor is not None:
  712.                 try:
  713.                     os.close(self._descriptor)
  714.                 except OSError, ex:
  715.                     if ex.errno == 9:
  716.                         # Ignore: OSError: [Errno 9] Bad file descriptor
  717.                         # XXX *Should* we be ignoring this? It appears very
  718.                         #     *in*frequently in test_wait.py.
  719.                         log.debug("[%s] _FileWrapper.close: closing "
  720.                                   "descriptor raised OSError", id(self))
  721.                     else:
  722.                         raise
  723.             if self._handle is not None:
  724.                 log.debug("[%s] _FileWrapper.close: close handle", id(self))
  725.                 try:
  726.                     win32api.CloseHandle(self._handle)
  727.                 except win32api.error:
  728.                     log.debug("[%s] _FileWrapper.close: closing handle raised",
  729.                               id(self))
  730.                     pass
  731.                 log.debug("[%s] _FileWrapper.close: done closing handle",
  732.                           id(self))
  733.     def __repr__(self):
  734.         return "<_FileWrapper: file:%r fd:%r os_handle:%r>"
  735.                % (self._file, self._descriptor, self._handle)
  736. class _CountingCloser:
  737.     """Call .close() on the given object after own .close() is called
  738.     the precribed number of times.
  739.     """
  740.     def __init__(self, objectsToClose, count):
  741.         """
  742.         "objectsToClose" is a list of object on which to call .close().
  743.         "count" is the number of times this object's .close() method
  744.             must be called before .close() is called on the given objects.
  745.         """
  746.         self.objectsToClose = objectsToClose
  747.         self.count = count
  748.         if self.count <= 0:
  749.             raise ProcessError("illegal 'count' value: %s" % self.count)
  750.     def close(self):
  751.         self.count -= 1
  752.         log.debug("[%d] _CountingCloser.close(): count=%d", id(self),
  753.                   self.count)
  754.         if self.count == 0:
  755.             for objectToClose in self.objectsToClose:
  756.                 objectToClose.close()
  757. #---- public interface
  758. class Process:
  759.     """Create a process.
  760.     One can optionally specify the starting working directory, the
  761.     process environment, and std handles to have the child process
  762.     inherit (all defaults are the parent's current settings). 'wait' and
  763.     'kill' method allow for control of the child's termination.
  764.     """
  765.     # TODO:
  766.     #   - Rename this or merge it with ProcessOpen somehow.
  767.     #
  768.     if sys.platform.startswith("win"):
  769.         # .wait() argument constants
  770.         INFINITE = win32event.INFINITE
  771.         # .wait() return error codes
  772.         WAIT_FAILED = win32event.WAIT_FAILED
  773.         WAIT_TIMEOUT = win32event.WAIT_TIMEOUT
  774.         # creation "flags" constants
  775.         # XXX Should drop these and just document usage of
  776.         #     win32process.CREATE_* constants on windows.
  777.         CREATE_NEW_CONSOLE = win32process.CREATE_NEW_CONSOLE
  778.     else:
  779.         # .wait() argument constants
  780.         INFINITE = 0
  781.         # .wait() return error codes
  782.         WAIT_TIMEOUT = 258
  783.         WAIT_FAILED = -1
  784.         # creation "flags" constants
  785.         CREATE_NEW_CONSOLE = 0x10 # same as win32process.CREATE_NEW_CONSOLE
  786.     def __init__(self, cmd, cwd=None, env=None, flags=0):
  787.         """Create a child process.
  788.         "cmd" is a command string or argument vector to spawn.
  789.         "cwd" is a working directory in which to start the child process.
  790.         "env" is an environment dictionary for the child.
  791.         "flags" are system-specific process creation flags. On Windows
  792.             this can be a bitwise-OR of any of the win32process.CREATE_*
  793.             constants (Note: win32process.CREATE_NEW_PROCESS_GROUP is always
  794.             OR'd in). On Unix, this is currently ignored.
  795.         """
  796.         log.info("Process.__init__(cmd=%r, cwd=%r, env=%r, flags=%r)",
  797.                  cmd, cwd, env, flags)
  798.         self._cmd = cmd
  799.         if not self._cmd:
  800.             raise ProcessError("You must specify a command.")
  801.         self._cwd = cwd
  802.         self._env = env
  803.         self._flags = flags
  804.         if sys.platform.startswith("win"):
  805.             self._flags |= win32process.CREATE_NEW_PROCESS_GROUP
  806.         self._killed = False
  807.         if sys.platform.startswith("win"):
  808.             self._startOnWindows()
  809.         else:
  810.             self.__retvalCache = None
  811.             self._startOnUnix()
  812.     def isKilled(self):
  813.         return self._killed
  814.     
  815.     def _runChildOnUnix(self):
  816.         #XXX Errors running the child do *not* get communicated back.
  817.         #XXX Perhaps we should *always* prefix with '/bin/sh -c'? There is a
  818.         #    disparity btwn how this works on Linux and Windows.
  819.         if isinstance(self._cmd, types.StringTypes):
  820.             # This is easier than trying to reproduce shell interpretation to
  821.             # separate the arguments.
  822.             cmd = ['/bin/sh', '-c', self._cmd]            
  823.         else:
  824.             cmd = self._cmd                    
  825.                            
  826.         # Close all file descriptors (except std*) inherited from the parent.
  827.         MAXFD = 256 # Max number of file descriptors (os.getdtablesize()???)
  828.         for i in range(3, MAXFD):
  829.             try:
  830.                 os.close(i)
  831.             except OSError:
  832.                 pass
  833.         try:
  834.             if self._env:
  835.                 os.execvpe(cmd[0], cmd, self._env)
  836.             else:
  837.                 os.execvp(cmd[0], cmd)
  838.         finally:
  839.             os._exit(1)  # Should never get here.
  840.     def _forkAndExecChildOnUnix(self):
  841.         """Fork and start the child process.
  842.         Sets self._pid as a side effect.
  843.         """
  844.         pid = os.fork()
  845.         if pid == 0: # child
  846.             self._runChildOnUnix()
  847.         # parent
  848.         self._pid = pid
  849.     def _startOnUnix(self):
  850.         if self._cwd:
  851.             oldDir = os.getcwd()
  852.             try:
  853.                 os.chdir(self._cwd)
  854.             except OSError, ex:
  855.                 raise ProcessError(msg=str(ex), errno=ex.errno)
  856.         self._forkAndExecChildOnUnix()
  857.         # parent
  858.         if self._cwd:
  859.             os.chdir(oldDir)
  860.     def _startOnWindows(self):
  861.         if type(self._cmd) in (types.ListType, types.TupleType):
  862.             # And arg vector was passed in.
  863.             cmd = _joinArgv(self._cmd)
  864.         else:
  865.             cmd = self._cmd
  866.         si = win32process.STARTUPINFO()
  867.         si.dwFlags = win32process.STARTF_USESHOWWINDOW
  868.         si.wShowWindow = SW_SHOWDEFAULT
  869.         # disabled by alch 01-04-2004
  870.         # didn't work for quoted commands
  871. ##        if and not (self._flags & self.CREATE_NEW_CONSOLE):
  872. ##            #XXX This is hacky.
  873. ##            # We cannot then use _fixupCommand because this will cause a
  874. ##            # shell to be openned as the command is launched. Therefore need
  875. ##            # to ensure be have the full path to the executable to launch.
  876. ##            try:
  877. ##                cmd = _whichFirstArg(cmd, self._env)
  878. ##            except ProcessError:
  879. ##                # Could not find the command, perhaps it is an internal
  880. ##                # shell command -- fallback to _fixupCommand
  881. ##                cmd = _fixupCommand(cmd, self._env)
  882. ##        else:
  883. ##            cmd = _fixupCommand(cmd, self._env)
  884.         log.debug("cmd = %r", cmd)
  885.         # Start the child process.
  886.         try:
  887.             self._hProcess, self._hThread, self._processId, self._threadId
  888.                 = _SaferCreateProcess(
  889.                     None,           # app name
  890.                     cmd,            # command line
  891.                     None,           # process security attributes
  892.                     None,           # primary thread security attributes
  893.                     0,              # handles are inherited
  894.                     self._flags,    # creation flags
  895.                     self._env,      # environment
  896.                     self._cwd,      # current working directory
  897.                     si)             # STARTUPINFO pointer
  898.             win32api.CloseHandle(self._hThread)
  899.         except win32api.error, ex:
  900.             raise ProcessError(msg="Error creating process for '%s': %s"
  901.                                    % (cmd, ex.args[2]),
  902.                                errno=ex.args[0])
  903.     def wait(self, timeout=None):
  904.         """Wait for the started process to complete.
  905.         "timeout" (on Windows) is a floating point number of seconds after
  906.             which to timeout.  Default is win32event.INFINITE.
  907.         "timeout" (on Unix) is akin to the os.waitpid() "options" argument
  908.             (os.WNOHANG may be used to return immediately if the process has
  909.             not exited). Default is 0, i.e. wait forever.
  910.         If the wait time's out it will raise a ProcessError. Otherwise it
  911.         will return the child's exit value (on Windows) or the child's exit
  912.         status excoded as per os.waitpid() (on Linux):
  913.             "a 16-bit number, whose low byte is the signal number that killed
  914.             the process, and whose high byte is the exit status (if the
  915.             signal number is zero); the high bit of the low byte is set if a
  916.             core file was produced."
  917.         In the latter case, use the os.W*() methods to interpret the return
  918.         value.
  919.         """
  920.         # XXX Or should returning the exit value be move out to another
  921.         #     function as on Win32 process control? If so, then should
  922.         #     perhaps not make WaitForSingleObject semantic transformation.
  923.         if sys.platform.startswith("win"):
  924.             if timeout is None:
  925.                 timeout = win32event.INFINITE
  926.             else:
  927.                 timeout = timeout * 1000.0 # Win32 API's timeout is in millisecs
  928.             rc = win32event.WaitForSingleObject(self._hProcess, int(timeout))
  929.             if rc == win32event.WAIT_FAILED:
  930.                 raise ProcessError("'WAIT_FAILED' when waiting for process to "
  931.                                    "terminate: %r" % self._cmd, rc)
  932.             elif rc == win32event.WAIT_TIMEOUT:
  933.                 raise ProcessError("'WAIT_TIMEOUT' when waiting for process to "
  934.                                    "terminate: %r" % self._cmd, rc)
  935.             retval = win32process.GetExitCodeProcess(self._hProcess)
  936.         else:
  937.             # os.waitpid() will raise:
  938.             #       OSError: [Errno 10] No child processes
  939.             # on subsequent .wait() calls. Change these semantics to have
  940.             # subsequent .wait() calls return the exit status and return
  941.             # immediately without raising an exception.
  942.             # (XXX It would require synchronization code to handle the case
  943.             # of multiple simultaneous .wait() requests, however we can punt
  944.             # on that because it is moot while Linux still has the problem
  945.             # for which _ThreadFixer() exists.)
  946.             if self.__retvalCache is not None:
  947.                 retval = self.__retvalCache
  948.             else:
  949.                 if timeout is None:
  950.                     timeout = 0
  951.                 pid, sts = os.waitpid(self._pid, timeout)
  952.                 if pid == self._pid:
  953.                     self.__retvalCache = retval = sts
  954.                 else:
  955.                     raise ProcessError("Wait for process timed out.",
  956.                                        self.WAIT_TIMEOUT)
  957.         return retval
  958.     def kill(self, exitCode=0, gracePeriod=1.0, sig=None):        
  959.         """Kill process.
  960.         "exitCode" [deprecated, not supported] (Windows only) is the
  961.             code the terminated process should exit with.
  962.         "gracePeriod" (Windows only) is a number of seconds the process is
  963.             allowed to shutdown with a WM_CLOSE signal before a hard
  964.             terminate is called.
  965.         "sig" (Unix only) is the signal to use to kill the process. Defaults
  966.             to signal.SIGKILL. See os.kill() for more information.
  967.         Windows:
  968.             Try for an orderly shutdown via WM_CLOSE.  If still running
  969.             after gracePeriod (1 sec. default), terminate.
  970.         """
  971.         self._killed = True
  972.         if sys.platform.startswith("win"):
  973.             import win32gui
  974.             # Send WM_CLOSE to windows in this process group.
  975.             win32gui.EnumWindows(self._close_, 0)
  976.             # Send Ctrl-Break signal to all processes attached to this
  977.             # console. This is supposed to trigger shutdown handlers in
  978.             # each of the processes.
  979.             try:
  980.                 win32api.GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
  981.                                                   self._processId)
  982.             except AttributeError:
  983.                 log.warn("The win32api module does not have "
  984.                          "GenerateConsoleCtrlEvent(). This may mean that "
  985.                          "parts of this process group have NOT been killed.")
  986.             except win32api.error, ex:
  987.                 if ex.args[0] not in (6, 87):
  988.                     # Ignore the following:
  989.                     #   api_error: (87, 'GenerateConsoleCtrlEvent', 'The parameter is incorrect.')
  990.                     #   api_error: (6, 'GenerateConsoleCtrlEvent', 'The handle is invalid.')
  991.                     # Get error 6 if there is no console.
  992.                     raise
  993.             # Last resort: call TerminateProcess if it has not yet.
  994.             retval = 0
  995.             try:
  996.                 self.wait(gracePeriod)
  997.             except ProcessError, ex:
  998.                 log.info("[%s] Process.kill: calling TerminateProcess", id(self))
  999.                 win32process.TerminateProcess(self._hProcess, -1)
  1000.                 win32api.Sleep(100) # wait for resources to be released
  1001.         else:
  1002.             if sig is None:
  1003.                 sig = signal.SIGKILL
  1004.             try:
  1005.                 os.kill(self._pid, sig)
  1006.             except OSError, ex:
  1007.                 if ex.errno != 3:
  1008.                     # Ignore:   OSError: [Errno 3] No such process
  1009.                     raise        
  1010.     def _close_(self, hwnd, dummy):
  1011.         """Callback used by .kill() on Windows.
  1012.         EnumWindows callback - sends WM_CLOSE to any window owned by this
  1013.         process.
  1014.         """
  1015.         threadId, processId = win32process.GetWindowThreadProcessId(hwnd)
  1016.         if processId == self._processId:
  1017.             import win32gui
  1018.             win32gui.PostMessage(hwnd, WM_CLOSE, 0, 0)
  1019. class ProcessOpen(Process):
  1020.     """Create a process and setup pipes to it standard handles.
  1021.     This is a super popen3.
  1022.     """
  1023.     # TODO:
  1024.     #   - Share some implementation with Process and ProcessProxy.
  1025.     #
  1026.     def __init__(self, cmd, mode='t', cwd=None, env=None):
  1027.         """Create a Process with proxy threads for each std handle.
  1028.         "cmd" is the command string or argument vector to run.
  1029.         "mode" (Windows only) specifies whether the pipes used to communicate
  1030.             with the child are openned in text, 't', or binary, 'b', mode.
  1031.             This is ignored on platforms other than Windows. Default is 't'.
  1032.         "cwd" optionally specifies the directory in which the child process
  1033.             should be started. Default is None, a.k.a. inherits the cwd from
  1034.             the parent.
  1035.         "env" is optionally a mapping specifying the environment in which to
  1036.             start the child. Default is None, a.k.a. inherits the environment
  1037.             of the parent.
  1038.         """
  1039.         # Keep a reference to ensure it is around for this object's destruction.
  1040.         self.__log = log
  1041.         log.info("ProcessOpen.__init__(cmd=%r, mode=%r, cwd=%r, env=%r)",
  1042.                  cmd, mode, cwd, env)
  1043.         self._cmd = cmd
  1044.         if not self._cmd:
  1045.             raise ProcessError("You must specify a command.")
  1046.         self._cwd = cwd
  1047.         self._env = env
  1048.         self._mode = mode
  1049.         if self._mode not in ('t', 'b'):
  1050.             raise ProcessError("'mode' must be 't' or 'b'.")
  1051.         self._closed = 0
  1052.         
  1053.         self._killed = False
  1054.         if sys.platform.startswith("win"):
  1055.             self._startOnWindows()
  1056.         else:
  1057.             self.__retvalCache = None
  1058.             self._startOnUnix()
  1059.         _registerProcess(self)
  1060.     def __del__(self):
  1061.         #XXX Should probably not rely upon this.
  1062.         logres.info("[%s] ProcessOpen.__del__()", id(self))
  1063.         self.close()
  1064.         del self.__log # drop reference
  1065.     def close(self):
  1066.         if not self._closed:
  1067.             self.__log.info("[%s] ProcessOpen.close()" % id(self))
  1068.             # Ensure that all IOBuffer's are closed. If they are not, these
  1069.             # can cause hangs.
  1070.             try:
  1071.                 self.__log.info("[%s] ProcessOpen: closing stdin (%r)."
  1072.                                 % (id(self), self.stdin))
  1073.                 self.stdin.close()
  1074.             except AttributeError:
  1075.                 # May not have gotten far enough in the __init__ to set
  1076.                 # self.stdin, etc.
  1077.                 pass
  1078.             try:
  1079.                 self.__log.info("[%s] ProcessOpen: closing stdout (%r)."
  1080.                                 % (id(self), self.stdout))
  1081.                 self.stdout.close()
  1082.             except AttributeError:
  1083.                 # May not have gotten far enough in the __init__ to set
  1084.                 # self.stdout, etc.
  1085.                 pass
  1086.             try:
  1087.                 self.__log.info("[%s] ProcessOpen: closing stderr (%r)."
  1088.                                 % (id(self), self.stderr))
  1089.                 self.stderr.close()
  1090.             except AttributeError:
  1091.                 # May not have gotten far enough in the __init__ to set
  1092.                 # self.stderr, etc.
  1093.                 pass
  1094.             self._closed = 1
  1095.     def _forkAndExecChildOnUnix(self, fdChildStdinRd, fdChildStdoutWr,
  1096.                                 fdChildStderrWr):
  1097.         """Fork and start the child process.
  1098.         Sets self._pid as a side effect.
  1099.         """
  1100.         pid = os.fork()
  1101.         if pid == 0: # child
  1102.             os.dup2(fdChildStdinRd, 0)
  1103.             os.dup2(fdChildStdoutWr, 1)
  1104.             os.dup2(fdChildStderrWr, 2)
  1105.             self._runChildOnUnix()
  1106.         # parent
  1107.         self._pid = pid
  1108.     def _startOnUnix(self):
  1109.         # Create pipes for std handles.
  1110.         fdChildStdinRd, fdChildStdinWr = os.pipe()
  1111.         fdChildStdoutRd, fdChildStdoutWr = os.pipe()
  1112.         fdChildStderrRd, fdChildStderrWr = os.pipe()
  1113.         if self._cwd:
  1114.             oldDir = os.getcwd()
  1115.             try:
  1116.                 os.chdir(self._cwd)
  1117.             except OSError, ex:
  1118.                 raise ProcessError(msg=str(ex), errno=ex.errno)
  1119.         self._forkAndExecChildOnUnix(fdChildStdinRd, fdChildStdoutWr,
  1120.                                      fdChildStderrWr)
  1121.         if self._cwd:
  1122.             os.chdir(oldDir)
  1123.         os.close(fdChildStdinRd)
  1124.         os.close(fdChildStdoutWr)
  1125.         os.close(fdChildStderrWr)
  1126.         self.stdin = _FileWrapper(descriptor=fdChildStdinWr)
  1127.         logres.info("[%s] ProcessOpen._start(): create child stdin: %r",
  1128.                     id(self), self.stdin)
  1129.         self.stdout = _FileWrapper(descriptor=fdChildStdoutRd)
  1130.         logres.info("[%s] ProcessOpen._start(): create child stdout: %r",
  1131.                     id(self), self.stdout)
  1132.         self.stderr = _FileWrapper(descriptor=fdChildStderrRd)
  1133.         logres.info("[%s] ProcessOpen._start(): create child stderr: %r",
  1134.                     id(self), self.stderr)
  1135.     def _startOnWindows(self):
  1136.         if type(self._cmd) in (types.ListType, types.TupleType):
  1137.             # An arg vector was passed in.
  1138.             cmd = _joinArgv(self._cmd)
  1139.         else:
  1140.             cmd = self._cmd
  1141.         # Create pipes for std handles.
  1142.         # (Set the bInheritHandle flag so pipe handles are inherited.)
  1143.         # alch 15-3-4 added win9x check, where we have no SECURITY_ATTRIBUTES
  1144.         isWin9x = win32api.GetVersionEx()[3] == VER_PLATFORM_WIN32_WINDOWS
  1145.         if not isWin9x:        
  1146.             saAttr = pywintypes.SECURITY_ATTRIBUTES()
  1147.             saAttr.bInheritHandle = 1
  1148.         else:
  1149.             saAttr = None
  1150.         #XXX Should maybe try with os.pipe. Dunno what that does for
  1151.         #    inheritability though.
  1152.         hChildStdinRd, hChildStdinWr = win32pipe.CreatePipe(saAttr, 0)
  1153.         hChildStdoutRd, hChildStdoutWr = win32pipe.CreatePipe(saAttr, 0)
  1154.         hChildStderrRd, hChildStderrWr = win32pipe.CreatePipe(saAttr, 0)
  1155.         try:
  1156.             # Duplicate the parent ends of the pipes so they are not
  1157.             # inherited.
  1158.             hChildStdinWrDup = win32api.DuplicateHandle(
  1159.                 win32api.GetCurrentProcess(),
  1160.                 hChildStdinWr,
  1161.                 win32api.GetCurrentProcess(),
  1162.                 0,
  1163.                 0, # not inherited
  1164.                 DUPLICATE_SAME_ACCESS)
  1165.             win32api.CloseHandle(hChildStdinWr)
  1166.             self._hChildStdinWr = hChildStdinWrDup
  1167.             hChildStdoutRdDup = win32api.DuplicateHandle(
  1168.                 win32api.GetCurrentProcess(),
  1169.                 hChildStdoutRd,
  1170.                 win32api.GetCurrentProcess(),
  1171.                 0,
  1172.                 0, # not inherited
  1173.                 DUPLICATE_SAME_ACCESS)
  1174.             win32api.CloseHandle(hChildStdoutRd)
  1175.             self._hChildStdoutRd = hChildStdoutRdDup
  1176.             hChildStderrRdDup = win32api.DuplicateHandle(
  1177.                 win32api.GetCurrentProcess(),
  1178.                 hChildStderrRd,
  1179.                 win32api.GetCurrentProcess(),
  1180.                 0,
  1181.                 0, # not inherited
  1182.                 DUPLICATE_SAME_ACCESS)
  1183.             win32api.CloseHandle(hChildStderrRd)
  1184.             self._hChildStderrRd = hChildStderrRdDup
  1185.             # Set the translation mode and buffering.
  1186.             if self._mode == 't':
  1187.                 flags = os.O_TEXT
  1188.             else:
  1189.                 flags = 0
  1190.             fdChildStdinWr = msvcrt.open_osfhandle(self._hChildStdinWr, flags)
  1191.             fdChildStdoutRd = msvcrt.open_osfhandle(self._hChildStdoutRd, flags)
  1192.             fdChildStderrRd = msvcrt.open_osfhandle(self._hChildStderrRd, flags)
  1193.             self.stdin = _FileWrapper(descriptor=fdChildStdinWr,
  1194.                                       handle=self._hChildStdinWr)
  1195.             logres.info("[%s] ProcessOpen._start(): create child stdin: %r",
  1196.                         id(self), self.stdin)
  1197.             self.stdout = _FileWrapper(descriptor=fdChildStdoutRd,
  1198.                                        handle=self._hChildStdoutRd)
  1199.             logres.info("[%s] ProcessOpen._start(): create child stdout: %r",
  1200.                         id(self), self.stdout)
  1201.             self.stderr = _FileWrapper(descriptor=fdChildStderrRd,
  1202.                                        handle=self._hChildStderrRd)
  1203.             logres.info("[%s] ProcessOpen._start(): create child stderr: %r",
  1204.                         id(self), self.stderr)
  1205.             # Start the child process.
  1206.             si = win32process.STARTUPINFO()
  1207.             si.dwFlags = win32process.STARTF_USESHOWWINDOW
  1208.             si.wShowWindow = 0 # SW_HIDE
  1209.             si.hStdInput = hChildStdinRd
  1210.             si.hStdOutput = hChildStdoutWr
  1211.             si.hStdError = hChildStderrWr
  1212.             si.dwFlags |= win32process.STARTF_USESTDHANDLES
  1213.               
  1214.             cmd = _fixupCommand(cmd, self._env)
  1215.             creationFlags = win32process.CREATE_NEW_PROCESS_GROUP
  1216.             try:
  1217.                 self._hProcess, hThread, self._processId, threadId
  1218.                     = _SaferCreateProcess(
  1219.                         None,           # app name
  1220.                         cmd,            # command line
  1221.                         None,           # process security attributes
  1222.                         None,           # primary thread security attributes
  1223.                         1,              # handles are inherited
  1224.                         creationFlags,  # creation flags
  1225.                         self._env,      # environment
  1226.                         self._cwd,      # current working directory
  1227.                         si)             # STARTUPINFO pointer
  1228.             except win32api.error, ex:
  1229.                 raise ProcessError(msg=ex.args[2], errno=ex.args[0])
  1230.             win32api.CloseHandle(hThread)
  1231.         finally:
  1232.             # Close child ends of pipes on the parent's side (the
  1233.             # parent's ends of the pipe are closed in the _FileWrappers.)
  1234.             win32file.CloseHandle(hChildStdinRd)
  1235.             win32file.CloseHandle(hChildStdoutWr)
  1236.             win32file.CloseHandle(hChildStderrWr)
  1237.     def wait(self, timeout=None):
  1238.         """Wait for the started process to complete.
  1239.         "timeout" (on Windows) is a floating point number of seconds after
  1240.             which to timeout.  Default is win32event.INFINITE.
  1241.         "timeout" (on Unix) is akin to the os.waitpid() "options" argument
  1242.             (os.WNOHANG may be used to return immediately if the process has
  1243.             not exited). Default is 0, i.e. wait forever.
  1244.         If the wait time's out it will raise a ProcessError. Otherwise it
  1245.         will return the child's exit value (on Windows) or the child's exit
  1246.         status excoded as per os.waitpid() (on Linux):
  1247.             "a 16-bit number, whose low byte is the signal number that killed
  1248.             the process, and whose high byte is the exit status (if the
  1249.             signal number is zero); the high bit of the low byte is set if a
  1250.             core file was produced."
  1251.         In the latter case, use the os.W*() methods to interpret the return
  1252.         value.
  1253.         """
  1254.         # XXX Or should returning the exit value be move out to another
  1255.         #    function as on Win32 process control? If so, then should
  1256.         #    perhaps not make WaitForSingleObject semantic
  1257.         #    transformation.
  1258.         # TODO:
  1259.         #   - Need to rationalize the .wait() API for Windows vs. Unix.
  1260.         #     It is a real pain in the current situation.
  1261.         if sys.platform.startswith("win"):
  1262.             if timeout is None:
  1263.                 timeout = win32event.INFINITE
  1264.             else:
  1265.                 timeout = timeout * 1000.0 # Win32 API's timeout is in millisecs
  1266.             rc = win32event.WaitForSingleObject(self._hProcess, int(timeout))
  1267.             if rc == win32event.WAIT_FAILED:
  1268.                 raise ProcessError("'WAIT_FAILED' when waiting for process to "
  1269.                                    "terminate: %r" % self._cmd, rc)
  1270.             elif rc == win32event.WAIT_TIMEOUT:
  1271.                 raise ProcessError("'WAIT_TIMEOUT' when waiting for process to "
  1272.                                    "terminate: %r" % self._cmd, rc)
  1273.             retval = win32process.GetExitCodeProcess(self._hProcess)
  1274.         else:
  1275.             # os.waitpid() will raise:
  1276.             #       OSError: [Errno 10] No child processes
  1277.             # on subsequent .wait() calls. Change these semantics to have
  1278.             # subsequent .wait() calls return the exit status and return
  1279.             # immediately without raising an exception.
  1280.             # (XXX It would require synchronization code to handle the case
  1281.             # of multiple simultaneous .wait() requests, however we can punt
  1282.             # on that because it is moot while Linux still has the problem
  1283.             # for which _ThreadFixer() exists.)
  1284.             if self.__retvalCache is not None:
  1285.                 retval = self.__retvalCache
  1286.             else:
  1287.                 if timeout is None:
  1288.                     timeout = 0
  1289.                 pid, sts = os.waitpid(self._pid, timeout)
  1290.                 if pid == self._pid:
  1291.                     self.__retvalCache = retval = sts
  1292.                 else:
  1293.                     raise ProcessError("Wait for process timed out.",
  1294.                                        self.WAIT_TIMEOUT)
  1295.         _unregisterProcess(self)
  1296.         return retval
  1297.     def kill(self, exitCode=0, gracePeriod=1.0, sig=None):
  1298.         """Kill process.
  1299.         "exitCode" [deprecated, not supported] (Windows only) is the
  1300.             code the terminated process should exit with.
  1301.         "gracePeriod" (Windows only) is a number of seconds the process is
  1302.             allowed to shutdown with a WM_CLOSE signal before a hard
  1303.             terminate is called.
  1304.         "sig" (Unix only) is the signal to use to kill the process. Defaults
  1305.             to signal.SIGKILL. See os.kill() for more information.
  1306.         Windows:
  1307.             Try for an orderly shutdown via WM_CLOSE.  If still running
  1308.             after gracePeriod (1 sec. default), terminate.
  1309.         """
  1310.         self._killed = True
  1311.         if sys.platform.startswith("win"):
  1312.             import win32gui
  1313.             # Send WM_CLOSE to windows in this process group.
  1314.             win32gui.EnumWindows(self._close_, 0)
  1315.             # Send Ctrl-Break signal to all processes attached to this
  1316.             # console. This is supposed to trigger shutdown handlers in
  1317.             # each of the processes.
  1318.             try:
  1319.                 win32api.GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
  1320.                                                   self._processId)
  1321.             except AttributeError:
  1322.                 log.warn("The win32api module does not have "
  1323.                          "GenerateConsoleCtrlEvent(). This may mean that "
  1324.                          "parts of this process group have NOT been killed.")
  1325.             except win32api.error, ex:
  1326.                 if ex.args[0] not in (6, 87):
  1327.                     # Ignore the following:
  1328.                     #   api_error: (87, 'GenerateConsoleCtrlEvent', 'The parameter is incorrect.')
  1329.                     #   api_error: (6, 'GenerateConsoleCtrlEvent', 'The handle is invalid.')
  1330.                     # Get error 6 if there is no console.
  1331.                     raise
  1332.             # Last resort: call TerminateProcess if it has not yet.
  1333.             retval = 0
  1334.             try:
  1335.                 self.wait(gracePeriod)
  1336.             except ProcessError, ex:
  1337.                 log.info("[%s] Process.kill: calling TerminateProcess", id(self))
  1338.                 win32process.TerminateProcess(self._hProcess, -1)
  1339.                 win32api.Sleep(100) # wait for resources to be released
  1340.         else:
  1341.             if sig is None:
  1342.                 sig = signal.SIGKILL
  1343.             try:
  1344.                 os.kill(self._pid, sig)
  1345.             except OSError, ex:
  1346.                 if ex.errno != 3:
  1347.                     # Ignore:   OSError: [Errno 3] No such process
  1348.                     raise
  1349.         _unregisterProcess(self)        
  1350.     def _close_(self, hwnd, dummy):
  1351.         """Callback used by .kill() on Windows.
  1352.         EnumWindows callback - sends WM_CLOSE to any window owned by this
  1353.         process.
  1354.         """
  1355.         threadId, processId = win32process.GetWindowThreadProcessId(hwnd)
  1356.         if processId == self._processId:
  1357.             import win32gui
  1358.             win32gui.PostMessage(hwnd, WM_CLOSE, 0, 0)
  1359. class ProcessProxy(Process):
  1360.     """Create a process and proxy communication via the standard handles.
  1361.     """
  1362.     #XXX To add to docstring:
  1363.     #   - stdout/stderr proxy handling
  1364.     #   - stdin proxy handling
  1365.     #   - termination
  1366.     #   - how to .start(), i.e. basic usage rules
  1367.     #   - mention that pased in stdin/stdout/stderr objects have to
  1368.     #     implement at least .write (is .write correct for stdin)?
  1369.     #   - if you pass in stdin, stdout, and/or stderr streams it is the
  1370.     #     user's responsibility to close them afterwards.
  1371.     #   - 'cmd' arg can be a command string or an arg vector
  1372.     #   - etc.
  1373.     #TODO:
  1374.     #   - .suspend() and .resume()? See Win32::Process Perl module.
  1375.     #
  1376.     def __init__(self, cmd, mode='t', priority='n', cwd=None, env=None,
  1377.                  stdin=None, stdout=None, stderr=None):
  1378.         """Create a Process with proxy threads for each std handle.
  1379.         "cmd" is the command string or argument vector to run.
  1380.         "mode" (Windows only) specifies whether the pipes used to communicate
  1381.             with the child are openned in text, 't', or binary, 'b', mode.
  1382.             This is ignored on platforms other than Windows. Default is 't'.
  1383.         "priority" (Windows only) specifies new process's priority class, 
  1384.             which is used to determine the scheduling priorities of the 
  1385.             process's threads. This is ignored on platforms other than Windows
  1386.             (because alch does not know how to transleate these values
  1387.             for nice on Unix). 
  1388.             Possible values are: 'n' - normal, 'i' - idle, 'h' - high and 
  1389.             'r' - realtime. Default is 'n'.
  1390.         "cwd" optionally specifies the directory in which the child process
  1391.             should be started. Default is None, a.k.a. inherits the cwd from
  1392.             the parent.
  1393.         "env" is optionally a mapping specifying the environment in which to
  1394.             start the child. Default is None, a.k.a. inherits the environment
  1395.             of the parent.
  1396.         "stdin", "stdout", "stderr" can be used to specify objects with
  1397.             file-like interfaces to handle read (stdout/stderr) and write
  1398.             (stdin) events from the child. By default a process.IOBuffer
  1399.             instance is assigned to each handler. IOBuffer may be
  1400.             sub-classed. See the IOBuffer doc string for more information.
  1401.         """
  1402.         # Keep a reference to ensure it is around for this object's destruction.
  1403.         self.__log = log
  1404.         self._priority = priority
  1405.         log.info("ProcessProxy.__init__(cmd=%r, mode=%r, cwd=%r, env=%r, "
  1406.                  "stdin=%r, stdout=%r, stderr=%r)",
  1407.                  cmd, mode, cwd, env, stdin, stdout, stderr)
  1408.         self._cmd = cmd
  1409.         if not self._cmd:
  1410.             raise ProcessError("You must specify a command.")
  1411.         self._mode = mode
  1412.         if self._mode not in ('t', 'b'):
  1413.             raise ProcessError("'mode' must be 't' or 'b'.")
  1414.         self._cwd = cwd
  1415.         self._env = env
  1416.         if stdin is None:
  1417.             self.stdin = IOBuffer(name='<stdin>')
  1418.         else:
  1419.             self.stdin = stdin
  1420.         if stdout is None:
  1421.             self.stdout = IOBuffer(name='<stdout>')
  1422.         else:
  1423.             self.stdout = stdout
  1424.         if stderr is None:
  1425.             self.stderr = IOBuffer(name='<stderr>')
  1426.         else:
  1427.             self.stderr = stderr
  1428.         self._closed = 0
  1429.         self._killed = False
  1430.         if sys.platform.startswith("win"):
  1431.             self._startOnWindows()
  1432.         else:
  1433.             self.__retvalCache = None
  1434.             self._startOnUnix()
  1435.         _registerProcess(self)
  1436.     def __del__(self):
  1437.         #XXX Should probably not rely upon this.        
  1438.         logres.info("[%s] ProcessProxy.__del__()", id(self))
  1439.         self.close()
  1440.         del self.__log # drop reference
  1441.     def close(self):        
  1442.         if not self._closed:
  1443.             self.__log.info("[%s] ProcessProxy.close()" % id(self))
  1444.             # Ensure that all IOBuffer's are closed. If they are not, these
  1445.             # can cause hangs.
  1446.             self.__log.info("[%s] ProcessProxy: closing stdin (%r)."
  1447.                             % (id(self), self.stdin))
  1448.             try:
  1449.                 self.stdin.close()
  1450.                 self._stdinProxy.join()
  1451.             except AttributeError:
  1452.                 # May not have gotten far enough in the __init__ to set
  1453.                 # self.stdin, etc.
  1454.                 pass
  1455.             self.__log.info("[%s] ProcessProxy: closing stdout (%r)."
  1456.                             % (id(self), self.stdout))
  1457.             try:
  1458.                 self.stdout.close()
  1459.                 if self._stdoutProxy is not threading.currentThread():
  1460.                     self._stdoutProxy.join()
  1461.             except AttributeError:
  1462.                 # May not have gotten far enough in the __init__ to set
  1463.                 # self.stdout, etc.
  1464.                 pass
  1465.             self.__log.info("[%s] ProcessProxy: closing stderr (%r)."
  1466.                             % (id(self), self.stderr))
  1467.             try:
  1468.                 self.stderr.close()
  1469.                 if self._stderrProxy is not threading.currentThread():
  1470.                     self._stderrProxy.join()
  1471.             except AttributeError:
  1472.                 # May not have gotten far enough in the __init__ to set
  1473.                 # self.stderr, etc.
  1474.                 pass
  1475.             self._closed = 1
  1476.             self.__log.info("[%s] ProcessProxy.closed = 1" % id(self))
  1477.             
  1478.     def _forkAndExecChildOnUnix(self, fdChildStdinRd, fdChildStdoutWr,
  1479.                                 fdChildStderrWr):
  1480.         """Fork and start the child process.
  1481.         Sets self._pid as a side effect.
  1482.         """
  1483.         pid = os.fork()
  1484.         if pid == 0: # child            
  1485.             os.dup2(fdChildStdinRd, 0)
  1486.             os.dup2(fdChildStdoutWr, 1)
  1487.             os.dup2(fdChildStderrWr, 2)
  1488.             self._runChildOnUnix()
  1489.         # parent
  1490.         self._pid = pid
  1491.     def _startOnUnix(self):
  1492.         # Create pipes for std handles.
  1493.         fdChildStdinRd, fdChildStdinWr = os.pipe()
  1494.         fdChildStdoutRd, fdChildStdoutWr = os.pipe()
  1495.         fdChildStderrRd, fdChildStderrWr = os.pipe()
  1496.         if self._cwd:
  1497.             oldDir = os.getcwd()
  1498.             try:
  1499.                 os.chdir(self._cwd)
  1500.             except OSError, ex:
  1501.                 raise ProcessError(msg=str(ex), errno=ex.errno)
  1502.         self._forkAndExecChildOnUnix(fdChildStdinRd, fdChildStdoutWr,
  1503.                                      fdChildStderrWr)
  1504.         if self._cwd:
  1505.             os.chdir(oldDir)
  1506.         os.close(fdChildStdinRd)
  1507.         os.close(fdChildStdoutWr)
  1508.         os.close(fdChildStderrWr)
  1509.         childStdin = _FileWrapper(descriptor=fdChildStdinWr)
  1510.         logres.info("[%s] ProcessProxy._start(): create child stdin: %r",
  1511.                     id(self), childStdin)
  1512.         childStdout = _FileWrapper(descriptor=fdChildStdoutRd)
  1513.         logres.info("[%s] ProcessProxy._start(): create child stdout: %r",
  1514.                     id(self), childStdout)
  1515.         childStderr = _FileWrapper(descriptor=fdChildStderrRd)
  1516.         logres.info("[%s] ProcessProxy._start(): create child stderr: %r",
  1517.                     id(self), childStderr)
  1518.         # Create proxy threads for the out pipes.
  1519.         self._stdinProxy = _InFileProxy(self.stdin, childStdin, name='<stdin>')
  1520.         self._stdinProxy.start()
  1521.         # Clean up the parent's side of <stdin> when it is observed that
  1522.         # the child has closed its side of <stdout> and <stderr>. (This
  1523.         # is one way of determining when it is appropriate to clean up
  1524.         # this pipe, with compromises. See the discussion at the top of
  1525.         # this module.)
  1526.         closer = _CountingCloser([self.stdin, childStdin, self], 2)
  1527.         self._stdoutProxy = _OutFileProxy(childStdout, self.stdout,
  1528.                                           [closer],
  1529.                                           name='<stdout>')
  1530.         self._stdoutProxy.start()
  1531.         self._stderrProxy = _OutFileProxy(childStderr, self.stderr,
  1532.                                           [closer],
  1533.                                           name='<stderr>')
  1534.         self._stderrProxy.start()
  1535.     def _startOnWindows(self):
  1536.         if type(self._cmd) in (types.ListType, types.TupleType):
  1537.             # An arg vector was passed in.
  1538.             cmd = _joinArgv(self._cmd)
  1539.         else:
  1540.             cmd = self._cmd
  1541.         # Create pipes for std handles.
  1542.         # (Set the bInheritHandle flag so pipe handles are inherited.)
  1543.         # alch 15-3-4 added win9x check, where we have no SECURITY_ATTRIBUTES
  1544.         isWin9x = win32api.GetVersionEx()[3] == VER_PLATFORM_WIN32_WINDOWS
  1545.         if not isWin9x:        
  1546.             saAttr = pywintypes.SECURITY_ATTRIBUTES()
  1547.             saAttr.bInheritHandle = 1
  1548.         else:
  1549.             saAttr = None
  1550.         
  1551.         #XXX Should maybe try with os.pipe. Dunno what that does for
  1552.         #    inheritability though.
  1553.         hChildStdinRd, hChildStdinWr = win32pipe.CreatePipe(saAttr, 0)
  1554.         hChildStdoutRd, hChildStdoutWr = win32pipe.CreatePipe(saAttr, 0)
  1555.         hChildStderrRd, hChildStderrWr = win32pipe.CreatePipe(saAttr, 0)
  1556.         try:
  1557.             # Duplicate the parent ends of the pipes so they are not
  1558.             # inherited.
  1559.             hChildStdinWrDup = win32api.DuplicateHandle(
  1560.                 win32api.GetCurrentProcess(),
  1561.                 hChildStdinWr,
  1562.                 win32api.GetCurrentProcess(),
  1563.                 0,
  1564.                 0, # not inherited
  1565.                 DUPLICATE_SAME_ACCESS)
  1566.             win32api.CloseHandle(hChildStdinWr)
  1567.             self._hChildStdinWr = hChildStdinWrDup
  1568.             hChildStdoutRdDup = win32api.DuplicateHandle(
  1569.                 win32api.GetCurrentProcess(),
  1570.                 hChildStdoutRd,
  1571.                 win32api.GetCurrentProcess(),
  1572.                 0,
  1573.                 0, # not inherited
  1574.                 DUPLICATE_SAME_ACCESS)
  1575.             win32api.CloseHandle(hChildStdoutRd)
  1576.             self._hChildStdoutRd = hChildStdoutRdDup
  1577.             hChildStderrRdDup = win32api.DuplicateHandle(
  1578.                 win32api.GetCurrentProcess(),
  1579.                 hChildStderrRd,
  1580.                 win32api.GetCurrentProcess(),
  1581.                 0,
  1582.                 0, # not inherited
  1583.                 DUPLICATE_SAME_ACCESS)
  1584.             win32api.CloseHandle(hChildStderrRd)
  1585.             self._hChildStderrRd = hChildStderrRdDup
  1586.             # Set the translation mode.
  1587.             if self._mode == 't':
  1588.                 flags = os.O_TEXT
  1589.                 mode = ''
  1590.             else:
  1591.                 flags = 0
  1592.                 mode = 'b'
  1593.             fdChildStdinWr = msvcrt.open_osfhandle(self._hChildStdinWr, flags)
  1594.             fdChildStdoutRd = msvcrt.open_osfhandle(self._hChildStdoutRd, flags)
  1595.             fdChildStderrRd = msvcrt.open_osfhandle(self._hChildStderrRd, flags)
  1596.             childStdin = _FileWrapper(descriptor=fdChildStdinWr,
  1597.                                       handle=self._hChildStdinWr)
  1598.             logres.info("[%s] ProcessProxy._start(): create child stdin: %r",
  1599.                         id(self), childStdin)
  1600.             childStdout = _FileWrapper(descriptor=fdChildStdoutRd,
  1601.                                        handle=self._hChildStdoutRd)
  1602.             logres.info("[%s] ProcessProxy._start(): create child stdout: %r",
  1603.                         id(self), childStdout)
  1604.             childStderr = _FileWrapper(descriptor=fdChildStderrRd,
  1605.                                        handle=self._hChildStderrRd)
  1606.             logres.info("[%s] ProcessProxy._start(): create child stderr: %r",
  1607.                         id(self), childStderr)
  1608.             # Start the child process.
  1609.             si = win32process.STARTUPINFO()
  1610.             si.dwFlags = win32process.STARTF_USESHOWWINDOW
  1611.             si.wShowWindow = 0 # SW_HIDE
  1612.             si.hStdInput = hChildStdinRd
  1613.             si.hStdOutput = hChildStdoutWr
  1614.             si.hStdError = hChildStderrWr
  1615.             si.dwFlags |= win32process.STARTF_USESTDHANDLES
  1616.             cmd = _fixupCommand(cmd, self._env)
  1617.             log.debug("cmd = %r", cmd)
  1618.             creationFlags = win32process.CREATE_NEW_PROCESS_GROUP
  1619.             if self._priority == 'h':
  1620.                 creationFlags |= win32process.HIGH_PRIORITY_CLASS
  1621.             elif self._priority == 'l':
  1622.                 creationFlags |= win32process.IDLE_PRIORITY_CLASS
  1623.             else:
  1624.                 creationFlags |= win32process.NORMAL_PRIORITY_CLASS
  1625.             try:
  1626.                 self._hProcess, hThread, self._processId, threadId
  1627.                     = _SaferCreateProcess(
  1628.                         None,           # app name
  1629.                         cmd,            # command line
  1630.                         None,           # process security attributes
  1631.                         None,           # primary thread security attributes
  1632.                         1,              # handles are inherited
  1633.                         creationFlags,  # creation flags
  1634.                         self._env,      # environment
  1635.                         self._cwd,      # current working directory
  1636.                         si)             # STARTUPINFO pointer
  1637.             except win32api.error, ex:
  1638.                 raise ProcessError(msg=ex.args[2], errno=ex.args[0])
  1639.             win32api.CloseHandle(hThread)            
  1640.         finally:
  1641.             # Close child ends of pipes on the parent's side (the
  1642.             # parent's ends of the pipe are closed in the _FileWrappers.)
  1643.             win32file.CloseHandle(hChildStdinRd)
  1644.             win32file.CloseHandle(hChildStdoutWr)
  1645.             win32file.CloseHandle(hChildStderrWr)
  1646.         # Create proxy threads for the pipes.
  1647.         self._stdinProxy = _InFileProxy(self.stdin, childStdin, name='<stdin>')
  1648.         self._stdinProxy.start()
  1649.         # Clean up the parent's side of <stdin> when it is observed that
  1650.         # the child has closed its side of <stdout>. (This is one way of
  1651.         # determining when it is appropriate to clean up this pipe, with
  1652.         # compromises. See the discussion at the top of this module.)
  1653.         self._stdoutProxy = _OutFileProxy(childStdout, self.stdout,
  1654.                                           [self.stdin, childStdin, self],
  1655.                                           name='<stdout>')
  1656.         self._stdoutProxy.start()
  1657.         self._stderrProxy = _OutFileProxy(childStderr, self.stderr,
  1658.                                           name='<stderr>')
  1659.         self._stderrProxy.start()
  1660.     def wait(self, timeout=None):
  1661.         """Wait for the started process to complete.
  1662.         "timeout" (on Windows) is a floating point number of seconds after
  1663.             which to timeout.  Default is win32event.INFINITE.
  1664.         "timeout" (on Unix) is akin to the os.waitpid() "options" argument
  1665.             (os.WNOHANG may be used to return immediately if the process has
  1666.             not exited). Default is 0, i.e. wait forever.
  1667.         If the wait time's out it will raise a ProcessError. Otherwise it
  1668.         will return the child's exit value (on Windows) or the child's exit
  1669.         status excoded as per os.waitpid() (on Linux):
  1670.             "a 16-bit number, whose low byte is the signal number that killed
  1671.             the process, and whose high byte is the exit status (if the
  1672.             signal number is zero); the high bit of the low byte is set if a
  1673.             core file was produced."
  1674.         In the latter case, use the os.W*() methods to interpret the return
  1675.         value.
  1676.         """
  1677.         # XXX Or should returning the exit value be move out to another
  1678.         #     function as on Win32 process control? If so, then should
  1679.         #     perhaps not make WaitForSingleObject semantic transformation.
  1680.         if sys.platform.startswith("win"):
  1681.             if timeout is None:
  1682.                 timeout = win32event.INFINITE
  1683.             else:
  1684.                 timeout = timeout * 1000.0 # Win32 API's timeout is in millisecs
  1685.             rc = win32event.WaitForSingleObject(self._hProcess, int(timeout))
  1686.             if rc == win32event.WAIT_FAILED:
  1687.                 raise ProcessError("'WAIT_FAILED' when waiting for process to "
  1688.                                    "terminate: %r" % self._cmd, rc)
  1689.             elif rc == win32event.WAIT_TIMEOUT:
  1690.                 raise ProcessError("'WAIT_TIMEOUT' when waiting for process to "
  1691.                                    "terminate: %r" % self._cmd, rc)
  1692.             retval = win32process.GetExitCodeProcess(self._hProcess)
  1693.         else:
  1694.             # os.waitpid() will raise:
  1695.             #       OSError: [Errno 10] No child processes
  1696.             # on subsequent .wait() calls. Change these semantics to have
  1697.             # subsequent .wait() calls return the exit status and return
  1698.             # immediately without raising an exception.
  1699.             # (XXX It would require synchronization code to handle the case
  1700.             # of multiple simultaneous .wait() requests, however we can punt
  1701.             # on that because it is moot while Linux still has the problem
  1702.             # for which _ThreadFixer() exists.)
  1703.             if self.__retvalCache is not None:
  1704.                 retval = self.__retvalCache
  1705.             else:
  1706.                 if timeout is None:
  1707.                     timeout = 0
  1708.                 pid, sts = os.waitpid(self._pid, timeout)
  1709.                 if pid == self._pid:
  1710.                     self.__retvalCache = retval = sts
  1711.                 else:
  1712.                     raise ProcessError("Wait for process timed out.",
  1713.                                        self.WAIT_TIMEOUT)
  1714.         _unregisterProcess(self)
  1715.         return retval
  1716.     def kill(self, exitCode=0, gracePeriod=1.0, sig=None):
  1717.         """Kill process.
  1718.         "exitCode" [deprecated, not supported] (Windows only) is the
  1719.             code the terminated process should exit with.
  1720.         "gracePeriod" (Windows only) is a number of seconds the process is
  1721.             allowed to shutdown with a WM_CLOSE signal before a hard
  1722.             terminate is called.
  1723.         "sig" (Unix only) is the signal to use to kill the process. Defaults
  1724.             to signal.SIGKILL. See os.kill() for more information.
  1725.         Windows:
  1726.             Try for an orderly shutdown via WM_CLOSE.  If still running
  1727.             after gracePeriod (1 sec. default), terminate.
  1728.         """
  1729.         self._killed = True
  1730.         if sys.platform.startswith("win"):
  1731.             log.info("[%s] Process.kill: ", id(self))
  1732.             import win32gui
  1733.             # Send WM_CLOSE to windows in this process group.
  1734.             win32gui.EnumWindows(self._close_, 0)
  1735.             # Send Ctrl-Break signal to all processes attached to this
  1736.             # console. This is supposed to trigger shutdown handlers in
  1737.             # each of the processes.
  1738.             try:
  1739.                 win32api.GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
  1740.                                                   self._processId)
  1741.             except AttributeError:
  1742.                 log.warn("The win32api module does not have "
  1743.                          "GenerateConsoleCtrlEvent(). This may mean that "
  1744.                          "parts of this process group have NOT been killed.")
  1745.             except win32api.error, ex:
  1746.                 if ex.args[0] not in (6, 87):
  1747.                     # Ignore the following:
  1748.                     #   api_error: (87, 'GenerateConsoleCtrlEvent', 'The parameter is incorrect.')
  1749.                     #   api_error: (6, 'GenerateConsoleCtrlEvent', 'The handle is invalid.')
  1750.                     # Get error 6 if there is no console.
  1751.                     raise
  1752.             # Last resort: call TerminateProcess if it has not yet.
  1753.             retval = 0
  1754.             try:
  1755.                 self.wait(gracePeriod)
  1756.             except ProcessError, ex:
  1757.                 log.info("[%s] Process.kill: calling TerminateProcess", id(self))
  1758.                 win32process.TerminateProcess(self._hProcess, -1)
  1759.                 win32api.Sleep(100) # wait for resources to be released
  1760.         else:
  1761.             if sig is None:
  1762.                 sig = signal.SIGKILL
  1763.             try:
  1764.                 os.kill(self._pid, sig)
  1765.             except OSError, ex:
  1766.                 if ex.errno != 3:
  1767.                     # Ignore:   OSError: [Errno 3] No such process
  1768.                     raise        
  1769.         _unregisterProcess(self)
  1770.         
  1771.     def _close_(self, hwnd, dummy):
  1772.         """Callback used by .kill() on Windows.
  1773.         EnumWindows callback - sends WM_CLOSE to any window owned by this
  1774.         process.
  1775.         """
  1776.         threadId, processId = win32process.GetWindowThreadProcessId(hwnd)
  1777.         if processId == self._processId:
  1778.             import win32gui
  1779.             win32gui.PostMessage(hwnd, WM_CLOSE, 0, 0)
  1780. class IOBuffer:
  1781.     """Want to be able to both read and write to this buffer from
  1782.     difference threads and have the same read/write semantics as for a
  1783.     std handler.
  1784.     This class is subclass-able. _doRead(), _doWrite(), _doReadline(),
  1785.     _doClose(), _haveLine(), and _haveNumBytes() can be overridden for
  1786.     specific functionality. The synchronization issues (block on read
  1787.     until write provides the needed data, termination) are handled for
  1788.     free.
  1789.     Cannot support:
  1790.         .seek()     # Because we are managing *two* positions (one each
  1791.         .tell()     #   for reading and writing), these do not make
  1792.                     #   sense.
  1793.     """
  1794.     #TODO:
  1795.     #   - Is performance a problem? This will likely be slower that
  1796.     #     StringIO.StringIO().
  1797.     #
  1798.     def __init__(self, mutex=None, stateChange=None, name=None):
  1799.         """'name' can be set for debugging, it will be used in log messages."""
  1800.         if name is not None:
  1801.             self._name = name
  1802.         else:
  1803.             self._name = id(self)
  1804.         log.info("[%s] IOBuffer.__init__()" % self._name)
  1805.         self.__buf = ''
  1806.         # A state change is defined as the buffer being closed or a
  1807.         # write occuring.
  1808.         if mutex is not None:
  1809.             self._mutex = mutex
  1810.         else:
  1811.             self._mutex = threading.Lock()
  1812.         if stateChange is not None:
  1813.             self._stateChange = stateChange
  1814.         else:
  1815.             self._stateChange = threading.Condition()
  1816.         self._closed = 0
  1817.     def _doWrite(self, s):
  1818.         self.__buf += s  # Append to buffer.
  1819.     def write(self, s):
  1820.         log.info("[%s] IOBuffer.write(s=%r)", self._name, s)
  1821.         # Silently drop writes after the buffer has been close()'d.
  1822.         if self._closed:
  1823.             return
  1824.         # If empty write, close buffer (mimicking behaviour from
  1825.         # koprocess.cpp.)
  1826.         if not s:
  1827.             self.close()
  1828.             return
  1829.         self._mutex.acquire()
  1830.         self._doWrite(s)
  1831.         self._stateChange.acquire()
  1832.         self._stateChange.notifyAll()   # Notify of the write().
  1833.         self._stateChange.release()
  1834.         self._mutex.release()
  1835.     def writelines(self, list):
  1836.         self.write(''.join(list))
  1837.     def _doRead(self, n):
  1838.         """Pop 'n' bytes from the internal buffer and return them."""
  1839.         if n < 0:
  1840.             idx = len(self.__buf)
  1841.         else:
  1842.             idx = min(n, len(self.__buf))
  1843.         retval, self.__buf = self.__buf[:idx], self.__buf[idx:]
  1844.         return retval
  1845.     def read(self, n=-1):
  1846.         log.info("[%s] IOBuffer.read(n=%r)" % (self._name, n))
  1847.         log.info("[%s] IOBuffer.read(): wait for data" % self._name)
  1848.         if n < 0:
  1849.             # Wait until the buffer is closed, i.e. no more writes will
  1850.             # come.
  1851.             while 1:
  1852.                 if self._closed: break
  1853.                 #log.debug("[%s]     <<< IOBuffer.read: state change .wait()"
  1854.                 #          % self._name)
  1855.                 self._stateChange.acquire()
  1856.                 self._stateChange.wait()
  1857.                 self._stateChange.release()
  1858.                 #log.debug("[%s]     >>> IOBuffer.read: done change .wait()"
  1859.                 #          % self._name)
  1860.         else:
  1861.             # Wait until there are the requested number of bytes to read
  1862.             # (or until the buffer is closed, i.e. no more writes will
  1863.             # come).
  1864.             # XXX WARNING: I *think* there is a race condition around
  1865.             #     here whereby self.fparent.read() in _InFileProxy can
  1866.             #     hang. *Sometime* test_stdin::test_stdin_buffer() will
  1867.             #     hang. This was *before* I moved the
  1868.             #     _stateChange.acquire() and .release() calls out side
  1869.             #     of the 'while 1:' here. ...and now they are back
  1870.             #     inside.
  1871.             while 1:
  1872.                 if self._closed: break
  1873.                 if self._haveNumBytes(n): break
  1874.                 #log.debug("[%s]     <<< IOBuffer.read: state change .wait()"
  1875.                 #          % self._name)
  1876.                 self._stateChange.acquire()
  1877.                 self._stateChange.wait()
  1878.                 self._stateChange.release()
  1879.                 #log.debug("[%s]     >>> IOBuffer.read: done change .wait()"
  1880.                 #          % self._name)
  1881.         log.info("[%s] IOBuffer.read(): done waiting for data" % self._name)
  1882.         self._mutex.acquire()
  1883.         retval = self._doRead(n)
  1884.         self._mutex.release()
  1885.         return retval
  1886.     def _doReadline(self, n):
  1887.         """Pop the front line (or n bytes of it, whichever is less) from
  1888.         the internal buffer and return it.
  1889.         """
  1890.         idx = self.__buf.find('n')
  1891.         if idx == -1:
  1892.             idx = len(self.__buf)
  1893.         else:
  1894.             idx += 1 # include the 'n'
  1895.         if n is not None:
  1896.             idx = min(idx, n)
  1897.         retval, self.__buf = self.__buf[:idx], self.__buf[idx:]
  1898.         return retval
  1899.     def _haveLine(self):
  1900.         return self.__buf.find('n') != -1
  1901.     def _haveNumBytes(self, n=None):
  1902.         return len(self.__buf) >= n
  1903.     def readline(self, n=None):
  1904.         # Wait until there is a full line (or at least 'n' bytes)
  1905.         # in the buffer or until the buffer is closed, i.e. no more
  1906.         # writes will come.
  1907.         log.info("[%s] IOBuffer.readline(n=%r)" % (self._name, n))
  1908.         log.info("[%s] IOBuffer.readline(): wait for data" % self._name)
  1909.         while 1:
  1910.             if self._closed: break           
  1911.             if self._haveLine(): break
  1912.             if n is not None and self._haveNumBytes(n): break
  1913.             self._stateChange.acquire()
  1914.             self._stateChange.wait()
  1915.             self._stateChange.release()
  1916.         log.info("[%s] IOBuffer.readline(): done waiting for data"
  1917.                  % self._name)
  1918.         self._mutex.acquire()
  1919.         retval = self._doReadline(n)
  1920.         self._mutex.release()
  1921.         return retval
  1922.     def readlines(self):
  1923.         lines = []
  1924.         while 1:
  1925.             line = self.readline()
  1926.             if line:
  1927.                 lines.append(line)
  1928.             else:
  1929.                 break
  1930.         return lines
  1931.     def _doClose(self):
  1932.         pass
  1933.     def close(self):
  1934.         if not self._closed:
  1935.             log.info("[%s] IOBuffer.close()" % self._name)
  1936.             self._doClose()
  1937.             self._closed = 1
  1938.             self._stateChange.acquire()
  1939.             self._stateChange.notifyAll()   # Notify of the close().
  1940.             self._stateChange.release()
  1941.     def flush(self):
  1942.         log.info("[%s] IOBuffer.flush()" % self._name)
  1943.         #XXX Perhaps flush() should unwedged possible waiting .read()
  1944.         #    and .readline() calls that are waiting for more data???
  1945. class _InFileProxy(threading.Thread):
  1946.     """A thread to proxy stdin.write()'s from the parent to the child."""
  1947.     def __init__(self, fParent, fChild, name=None):
  1948.         """
  1949.         "fParent" is a Python file-like object setup for writing.
  1950.         "fChild" is a Win32 handle to the a child process' output pipe.
  1951.         "name" can be set for debugging, it will be used in log messages.
  1952.         """
  1953.         log.info("[%s, %s] _InFileProxy.__init__(fChild=%r, fParent=%r)",
  1954.                  name, id(self), fChild, fParent)
  1955.         threading.Thread.__init__(self, name=name)
  1956.         self.fChild = fChild
  1957.         self.fParent = fParent
  1958.     def run(self):
  1959.         log.info("[%s] _InFileProxy: start" % self.getName())
  1960.         try:
  1961.             self._proxyFromParentToChild()
  1962.         finally:
  1963.             log.info("[%s] _InFileProxy: closing parent (%r)"
  1964.                      % (self.getName(), self.fParent))
  1965.             try:
  1966.                 self.fParent.close()
  1967.             except IOError:
  1968.                 pass # Ignore: IOError: [Errno 4] Interrupted system call
  1969.         log.info("[%s] _InFileProxy: done" % self.getName())
  1970.     def _proxyFromParentToChild(self):
  1971.         CHUNKSIZE = 4096
  1972.         # Read output from the child process, and (for now) just write
  1973.         # it out.
  1974.         while 1:
  1975.             log.info("[%s] _InFileProxy: waiting for read on parent (%r)"
  1976.                      % (self.getName(), self.fParent))
  1977.             # XXX Get hangs here (!) even with
  1978.             #     self.stdin.close() in ProcessProxy' __del__() under this
  1979.             #     cond:
  1980.             #           p = ProcessProxy([...], stdin=sys.stdin)
  1981.             #     The user must manually send 'n' via <Enter> or EOF
  1982.             #     via <Ctrl-Z> to unlock this. How to get around that?
  1983.             #     See cleanOnTermination note in _OutFileProxy.run()
  1984.             #     below.
  1985.             #log.debug("XXX          -> start read on %r" % self.fParent)
  1986.             try:
  1987.                 text = self.fParent.read(CHUNKSIZE)
  1988.             except ValueError, ex:
  1989.                 # ValueError is raised with trying to write to a closed
  1990.                 # file/pipe.
  1991.                 text = None
  1992.             #log.debug("XXX          <- done read on %r" % self.fParent)
  1993.             if not text:
  1994.                 # Empty text signifies that the pipe has been closed on
  1995.                 # the parent's end.
  1996.                 log.info("[%s] _InFileProxy: observed close of parent (%r)"
  1997.                          % (self.getName(), self.fParent))
  1998.                 # Signal the child so it knows to stop listening.
  1999.                 try:
  2000.                     logres.info("[%s] _InFileProxy: closing child after "
  2001.                                 "observing parent's close: %r", self.getName(),
  2002.                                 self.fChild)
  2003.                     try:
  2004.                         self.fChild.close()
  2005.                     except IOError:
  2006.                         pass # Ignore: IOError: [Errno 4] Interrupted system call
  2007.                 except IOError, ex:
  2008.                     # Ignore: IOError: [Errno 9] Bad file descriptor
  2009.                     # XXX Do we *know* we want to do that?
  2010.                     pass
  2011.                 break
  2012.             else:
  2013.                 log.info("[%s] _InFileProxy: read %d bytes from parent: %r"
  2014.                          % (self.getName(), len(text), text))
  2015.             log.info("[%s, %s] _InFileProxy: writing %r to child (%r)",
  2016.                      self.getName(), id(self), text, self.fChild)
  2017.             try:
  2018.                 self.fChild.write(text)
  2019.             except (OSError, IOError), ex:
  2020.                 # Ignore errors for now. For example:
  2021.                 # - Get this on Win9x when writing multiple lines to "dir":
  2022.                 #   OSError: [Errno 32] Broken pipe
  2023.                 #XXX There *may* be errors we don't want to avoid.
  2024.                 #XXX Should maybe just ignore EnvironmentError (base class).
  2025.                 log.info("[%s] _InFileProxy: error writing to child (%r), "
  2026.                          "closing: %s" % (self.getName(), self.fParent, ex))
  2027.                 break
  2028.             log.info("[%s] _InFileProxy: wrote %d bytes to child: %r"
  2029.                      % (self.getName(), len(text), text))
  2030. class _OutFileProxy(threading.Thread):
  2031.     """A thread to watch an "out" file from the spawned child process
  2032.     and pass on write's to the parent.
  2033.     """
  2034.     def __init__(self, fChild, fParent, toClose=[], name=None):
  2035.         """
  2036.         "fChild" is a Win32 handle to the a child process' output pipe.
  2037.         "fParent" is a Python file-like object setup for writing.
  2038.         "toClose" is a list of objects on which to call .close when this
  2039.             proxy is terminating.
  2040.         "name" can be set for debugging, it will be used in log messages.
  2041.         """
  2042.         log.info("[%s] _OutFileProxy.__init__(fChild=%r, fParent=%r, "
  2043.                  "toClose=%r)", name, fChild, fParent, toClose)
  2044.         threading.Thread.__init__(self, name=name)
  2045.         self.fChild = fChild
  2046.         self.fParent = fParent
  2047.         self.toClose = toClose
  2048.     def run(self):
  2049.         log.info("[%s] _OutFileProxy: start" % self.getName())
  2050.         try:
  2051.             self._proxyFromChildToParent()
  2052.         finally:
  2053.             logres.info("[%s] _OutFileProxy: terminating, close child (%r)",
  2054.                         self.getName(), self.fChild)
  2055.             try:
  2056.                 self.fChild.close()
  2057.             except IOError:
  2058.                 pass # Ignore: IOError: [Errno 4] Interrupted system call
  2059.             log.info("[%s] _OutFileProxy: closing parent (%r)",
  2060.                      self.getName(), self.fParent)
  2061.             try:
  2062.                 self.fParent.close()
  2063.             except IOError:
  2064.                 pass # Ignore: IOError: [Errno 4] Interrupted system call
  2065.             while self.toClose:
  2066.                 logres.info("[%s] _OutFileProxy: closing %r after "
  2067.                             "closing parent", self.getName(), self.toClose[0])
  2068.                 try:
  2069.                     self.toClose[0].close()
  2070.                 except IOError:
  2071.                     pass # Ignore: IOError: [Errno 4] Interrupted system call
  2072.                 del self.toClose[0]
  2073.         log.info("[%s] _OutFileProxy: done" % self.getName())
  2074.     def _proxyFromChildToParent(self):
  2075.         CHUNKSIZE = 4096
  2076.         # Read output from the child process, and (for now) just write
  2077.         # it out.
  2078.         while 1:
  2079.             text = None
  2080.             try:
  2081.                 log.info("[%s] _OutFileProxy: waiting for read on child (%r)"
  2082.                          % (self.getName(), self.fChild))
  2083.                 text = self.fChild.read(CHUNKSIZE)
  2084.             except IOError, ex:
  2085.                 # Ignore: IOError: [Errno 9] Bad file descriptor
  2086.                 # XXX Do we *know* we want to do that?
  2087.                 log.info("[%s] _OutFileProxy: error reading from child (%r), "
  2088.                          "shutting down: %s", self.getName(), self.fChild, ex)
  2089.                 break
  2090.             if not text:
  2091.                 # Empty text signifies that the pipe has been closed on
  2092.                 # the child's end.
  2093.                 log.info("[%s] _OutFileProxy: observed close of child (%r)"
  2094.                          % (self.getName(), self.fChild))
  2095.                 break
  2096.             log.info("[%s] _OutFileProxy: text(len=%d): %r",
  2097.                      self.getName(), len(text), text)
  2098.             self.fParent.write(text)
  2099. # Disabled By alch 24-3-04
  2100. # seems to fail intermottenly on Linux
  2101. # in our case this is not needed
  2102. # because we don't wait from a subthread
  2103. if False and sys.platform.startswith("linux"):
  2104.     class _ThreadFixer:
  2105.         """Mixin class for various classes in the Process hierarchy to
  2106.         work around the known LinuxThreads bug where one cannot .wait()
  2107.         on a created process from a subthread of the thread that created
  2108.         the process.
  2109.         Usage:
  2110.             class ProcessXXX(_ThreadFixer, BrokenProcessXXX):
  2111.                 _pclass = BrokenProcessXXX
  2112.         Details:
  2113.             Because we must do all real os.wait() calls on the child
  2114.             process from the thread that spawned it, we use a proxy
  2115.             thread whose only responsibility is just that. The proxy
  2116.             thread just starts the child and then immediately wait's for
  2117.             the child to terminate. On termination is stores the exit
  2118.             status (for use by the main thread) and notifies any thread
  2119.             waiting for this termination (possibly the main thread). The
  2120.             overriden .wait() uses this stored exit status and the
  2121.             termination notification to simulate the .wait().
  2122.         """
  2123.         def __init__(self, *args, **kwargs):
  2124.             # Keep a reference to 'log' ensure it is around for this object's
  2125.             # destruction.
  2126.             self.__log = log
  2127.             self.__waiter = None
  2128.             self.__hasTerminated = threading.Condition()
  2129.             self.__terminationResult = None
  2130.             self.__childStarted = threading.Condition()
  2131.             self._pclass.__init__(self, *args, **kwargs)
  2132.         def _forkAndExecChildOnUnix(self, *args, **kwargs):
  2133.             """Fork and start the child process do it in a special subthread
  2134.             that will negotiate subsequent .wait()'s.
  2135.             Sets self._pid as a side effect.
  2136.             """
  2137.             self.__waiter = threading.Thread(
  2138.                 target=self.__launchAndWait, args=args, kwargs=kwargs)
  2139.             # Start subthread that will launch child and wait until it
  2140.             # *has* started.
  2141.             self.__childStarted.acquire()
  2142.             self.__waiter.start()
  2143.             self.__childStarted.wait()
  2144.             self.__childStarted.release()
  2145.         def __launchAndWait(self, *args, **kwargs):
  2146.             """Launch the given command and wait for it to terminate.
  2147.             When the process has terminated then store its exit value
  2148.             and finish.
  2149.             """
  2150.             logfix.info("start child in thread %s",
  2151.                         threading.currentThread().getName())
  2152.             # Spawn the child process and notify the main thread of
  2153.             # this.
  2154.             self.__childStarted.acquire()
  2155.             self._pclass._forkAndExecChildOnUnix(self, *args, **kwargs)
  2156.             self.__childStarted.notifyAll()
  2157.             self.__childStarted.release()
  2158.             # Wait on the thread and store appropriate results when
  2159.             # finished.
  2160.             try:
  2161.                 waitResult = self._pclass.wait(self)
  2162.             except ProcessError, ex:
  2163.                 waitResult = ex
  2164.             self.__hasTerminated.acquire()
  2165.             self.__terminationResult = waitResult
  2166.             self.__hasTerminated.notifyAll()
  2167.             self.__hasTerminated.release()
  2168.             self.__waiter = None # drop ref that would keep instance alive
  2169.         def wait(self, timeout=None):
  2170.             # If the process __hasTerminated then return the exit
  2171.             # status. Otherwise simulate the wait as appropriate.
  2172.             # Note:
  2173.             #   - This class is only used on linux so 'timeout' has the
  2174.             #     Unix 'timeout' semantics.
  2175.             self.__hasTerminated.acquire()
  2176.             if self.__terminationResult is None:
  2177.                 if timeout == os.WNOHANG:   # Poll.
  2178.                     self.__hasTerminated.wait(0)
  2179.                 else:                       # Block until process finishes.
  2180.                     self.__hasTerminated.wait()
  2181.             terminationResult = self.__terminationResult
  2182.             self.__hasTerminated.release()
  2183.             if terminationResult is None:
  2184.                 # process has not finished yet
  2185.                 raise ProcessError("Wait for process timed out.",
  2186.                                    self.WAIT_TIMEOUT)
  2187.             elif isinstance(terminationResult, Exception):
  2188.                 # some error waiting for process termination
  2189.                 raise terminationResult
  2190.             else:
  2191.                 # the process terminated
  2192.                 return terminationResult
  2193.     _ThreadBrokenProcess = Process
  2194.     class Process(_ThreadFixer, _ThreadBrokenProcess):
  2195.         _pclass = _ThreadBrokenProcess
  2196.     _ThreadBrokenProcessOpen = ProcessOpen
  2197.     class ProcessOpen(_ThreadFixer, _ThreadBrokenProcessOpen):
  2198.         _pclass = _ThreadBrokenProcessOpen
  2199.     _ThreadBrokenProcessProxy = ProcessProxy
  2200.     class ProcessProxy(_ThreadFixer, _ThreadBrokenProcessProxy):
  2201.         _pclass = _ThreadBrokenProcessProxy
  2202.         
  2203. if __name__=='__main__':
  2204.     proc = ProcessOpen('"c:\winnt\explorer1.exe" "c:\"')
  2205.     ret = proc.wait()
  2206.     print ret