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