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:
13
The above copyright notice and this permission notice shall be included
14
in all copies or substantial portions of the Software.
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.
26
Python interface for process control.
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
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.
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
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
59
Understanding ProcessProxy is pretty complex. Some examples
60
below attempt to help show some uses. Here is a diagram of the
64
,---->->->------' ^ `------>->->----,
66
IOBuffer IOBuffer IOBuffer
67
(p.stdout) (p.stderr) (p.stdin)
69
_OutFileProxy _OutFileProxy _InFileProxy
72
`----<-<-<------, | ,------<-<-<----'
77
p = process.<Process class>(cmd='echo hi', ...)
78
#... use the various methods and attributes
81
A simple 'hello world':
83
>>> p = process.ProcessOpen(['echo', 'hello'])
86
>>> p.wait() # .wait() returns the child's exit status
89
Redirecting the stdout handler:
91
>>> p = process.ProcessProxy(['echo', 'hello'], stdout=sys.stdout)
94
Using stdin (need to use ProcessProxy here because it defaults to
95
text-mode translation on Windows, ProcessOpen does not support
97
>>> p = process.ProcessProxy(['sort'])
98
>>> p.stdin.write('5\n')
99
>>> p.stdin.write('2\n')
100
>>> p.stdin.write('7\n')
105
Specifying environment variables:
106
>>> p = process.ProcessOpen(['perl', '-e', 'print $ENV{FOO}'])
109
>>> p = process.ProcessOpen(['perl', '-e', 'print $ENV{FOO}'],
110
... env={'FOO':'bar'})
114
Killing a long running process (On Linux, to poll you must use
116
>>> p = ProcessOpen(['perl', '-e', 'while (1) {}'])
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"
123
process is still running
128
Providing objects for stdin/stdout/stderr:
129
XXX write this, mention IOBuffer subclassing.
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?
153
if sys.platform.startswith("win"):
161
# constants pulled from win32con to save memory
162
VER_PLATFORM_WIN32_WINDOWS = 1
166
DUPLICATE_SAME_ACCESS = 2
174
class ProcessError(Exception):
175
def __init__(self, msg, errno=-1):
176
Exception.__init__(self, msg)
180
#---- internal logging facility
183
DEBUG, INFO, WARN, ERROR, FATAL = range(5)
184
def __init__(self, name, level=None, streamOrFileName=sys.stderr):
187
self.level = self.WARN
190
if type(streamOrFileName) == types.StringType:
191
self.stream = open(streamOrFileName, 'w')
192
self._opennedStream = 1
194
self.stream = streamOrFileName
195
self._opennedStream = 0
197
if self._opennedStream:
199
def _getLevelName(self, level):
207
return levelNameMap[level]
208
def log(self, level, msg, *args):
209
if level < self.level:
211
message = "%s: %s:" % (self.name, self._getLevelName(level).lower())
212
message = message + (msg % args) + "\n"
213
self.stream.write(message)
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)
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)
247
_version_ = (0, 5, 0)
249
# List of registered processes (see _(un)registerProcess).
254
#---- internal support routines
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'\"')
263
r"""Join an arglist to a string appropriate for running.
266
>>> _joinArgv(['foo', 'bar "baz'])
271
if ' ' in arg or ';' in arg:
272
cmdstr += '"%s"' % _escapeArg(arg)
274
cmdstr += _escapeArg(arg)
276
if cmdstr.endswith(' '): cmdstr = cmdstr[:-1] # strip trailing space
280
def _getPathFromEnv(env):
281
"""Return the PATH environment variable or None.
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.
287
if sys.platform.startswith("win"):
288
for key in env.keys():
289
if key.upper() == "PATH":
294
if env.has_key("PATH"):
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.
304
Raise a ProcessError if no such executable could be found.
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)
313
raise ProcessError("Malformed command: %r" % cmd)
314
first, rest = cmd[1:idx], cmd[idx+1:]
318
first, rest = cmd.split(' ', 1)
320
first, rest = cmd, ""
322
# Ensure the first arg is a valid path to the appropriate file.
325
altpath = [os.path.dirname(first)]
326
firstbase = os.path.basename(first)
327
candidates = list(which.which(firstbase, path=altpath))
329
altpath = _getPathFromEnv(env)
331
candidates = list(which.which(first, altpath.split(os.pathsep)))
333
candidates = list(which.which(first))
335
candidates = list(which.which(first))
337
return _joinArgv( [candidates[0]] ) + ' ' + rest
339
raise ProcessError("Could not find an appropriate leading command "\
343
if sys.platform.startswith("win"):
344
def _SaferCreateProcess(appName, # app name
346
processSA, # process security attributes
347
threadSA, # thread security attributes
348
inheritHandles, # are handles are inherited
349
creationFlags, # creation flags
351
cwd, # current working directory
352
si): # STARTUPINFO pointer
353
"""If CreateProcess fails from environment type inconsistency then
354
fix that and try again.
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.
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.
369
#XXX Komodo 2.0 Beta 1 hack. This requirement should be
370
# pushed out to Komodo code using process.py. Or should it?
373
for key, value in env.items():
374
aenv[str(key)] = str(value)
378
_SaferCreateProcess(appName=%r,
383
""", appName, cmd, env, cwd, os.getcwd())
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.
398
#warnings.warn('env: ' + str(ex), stacklevel=4)
402
for key, value in env.items():
403
aenv[str(key)] = str(value)
404
except UnicodeError, ex:
405
raise ProcessError(str(ex))
409
for key, val in env.items():
411
uenv[unicode(key)] = unicode(val) # default encoding
414
uenv[unicode(key, 'iso-8859-1')] = unicode(val, 'iso-8859-1') # backup encoding
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))
418
hProcess, hThread, processId, threadId\
419
= win32process.CreateProcess(appName, cmd, processSA,
420
threadSA, inheritHandles,
421
creationFlags, env, cwd,
425
return hProcess, hThread, processId, threadId
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):
440
log.info("_registerprocess(process=%r)", process)
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
449
# poll to see if is process still running
450
if sys.platform.startswith("win"):
455
_unregisterProcess(p)
456
except ProcessError, ex:
457
if ex.errno == ProcessProxy.WAIT_TIMEOUT:
462
_processes.append(process)
464
def _unregisterProcess(process):
466
log.info("_unregisterProcess(process=%r)", process)
468
_processes.remove(process)
474
def _fixupCommand(cmd, env=None):
475
"""Fixup the command string so it is launchable via CreateProcess.
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
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.
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"
500
>>> p = ProcessOpen('hang.exe')
501
# This results in the same command to CreateProcess as
505
Hence, for these platforms we fallback to option (2). Cons:
506
- cannot spawn shell commands like 'dir' directly
507
- cannot spawn batch files
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()
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
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.
532
cmd = _whichFirstArg(cmd, env)
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)
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)),
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),
549
if not os.path.exists(w9xpopen):
551
"Can not locate 'w9xpopen.exe' which is needed "\
552
"for ProcessOpen to work with your shell or "\
554
## This would be option (1):
555
#cmd = '%s "%s /c %s"'\
556
# % (w9xpopen, comspec, cmd.replace('"', '\\"'))
558
cmd = _whichFirstArg(cmd, env)
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('"', '\\"'))
568
"""Wrap a system file object, hiding some nitpicky details.
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
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)
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.)
589
def __init__(self, file=None, descriptor=None, handle=None):
591
self._descriptor = descriptor
592
self._handle = handle
594
if self._descriptor is not None or self._handle is not None:
595
self._lineBuf = "" # to support .readline()
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)
605
raise ProcessError("no file object to pass '%s' attribute to"
608
def _win32Read(self, nBytes):
610
log.info("[%s] _FileWrapper.read: waiting for read on pipe",
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",
619
assert errCode == 0,\
620
"Why is 'errCode' from ReadFile non-zero? %r" % errCode
622
# Empty text signifies that the pipe has been closed on
624
log.info("[%s] _FileWrapper.read: observed close of parent",
626
# Signal the child so it knows to stop listening.
630
log.info("[%s] _FileWrapper.read: read %d bytes from pipe: %r",
631
id(self), len(text), text)
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:
642
text, self._lineBuf = self._lineBuf, ""
644
t = os.read(self._descriptor, 4092)
650
if len(self._lineBuf) >= nBytes:
651
text, self._lineBuf =\
652
self._lineBuf[:nBytes], self._lineBuf[nBytes:]
654
nBytesToGo = nBytes - len(self._lineBuf)
655
text = self._lineBuf + os.read(self._descriptor,
659
elif self._handle is not None:
661
text, self._lineBuf = self._lineBuf, ""
663
t = self._win32Read(4092)
669
if len(self._lineBuf) >= nBytes:
670
text, self._lineBuf =\
671
self._lineBuf[:nBytes], self._lineBuf[nBytes:]
673
nBytesToGo = nBytes - len(self._lineBuf)
674
text, self._lineBuf =\
675
self._lineBuf + self._win32Read(nBytesToGo), ""
677
elif self._file is not None:
678
return self._file.read(nBytes)
680
raise "FileHandle.read: no handle to read with"
683
if self._descriptor is not None or self._handle is not None:
685
#XXX This is not portable to the Mac.
686
idx = self._lineBuf.find('\n')
688
line, self._lineBuf =\
689
self._lineBuf[:idx+1], self._lineBuf[idx+1:]
692
lengthBefore = len(self._lineBuf)
694
if len(t) <= lengthBefore: # no new data was read
695
line, self._lineBuf = self._lineBuf, ""
700
elif self._file is not None:
701
return self._file.readline()
703
raise "FileHandle.readline: no handle to read with"
706
if self._descriptor is not None or self._handle is not None:
709
line = self.readline()
715
elif self._file is not None:
716
return self._file.readlines()
718
raise "FileHandle.readline: no handle to read with"
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:
725
errCode, nBytesWritten = win32file.WriteFile(self._handle, text)
726
except pywintypes.error, ex:
727
# Ingore errors like "The pipe is being closed.", for
729
log.info("[%s] _FileWrapper.write: error writing to pipe, "\
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",
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)
746
raise "FileHandle.write: nothing to write with"
749
"""Close all associated file objects and handles."""
750
log.debug("[%s] _FileWrapper.close()", id(self))
753
if self._file is not None:
754
log.debug("[%s] _FileWrapper.close: close file", id(self))
756
log.debug("[%s] _FileWrapper.close: done file close", id(self))
757
if self._descriptor is not None:
759
os.close(self._descriptor)
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))
769
if self._handle is not None:
770
log.debug("[%s] _FileWrapper.close: close handle", id(self))
772
win32api.CloseHandle(self._handle)
773
except win32api.error:
774
log.debug("[%s] _FileWrapper.close: closing handle raised",
777
log.debug("[%s] _FileWrapper.close: done closing handle",
781
return "<_FileWrapper: file:%r fd:%r os_handle:%r>"\
782
% (self._file, self._descriptor, self._handle)
785
class _CountingCloser:
786
"""Call .close() on the given object after own .close() is called
787
the precribed number of times.
789
def __init__(self, objectsToClose, count):
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.
795
self.objectsToClose = objectsToClose
798
raise ProcessError("illegal 'count' value: %s" % self.count)
802
log.debug("[%d] _CountingCloser.close(): count=%d", id(self),
805
for objectToClose in self.objectsToClose:
806
objectToClose.close()
810
#---- public interface
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.
821
# - Rename this or merge it with ProcessOpen somehow.
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
834
# .wait() argument constants
836
# .wait() return error codes
839
# creation "flags" constants
840
CREATE_NEW_CONSOLE = 0x10 # same as win32process.CREATE_NEW_CONSOLE
842
def __init__(self, cmd, cwd=None, env=None, flags=0):
843
"""Create a child process.
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.
853
log.info("Process.__init__(cmd=%r, cwd=%r, env=%r, flags=%r)",
854
cmd, cwd, env, flags)
857
raise ProcessError("You must specify a command.")
861
if sys.platform.startswith("win"):
862
self._flags |= win32process.CREATE_NEW_PROCESS_GROUP
864
if sys.platform.startswith("win"):
865
self._startOnWindows()
867
self.__retvalCache = None
870
def _runChildOnUnix(self):
871
#XXX Errors running the child do *not* get communicated back.
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]
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):
892
os.execvpe(cmd[0], cmd, self._env)
894
os.execvp(cmd[0], cmd)
896
os._exit(1) # Should never get here.
898
def _forkAndExecChildOnUnix(self):
899
"""Fork and start the child process.
901
Sets self._pid as a side effect.
905
self._runChildOnUnix()
909
def _startOnUnix(self):
915
raise ProcessError(msg=str(ex), errno=ex.errno)
916
self._forkAndExecChildOnUnix()
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)
929
si = win32process.STARTUPINFO()
930
si.dwFlags = win32process.STARTF_USESHOWWINDOW
931
si.wShowWindow = SW_SHOWDEFAULT
933
if not (self._flags & self.CREATE_NEW_CONSOLE):
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.
939
cmd = _whichFirstArg(cmd, self._env)
941
# Could not find the command, perhaps it is an internal
942
# shell command -- fallback to _fixupCommand
943
cmd = _fixupCommand(cmd, self._env)
945
cmd = _fixupCommand(cmd, self._env)
946
log.debug("cmd = %r", cmd)
948
# Start the child process.
950
self._hProcess, self._hThread, self._processId, self._threadId\
951
= _SaferCreateProcess(
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"\
967
def wait(self, timeout=None):
968
"""Wait for the started process to complete.
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.
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
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"):
991
timeout = win32event.INFINITE
993
timeout = timeout * 1000.0 # Win32 API's timeout is in millisecs
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)
1003
retval = win32process.GetExitCodeProcess(self._hProcess)
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
1019
pid, sts = os.waitpid(self._pid, timeout)
1020
if pid == self._pid:
1021
self.__retvalCache = retval = sts
1023
raise ProcessError("Wait for process timed out.",
1027
def kill(self, exitCode=0, gracePeriod=1.0, sig=None):
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.
1039
Try for an orderly shutdown via WM_CLOSE. If still running
1040
after gracePeriod (1 sec. default), terminate.
1042
if sys.platform.startswith("win"):
1044
# Send WM_CLOSE to windows in this process group.
1045
win32gui.EnumWindows(self._close_, 0)
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.
1051
win32api.GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
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.
1065
# Last resort: call TerminateProcess if it has not yet.
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
1076
sig = signal.SIGKILL
1078
os.kill(self._pid, sig)
1081
# Ignore: OSError: [Errno 3] No such process
1084
def _close_(self, hwnd, dummy):
1085
"""Callback used by .kill() on Windows.
1087
EnumWindows callback - sends WM_CLOSE to any window owned by this
1090
threadId, processId = win32process.GetWindowThreadProcessId(hwnd)
1091
if processId == self._processId:
1093
win32gui.PostMessage(hwnd, WM_CLOSE, 0, 0)
1096
class ProcessOpen(Process):
1097
"""Create a process and setup pipes to it standard handles.
1099
This is a super popen3.
1102
# - Share some implementation with Process and ProcessProxy.
1105
def __init__(self, cmd, mode='t', cwd=None, env=None):
1106
"""Create a Process with proxy threads for each std handle.
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
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
1119
# Keep a reference to ensure it is around for this object's destruction.
1121
log.info("ProcessOpen.__init__(cmd=%r, mode=%r, cwd=%r, env=%r)",
1122
cmd, mode, cwd, env)
1125
raise ProcessError("You must specify a command.")
1129
if self._mode not in ('t', 'b'):
1130
raise ProcessError("'mode' must be 't' or 'b'.")
1133
if sys.platform.startswith("win"):
1134
self._startOnWindows()
1136
self.__retvalCache = None
1139
_registerProcess(self)
1142
#XXX Should probably not rely upon this.
1143
logres.info("[%s] ProcessOpen.__del__()", id(self))
1145
del self.__log # drop reference
1148
if not self._closed:
1149
self.__log.info("[%s] ProcessOpen.close()" % id(self))
1151
# Ensure that all IOBuffer's are closed. If they are not, these
1154
self.__log.info("[%s] ProcessOpen: closing stdin (%r)."\
1155
% (id(self), self.stdin))
1157
except AttributeError:
1158
# May not have gotten far enough in the __init__ to set
1162
self.__log.info("[%s] ProcessOpen: closing stdout (%r)."\
1163
% (id(self), self.stdout))
1165
except AttributeError:
1166
# May not have gotten far enough in the __init__ to set
1170
self.__log.info("[%s] ProcessOpen: closing stderr (%r)."\
1171
% (id(self), self.stderr))
1173
except AttributeError:
1174
# May not have gotten far enough in the __init__ to set
1180
def _forkAndExecChildOnUnix(self, fdChildStdinRd, fdChildStdoutWr,
1182
"""Fork and start the child process.
1184
Sets self._pid as a side effect.
1187
if pid == 0: # child
1188
os.dup2(fdChildStdinRd, 0)
1189
os.dup2(fdChildStdoutWr, 1)
1190
os.dup2(fdChildStderrWr, 2)
1191
self._runChildOnUnix()
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()
1202
oldDir = os.getcwd()
1206
raise ProcessError(msg=str(ex), errno=ex.errno)
1207
self._forkAndExecChildOnUnix(fdChildStdinRd, fdChildStdoutWr,
1212
os.close(fdChildStdinRd)
1213
os.close(fdChildStdoutWr)
1214
os.close(fdChildStderrWr)
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)
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)
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)
1244
# Duplicate the parent ends of the pipes so they are not
1246
hChildStdinWrDup = win32api.DuplicateHandle(
1247
win32api.GetCurrentProcess(),
1249
win32api.GetCurrentProcess(),
1252
DUPLICATE_SAME_ACCESS)
1253
win32api.CloseHandle(hChildStdinWr)
1254
self._hChildStdinWr = hChildStdinWrDup
1255
hChildStdoutRdDup = win32api.DuplicateHandle(
1256
win32api.GetCurrentProcess(),
1258
win32api.GetCurrentProcess(),
1261
DUPLICATE_SAME_ACCESS)
1262
win32api.CloseHandle(hChildStdoutRd)
1263
self._hChildStdoutRd = hChildStdoutRdDup
1264
hChildStderrRdDup = win32api.DuplicateHandle(
1265
win32api.GetCurrentProcess(),
1267
win32api.GetCurrentProcess(),
1270
DUPLICATE_SAME_ACCESS)
1271
win32api.CloseHandle(hChildStderrRd)
1272
self._hChildStderrRd = hChildStderrRdDup
1274
# Set the translation mode and buffering.
1275
if self._mode == 't':
1279
fdChildStdinWr = msvcrt.open_osfhandle(self._hChildStdinWr, flags)
1280
fdChildStdoutRd = msvcrt.open_osfhandle(self._hChildStdoutRd, flags)
1281
fdChildStderrRd = msvcrt.open_osfhandle(self._hChildStderrRd, flags)
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)
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
1305
cmd = _fixupCommand(cmd, self._env)
1307
creationFlags = win32process.CREATE_NEW_PROCESS_GROUP
1309
self._hProcess, hThread, self._processId, threadId\
1310
= _SaferCreateProcess(
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)
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)
1331
def wait(self, timeout=None):
1332
"""Wait for the started process to complete.
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.
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
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
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"):
1359
timeout = win32event.INFINITE
1361
timeout = timeout * 1000.0 # Win32 API's timeout is in millisecs
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)
1372
retval = win32process.GetExitCodeProcess(self._hProcess)
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
1388
pid, sts = os.waitpid(self._pid, timeout)
1389
if pid == self._pid:
1390
self.__retvalCache = retval = sts
1392
raise ProcessError("Wait for process timed out.",
1394
_unregisterProcess(self)
1397
def kill(self, exitCode=0, gracePeriod=1.0, sig=None):
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.
1409
Try for an orderly shutdown via WM_CLOSE. If still running
1410
after gracePeriod (1 sec. default), terminate.
1412
if sys.platform.startswith("win"):
1414
# Send WM_CLOSE to windows in this process group.
1415
win32gui.EnumWindows(self._close_, 0)
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.
1421
win32api.GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
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.
1435
# Last resort: call TerminateProcess if it has not yet.
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
1446
sig = signal.SIGKILL
1448
os.kill(self._pid, sig)
1451
# Ignore: OSError: [Errno 3] No such process
1454
_unregisterProcess(self)
1456
def _close_(self, hwnd, dummy):
1457
"""Callback used by .kill() on Windows.
1459
EnumWindows callback - sends WM_CLOSE to any window owned by this
1462
threadId, processId = win32process.GetWindowThreadProcessId(hwnd)
1463
if processId == self._processId:
1465
win32gui.PostMessage(hwnd, WM_CLOSE, 0, 0)
1468
class ProcessProxy(Process):
1469
"""Create a process and proxy communication via the standard handles.
1471
#XXX To add to docstring:
1472
# - stdout/stderr proxy handling
1473
# - stdin proxy handling
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
1483
# - .suspend() and .resume()? See Win32::Process Perl module.
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.
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
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
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.
1505
# Keep a reference to ensure it is around for this object's destruction.
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)
1512
raise ProcessError("You must specify a command.")
1514
if self._mode not in ('t', 'b'):
1515
raise ProcessError("'mode' must be 't' or 'b'.")
1519
self.stdin = IOBuffer(name='<stdin>')
1523
self.stdout = IOBuffer(name='<stdout>')
1525
self.stdout = stdout
1527
self.stderr = IOBuffer(name='<stderr>')
1529
self.stderr = stderr
1532
if sys.platform.startswith("win"):
1533
self._startOnWindows()
1535
self.__retvalCache = None
1538
_registerProcess(self)
1541
#XXX Should probably not rely upon this.
1542
logres.info("[%s] ProcessProxy.__del__()", id(self))
1544
del self.__log # drop reference
1547
if not self._closed:
1548
self.__log.info("[%s] ProcessProxy.close()" % id(self))
1550
# Ensure that all IOBuffer's are closed. If they are not, these
1552
self.__log.info("[%s] ProcessProxy: closing stdin (%r)."\
1553
% (id(self), self.stdin))
1556
self._stdinProxy.join()
1557
except AttributeError:
1558
# May not have gotten far enough in the __init__ to set
1561
self.__log.info("[%s] ProcessProxy: closing stdout (%r)."\
1562
% (id(self), self.stdout))
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
1571
self.__log.info("[%s] ProcessProxy: closing stderr (%r)."\
1572
% (id(self), self.stderr))
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
1584
def _forkAndExecChildOnUnix(self, fdChildStdinRd, fdChildStdoutWr,
1586
"""Fork and start the child process.
1588
Sets self._pid as a side effect.
1591
if pid == 0: # child
1592
os.dup2(fdChildStdinRd, 0)
1593
os.dup2(fdChildStdoutWr, 1)
1594
os.dup2(fdChildStderrWr, 2)
1595
self._runChildOnUnix()
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()
1606
oldDir = os.getcwd()
1610
raise ProcessError(msg=str(ex), errno=ex.errno)
1611
self._forkAndExecChildOnUnix(fdChildStdinRd, fdChildStdoutWr,
1616
os.close(fdChildStdinRd)
1617
os.close(fdChildStdoutWr)
1618
os.close(fdChildStderrWr)
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)
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
1638
closer = _CountingCloser([self.stdin, childStdin, self], 2)
1639
self._stdoutProxy = _OutFileProxy(childStdout, self.stdout,
1642
self._stdoutProxy.start()
1643
self._stderrProxy = _OutFileProxy(childStderr, self.stderr,
1646
self._stderrProxy.start()
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)
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)
1666
# Duplicate the parent ends of the pipes so they are not
1668
hChildStdinWrDup = win32api.DuplicateHandle(
1669
win32api.GetCurrentProcess(),
1671
win32api.GetCurrentProcess(),
1674
DUPLICATE_SAME_ACCESS)
1675
win32api.CloseHandle(hChildStdinWr)
1676
self._hChildStdinWr = hChildStdinWrDup
1677
hChildStdoutRdDup = win32api.DuplicateHandle(
1678
win32api.GetCurrentProcess(),
1680
win32api.GetCurrentProcess(),
1683
DUPLICATE_SAME_ACCESS)
1684
win32api.CloseHandle(hChildStdoutRd)
1685
self._hChildStdoutRd = hChildStdoutRdDup
1686
hChildStderrRdDup = win32api.DuplicateHandle(
1687
win32api.GetCurrentProcess(),
1689
win32api.GetCurrentProcess(),
1692
DUPLICATE_SAME_ACCESS)
1693
win32api.CloseHandle(hChildStderrRd)
1694
self._hChildStderrRd = hChildStderrRdDup
1696
# Set the translation mode.
1697
if self._mode == 't':
1703
fdChildStdinWr = msvcrt.open_osfhandle(self._hChildStdinWr, flags)
1704
fdChildStdoutRd = msvcrt.open_osfhandle(self._hChildStdoutRd, flags)
1705
fdChildStderrRd = msvcrt.open_osfhandle(self._hChildStderrRd, flags)
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)
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
1729
cmd = _fixupCommand(cmd, self._env)
1730
log.debug("cmd = %r", cmd)
1732
creationFlags = win32process.CREATE_NEW_PROCESS_GROUP
1734
self._hProcess, hThread, self._processId, threadId\
1735
= _SaferCreateProcess(
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)
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)
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],
1766
self._stdoutProxy.start()
1767
self._stderrProxy = _OutFileProxy(childStderr, self.stderr,
1769
self._stderrProxy.start()
1771
def wait(self, timeout=None):
1772
"""Wait for the started process to complete.
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.
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
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"):
1795
timeout = win32event.INFINITE
1797
timeout = timeout * 1000.0 # Win32 API's timeout is in millisecs
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)
1807
retval = win32process.GetExitCodeProcess(self._hProcess)
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
1823
pid, sts = os.waitpid(self._pid, timeout)
1824
if pid == self._pid:
1825
self.__retvalCache = retval = sts
1827
raise ProcessError("Wait for process timed out.",
1829
_unregisterProcess(self)
1832
def kill(self, exitCode=0, gracePeriod=1.0, sig=None):
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.
1844
Try for an orderly shutdown via WM_CLOSE. If still running
1845
after gracePeriod (1 sec. default), terminate.
1847
if sys.platform.startswith("win"):
1849
# Send WM_CLOSE to windows in this process group.
1850
win32gui.EnumWindows(self._close_, 0)
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.
1856
win32api.GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
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.
1870
# Last resort: call TerminateProcess if it has not yet.
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
1881
sig = signal.SIGKILL
1883
os.kill(self._pid, sig)
1886
# Ignore: OSError: [Errno 3] No such process
1889
_unregisterProcess(self)
1891
def _close_(self, hwnd, dummy):
1892
"""Callback used by .kill() on Windows.
1894
EnumWindows callback - sends WM_CLOSE to any window owned by this
1897
threadId, processId = win32process.GetWindowThreadProcessId(hwnd)
1898
if processId == self._processId:
1900
win32gui.PostMessage(hwnd, WM_CLOSE, 0, 0)
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
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
1915
.seek() # Because we are managing *two* positions (one each
1916
.tell() # for reading and writing), these do not make
1920
# - Is performance a problem? This will likely be slower that
1921
# StringIO.StringIO().
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:
1928
self._name = id(self)
1929
log.info("[%s] IOBuffer.__init__()" % self._name)
1932
# A state change is defined as the buffer being closed or a
1934
if mutex is not None:
1937
self._mutex = threading.Lock()
1938
if stateChange is not None:
1939
self._stateChange = stateChange
1941
self._stateChange = threading.Condition()
1944
def _doWrite(self, s):
1945
self.__buf += s # Append to buffer.
1948
log.info("[%s] IOBuffer.write(s=%r)", self._name, s)
1949
# Silently drop writes after the buffer has been close()'d.
1952
# If empty write, close buffer (mimicking behaviour from
1958
self._mutex.acquire()
1960
self._stateChange.acquire()
1961
self._stateChange.notifyAll() # Notify of the write().
1962
self._stateChange.release()
1963
self._mutex.release()
1965
def writelines(self, list):
1966
self.write(''.join(list))
1968
def _doRead(self, n):
1969
"""Pop 'n' bytes from the internal buffer and return them."""
1971
idx = len(self.__buf)
1973
idx = min(n, len(self.__buf))
1974
retval, self.__buf = self.__buf[:idx], self.__buf[idx:]
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)
1981
# Wait until the buffer is closed, i.e. no more writes will
1984
if self._closed: break
1985
#log.debug("[%s] <<< IOBuffer.read: state change .wait()"\
1987
self._stateChange.acquire()
1988
self._stateChange.wait()
1989
self._stateChange.release()
1990
#log.debug("[%s] >>> IOBuffer.read: done change .wait()"\
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
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
2004
if self._closed: break
2005
if self._haveNumBytes(n): break
2006
#log.debug("[%s] <<< IOBuffer.read: state change .wait()"\
2008
self._stateChange.acquire()
2009
self._stateChange.wait()
2010
self._stateChange.release()
2011
#log.debug("[%s] >>> IOBuffer.read: done change .wait()"\
2013
log.info("[%s] IOBuffer.read(): done waiting for data" % self._name)
2015
self._mutex.acquire()
2016
retval = self._doRead(n)
2017
self._mutex.release()
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.
2024
idx = self.__buf.find('\n')
2026
idx = len(self.__buf)
2028
idx += 1 # include the '\n'
2031
retval, self.__buf = self.__buf[:idx], self.__buf[idx:]
2034
def _haveLine(self):
2035
return self.__buf.find('\n') != -1
2037
def _haveNumBytes(self, n=None):
2038
return len(self.__buf) >= n
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
2044
log.info("[%s] IOBuffer.readline(n=%r)" % (self._name, n))
2046
log.info("[%s] IOBuffer.readline(): wait for data" % self._name)
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"\
2057
self._mutex.acquire()
2058
retval = self._doReadline(n)
2059
self._mutex.release()
2062
def readlines(self):
2065
line = self.readline()
2076
if not self._closed:
2077
log.info("[%s] IOBuffer.close()" % self._name)
2080
self._stateChange.acquire()
2081
self._stateChange.notifyAll() # Notify of the close().
2082
self._stateChange.release()
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???
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):
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.
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
2105
log.info("[%s] _InFileProxy: start" % self.getName())
2107
self._proxyFromParentToChild()
2109
log.info("[%s] _InFileProxy: closing parent (%r)"\
2110
% (self.getName(), self.fParent))
2112
self.fParent.close()
2114
pass # Ignore: IOError: [Errno 4] Interrupted system call
2115
log.info("[%s] _InFileProxy: done" % self.getName())
2117
def _proxyFromParentToChild(self):
2119
# Read output from the child process, and (for now) just write
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
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()
2132
#log.debug("XXX -> start read on %r" % self.fParent)
2134
text = self.fParent.read(CHUNKSIZE)
2135
except ValueError, ex:
2136
# ValueError is raised with trying to write to a closed
2139
#log.debug("XXX <- done read on %r" % self.fParent)
2141
# Empty text signifies that the pipe has been closed on
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.
2147
logres.info("[%s] _InFileProxy: closing child after "\
2148
"observing parent's close: %r", self.getName(),
2153
pass # Ignore: IOError: [Errno 4] Interrupted system call
2155
# Ignore: IOError: [Errno 9] Bad file descriptor
2156
# XXX Do we *know* we want to do that?
2160
log.info("[%s] _InFileProxy: read %d bytes from parent: %r"\
2161
% (self.getName(), len(text), text))
2163
log.info("[%s, %s] _InFileProxy: writing %r to child (%r)",
2164
self.getName(), id(self), text, self.fChild)
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))
2176
log.info("[%s] _InFileProxy: wrote %d bytes to child: %r"\
2177
% (self.getName(), len(text), text))
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.
2184
def __init__(self, fChild, fParent, toClose=[], name=None):
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.
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
2200
log.info("[%s] _OutFileProxy: start" % self.getName())
2202
self._proxyFromChildToParent()
2204
logres.info("[%s] _OutFileProxy: terminating, close child (%r)",
2205
self.getName(), self.fChild)
2209
pass # Ignore: IOError: [Errno 4] Interrupted system call
2210
log.info("[%s] _OutFileProxy: closing parent (%r)",
2211
self.getName(), self.fParent)
2213
self.fParent.close()
2215
pass # Ignore: IOError: [Errno 4] Interrupted system call
2217
logres.info("[%s] _OutFileProxy: closing %r after "\
2218
"closing parent", self.getName(), self.toClose[0])
2220
self.toClose[0].close()
2222
pass # Ignore: IOError: [Errno 4] Interrupted system call
2224
log.info("[%s] _OutFileProxy: done" % self.getName())
2226
def _proxyFromChildToParent(self):
2228
# Read output from the child process, and (for now) just write
2233
log.info("[%s] _OutFileProxy: waiting for read on child (%r)"\
2234
% (self.getName(), self.fChild))
2235
text = self.fChild.read(CHUNKSIZE)
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)
2243
# Empty text signifies that the pipe has been closed on
2245
log.info("[%s] _OutFileProxy: observed close of child (%r)"\
2246
% (self.getName(), self.fChild))
2249
log.info("[%s] _OutFileProxy: text(len=%d): %r",
2250
self.getName(), len(text), text)
2251
self.fParent.write(text)
2255
if sys.platform.startswith("linux"):
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
2263
class ProcessXXX(_ThreadFixer, BrokenProcessXXX):
2264
_pclass = BrokenProcessXXX
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().
2277
def __init__(self, *args, **kwargs):
2278
# Keep a reference to 'log' ensure it is around for this object's
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)
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.
2291
Sets self._pid as a side effect.
2293
self.__waiter = threading.Thread(
2294
target=self.__launchAndWait, args=args, kwargs=kwargs)
2296
# Start subthread that will launch child and wait until it
2298
self.__childStarted.acquire()
2299
self.__waiter.start()
2300
self.__childStarted.wait()
2301
self.__childStarted.release()
2303
def __launchAndWait(self, *args, **kwargs):
2304
"""Launch the given command and wait for it to terminate.
2306
When the process has terminated then store its exit value
2309
logfix.info("start child in thread %s",
2310
threading.currentThread().getName())
2312
# Spawn the child process and notify the main thread of
2314
self.__childStarted.acquire()
2315
self._pclass._forkAndExecChildOnUnix(self, *args, **kwargs)
2316
self.__childStarted.notifyAll()
2317
self.__childStarted.release()
2319
# Wait on the thread and store appropriate results when
2322
waitResult = self._pclass.wait(self)
2323
except ProcessError, ex:
2325
self.__hasTerminated.acquire()
2326
self.__terminationResult = waitResult
2327
self.__hasTerminated.notifyAll()
2328
self.__hasTerminated.release()
2330
self.__waiter = None # drop ref that would keep instance alive
2332
def wait(self, timeout=None):
2333
# If the process __hasTerminated then return the exit
2334
# status. Otherwise simulate the wait as appropriate.
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()
2347
if terminationResult is None:
2348
# process has not finished yet
2349
raise ProcessError("Wait for process timed out.",
2351
elif isinstance(terminationResult, Exception):
2352
# some error waiting for process termination
2353
raise terminationResult
2355
# the process terminated
2356
return terminationResult
2358
_ThreadBrokenProcess = Process
2359
class Process(_ThreadFixer, _ThreadBrokenProcess):
2360
_pclass = _ThreadBrokenProcess
2362
_ThreadBrokenProcessOpen = ProcessOpen
2363
class ProcessOpen(_ThreadFixer, _ThreadBrokenProcessOpen):
2364
_pclass = _ThreadBrokenProcessOpen
2366
_ThreadBrokenProcessProxy = ProcessProxy
2367
class ProcessProxy(_ThreadFixer, _ThreadBrokenProcessProxy):
2368
_pclass = _ThreadBrokenProcessProxy