##// END OF EJS Templates
Fix iptest under windows vista.
gvaroquaux -
Show More
@@ -1,182 +1,177 b''
1 1 # Addapted from killableprocess.py.
2 2 #______________________________________________________________________________
3 3 #
4 4 # killableprocess - subprocesses which can be reliably killed
5 5 #
6 6 # Parts of this module are copied from the subprocess.py file contained
7 7 # in the Python distribution.
8 8 #
9 9 # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
10 10 #
11 11 # Additions and modifications written by Benjamin Smedberg
12 12 # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
13 13 # <http://www.mozilla.org/>
14 14 #
15 15 # By obtaining, using, and/or copying this software and/or its
16 16 # associated documentation, you agree that you have read, understood,
17 17 # and will comply with the following terms and conditions:
18 18 #
19 19 # Permission to use, copy, modify, and distribute this software and
20 20 # its associated documentation for any purpose and without fee is
21 21 # hereby granted, provided that the above copyright notice appears in
22 22 # all copies, and that both that copyright notice and this permission
23 23 # notice appear in supporting documentation, and that the name of the
24 24 # author not be used in advertising or publicity pertaining to
25 25 # distribution of the software without specific, written prior
26 26 # permission.
27 27 #
28 28 # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
29 29 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
30 30 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
31 31 # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
32 32 # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
33 33 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
34 34 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 35
36 36 r"""killableprocess - Subprocesses which can be reliably killed
37 37
38 38 This module is a subclass of the builtin "subprocess" module. It allows
39 39 processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
40 40
41 41 It also adds a timeout argument to Wait() for a limited period of time before
42 42 forcefully killing the process.
43 43
44 44 Note: On Windows, this module requires Windows 2000 or higher (no support for
45 45 Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
46 46 Python 2.5+ or available from http://python.net/crew/theller/ctypes/
47 47 """
48 48
49 49 import subprocess
50 50 from subprocess import PIPE
51 51 import sys
52 52 import os
53 53 import types
54 54
55 55 try:
56 56 from subprocess import CalledProcessError
57 57 except ImportError:
58 58 # Python 2.4 doesn't implement CalledProcessError
59 59 class CalledProcessError(Exception):
60 60 """This exception is raised when a process run by check_call() returns
61 61 a non-zero exit status. The exit status will be stored in the
62 62 returncode attribute."""
63 63 def __init__(self, returncode, cmd):
64 64 self.returncode = returncode
65 65 self.cmd = cmd
66 66 def __str__(self):
67 67 return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
68 68
69 69 mswindows = (sys.platform == "win32")
70 70
71 71 skip = False
72 72
73 73 if mswindows:
74 # Some versions of windows, in some strange corner cases, give
75 # errror with the killableprocess.
76 if sys.version_info[:2] > (2, 4):
77 import platform
78 if platform.uname()[3] == '' or platform.uname()[3] > '6.0.6000':
79 skip = True
80 else:
81 import winprocess
82 else:
74 import platform
75 if platform.uname()[3] == '' or platform.uname()[3] > '6.0.6000':
83 76 skip = True
77 else:
78 import winprocess
84 79 else:
85 80 import signal
86 81
87 82 if not mswindows:
88 83 def DoNothing(*args):
89 84 pass
90 85
91 86
92 87 if skip:
93 88 Popen = subprocess.Popen
94 89 else:
95 90 class Popen(subprocess.Popen):
96 91 if not mswindows:
97 92 # Override __init__ to set a preexec_fn
98 93 def __init__(self, *args, **kwargs):
99 94 if len(args) >= 7:
100 95 raise Exception("Arguments preexec_fn and after must be passed by keyword.")
101 96
102 97 real_preexec_fn = kwargs.pop("preexec_fn", None)
103 98 def setpgid_preexec_fn():
104 99 os.setpgid(0, 0)
105 100 if real_preexec_fn:
106 101 apply(real_preexec_fn)
107 102
108 103 kwargs['preexec_fn'] = setpgid_preexec_fn
109 104
110 105 subprocess.Popen.__init__(self, *args, **kwargs)
111 106
112 107 if mswindows:
113 108 def _execute_child(self, args, executable, preexec_fn, close_fds,
114 109 cwd, env, universal_newlines, startupinfo,
115 110 creationflags, shell,
116 111 p2cread, p2cwrite,
117 112 c2pread, c2pwrite,
118 113 errread, errwrite):
119 114 if not isinstance(args, types.StringTypes):
120 115 args = subprocess.list2cmdline(args)
121 116
122 117 if startupinfo is None:
123 118 startupinfo = winprocess.STARTUPINFO()
124 119
125 120 if None not in (p2cread, c2pwrite, errwrite):
126 121 startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
127 122
128 123 startupinfo.hStdInput = int(p2cread)
129 124 startupinfo.hStdOutput = int(c2pwrite)
130 125 startupinfo.hStdError = int(errwrite)
131 126 if shell:
132 127 startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
133 128 startupinfo.wShowWindow = winprocess.SW_HIDE
134 129 comspec = os.environ.get("COMSPEC", "cmd.exe")
135 130 args = comspec + " /c " + args
136 131
137 132 # We create a new job for this process, so that we can kill
138 133 # the process and any sub-processes
139 134 self._job = winprocess.CreateJobObject()
140 135
141 136 creationflags |= winprocess.CREATE_SUSPENDED
142 137 creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
143 138
144 139 hp, ht, pid, tid = winprocess.CreateProcess(
145 140 executable, args,
146 141 None, None, # No special security
147 142 1, # Must inherit handles!
148 143 creationflags,
149 144 winprocess.EnvironmentBlock(env),
150 145 cwd, startupinfo)
151 146
152 147 self._child_created = True
153 148 self._handle = hp
154 149 self._thread = ht
155 150 self.pid = pid
156 151
157 152 winprocess.AssignProcessToJobObject(self._job, hp)
158 153 winprocess.ResumeThread(ht)
159 154
160 155 if p2cread is not None:
161 156 p2cread.Close()
162 157 if c2pwrite is not None:
163 158 c2pwrite.Close()
164 159 if errwrite is not None:
165 160 errwrite.Close()
166 161
167 162 def kill(self, group=True):
168 163 """Kill the process. If group=True, all sub-processes will also be killed."""
169 164 if mswindows:
170 165 if group:
171 166 winprocess.TerminateJobObject(self._job, 127)
172 167 else:
173 168 winprocess.TerminateProcess(self._handle, 127)
174 169 self.returncode = 127
175 170 else:
176 171 if group:
177 172 os.killpg(self.pid, signal.SIGKILL)
178 173 else:
179 174 os.kill(self.pid, signal.SIGKILL)
180 175 self.returncode = -9
181 176
182 177
@@ -1,74 +1,74 b''
1 1 # encoding: utf-8
2 2 """
3 3 Object for encapsulating process execution by using callbacks for stdout,
4 4 stderr and stdin.
5 5 """
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 15 #-------------------------------------------------------------------------------
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18 from killableprocess import Popen, PIPE
19 19 from threading import Thread
20 20 from time import sleep
21 21 import os
22 22
23 23 class PipedProcess(Thread):
24 24 """ Class that encapsulates process execution by using callbacks for
25 25 stdout, stderr and stdin, and providing a reliable way of
26 26 killing it.
27 27 """
28 28
29 29 def __init__(self, command_string, out_callback,
30 30 end_callback=None,):
31 31 """ command_string: the command line executed to start the
32 32 process.
33 33
34 34 out_callback: the python callable called on stdout/stderr.
35 35
36 36 end_callback: an optional callable called when the process
37 37 finishes.
38 38
39 39 These callbacks are called from a different thread as the
40 40 thread from which is started.
41 41 """
42 42 self.command_string = command_string
43 43 self.out_callback = out_callback
44 44 self.end_callback = end_callback
45 45 Thread.__init__(self)
46 46
47 47
48 48 def run(self):
49 49 """ Start the process and hook up the callbacks.
50 50 """
51 51 env = os.environ
52 52 env['TERM'] = 'xterm'
53 process = Popen((self.command_string + ' 2>&1', ), shell=True,
53 process = Popen(self.command_string + ' 2>&1', shell=True,
54 54 env=env,
55 55 universal_newlines=True,
56 56 stdout=PIPE, stdin=PIPE, )
57 57 self.process = process
58 58 while True:
59 59 out_char = process.stdout.read(1)
60 60 if out_char == '':
61 61 if process.poll() is not None:
62 62 # The process has finished
63 63 break
64 64 else:
65 65 # The process is not giving any interesting
66 66 # output. No use polling it immediatly.
67 67 sleep(0.1)
68 68 else:
69 69 self.out_callback(out_char)
70 70
71 71 if self.end_callback is not None:
72 72 self.end_callback()
73 73
74 74
@@ -1,73 +1,67 b''
1 1 # encoding: utf-8
2 2 """
3 3 Test process execution and IO redirection.
4 4 """
5 5
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is
12 12 # in the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 15 from cStringIO import StringIO
16 16 from time import sleep
17 17 import sys
18 18
19 19 from IPython.frontend._process import PipedProcess
20 20 from IPython.testing import decorators as testdec
21 21
22 22
23 # FIXME
24 @testdec.skip("This doesn't work under Windows")
25 23 def test_capture_out():
26 24 """ A simple test to see if we can execute a process and get the output.
27 25 """
28 26 s = StringIO()
29 27 p = PipedProcess('echo 1', out_callback=s.write, )
30 28 p.start()
31 29 p.join()
32 30 result = s.getvalue().rstrip()
33 31 assert result == '1'
34 32
35 33
36 # FIXME
37 @testdec.skip("This doesn't work under Windows")
38 34 def test_io():
39 35 """ Checks that we can send characters on stdin to the process.
40 36 """
41 37 s = StringIO()
42 38 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
43 39 out_callback=s.write, )
44 40 p.start()
45 41 test_string = '12345\n'
46 42 while not hasattr(p, 'process'):
47 43 sleep(0.1)
48 44 p.process.stdin.write(test_string)
49 45 p.join()
50 46 result = s.getvalue()
51 47 assert result == test_string
52 48
53 49
54 # FIXME
55 @testdec.skip("This doesn't work under Windows")
56 50 def test_kill():
57 51 """ Check that we can kill a process, and its subprocess.
58 52 """
59 53 s = StringIO()
60 54 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
61 55 out_callback=s.write, )
62 56 p.start()
63 57 while not hasattr(p, 'process'):
64 58 sleep(0.1)
65 59 p.process.kill()
66 60 assert p.process.poll() is not None
67 61
68 62
69 63 if __name__ == '__main__':
70 64 test_capture_out()
71 65 test_io()
72 66 test_kill()
73 67
General Comments 0
You need to be logged in to leave comments. Login now