]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/process.py
Don't use the old wxPython namespace
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / process.py
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