~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to wxPython/samples/ide/activegrid/tool/process.py

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

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