##// END OF EJS Templates
test IPython.kernel
MinRK -
Show More
@@ -0,0 +1,253 b''
1 """Utilities for launching kernels
2
3 Authors:
4
5 * Min Ragan-Kelley
6
7 """
8
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2013 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Imports
18 #-----------------------------------------------------------------------------
19
20 import os
21 import sys
22 from subprocess import Popen, PIPE
23
24
25 #-----------------------------------------------------------------------------
26 # Launching Kernels
27 #-----------------------------------------------------------------------------
28
29 def swallow_argv(argv, aliases=None, flags=None):
30 """strip frontend-specific aliases and flags from an argument list
31
32 For use primarily in frontend apps that want to pass a subset of command-line
33 arguments through to a subprocess, where frontend-specific flags and aliases
34 should be removed from the list.
35
36 Parameters
37 ----------
38
39 argv : list(str)
40 The starting argv, to be filtered
41 aliases : container of aliases (dict, list, set, etc.)
42 The frontend-specific aliases to be removed
43 flags : container of flags (dict, list, set, etc.)
44 The frontend-specific flags to be removed
45
46 Returns
47 -------
48
49 argv : list(str)
50 The argv list, excluding flags and aliases that have been stripped
51 """
52
53 if aliases is None:
54 aliases = set()
55 if flags is None:
56 flags = set()
57
58 stripped = list(argv) # copy
59
60 swallow_next = False
61 was_flag = False
62 for a in argv:
63 if swallow_next:
64 swallow_next = False
65 # last arg was an alias, remove the next one
66 # *unless* the last alias has a no-arg flag version, in which
67 # case, don't swallow the next arg if it's also a flag:
68 if not (was_flag and a.startswith('-')):
69 stripped.remove(a)
70 continue
71 if a.startswith('-'):
72 split = a.lstrip('-').split('=')
73 alias = split[0]
74 if alias in aliases:
75 stripped.remove(a)
76 if len(split) == 1:
77 # alias passed with arg via space
78 swallow_next = True
79 # could have been a flag that matches an alias, e.g. `existing`
80 # in which case, we might not swallow the next arg
81 was_flag = alias in flags
82 elif alias in flags and len(split) == 1:
83 # strip flag, but don't swallow next, as flags don't take args
84 stripped.remove(a)
85
86 # return shortened list
87 return stripped
88
89
90 def make_ipkernel_cmd(code, executable=None, extra_arguments=[], **kw):
91 """Build Popen command list for launching an IPython kernel.
92
93 Parameters
94 ----------
95 code : str,
96 A string of Python code that imports and executes a kernel entry point.
97
98 executable : str, optional (default sys.executable)
99 The Python executable to use for the kernel process.
100
101 extra_arguments : list, optional
102 A list of extra arguments to pass when executing the launch code.
103
104 Returns
105 -------
106
107 A Popen command list
108 """
109
110 # Build the kernel launch command.
111 if executable is None:
112 executable = sys.executable
113 arguments = [ executable, '-c', code, '-f', '{connection_file}' ]
114 arguments.extend(extra_arguments)
115
116 # Spawn a kernel.
117 if sys.platform == 'win32':
118
119 # If the kernel is running on pythonw and stdout/stderr are not been
120 # re-directed, it will crash when more than 4KB of data is written to
121 # stdout or stderr. This is a bug that has been with Python for a very
122 # long time; see http://bugs.python.org/issue706263.
123 # A cleaner solution to this problem would be to pass os.devnull to
124 # Popen directly. Unfortunately, that does not work.
125 if executable.endswith('pythonw.exe'):
126 arguments.append('--no-stdout')
127 arguments.append('--no-stderr')
128
129 return arguments
130
131
132 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None,
133 independent=False,
134 cwd=None, ipython_kernel=True,
135 **kw
136 ):
137 """ Launches a localhost kernel, binding to the specified ports.
138
139 Parameters
140 ----------
141 cmd : Popen list,
142 A string of Python code that imports and executes a kernel entry point.
143
144 stdin, stdout, stderr : optional (default None)
145 Standards streams, as defined in subprocess.Popen.
146
147 independent : bool, optional (default False)
148 If set, the kernel process is guaranteed to survive if this process
149 dies. If not set, an effort is made to ensure that the kernel is killed
150 when this process dies. Note that in this case it is still good practice
151 to kill kernels manually before exiting.
152
153 cwd : path, optional
154 The working dir of the kernel process (default: cwd of this process).
155
156 ipython_kernel : bool, optional
157 Whether the kernel is an official IPython one,
158 and should get a bit of special treatment.
159
160 Returns
161 -------
162
163 Popen instance for the kernel subprocess
164 """
165
166 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
167 # are invalid. Unfortunately, there is in general no way to detect whether
168 # they are valid. The following two blocks redirect them to (temporary)
169 # pipes in certain important cases.
170
171 # If this process has been backgrounded, our stdin is invalid. Since there
172 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
173 # place this one safe and always redirect.
174 redirect_in = True
175 _stdin = PIPE if stdin is None else stdin
176
177 # If this process in running on pythonw, we know that stdin, stdout, and
178 # stderr are all invalid.
179 redirect_out = sys.executable.endswith('pythonw.exe')
180 if redirect_out:
181 _stdout = PIPE if stdout is None else stdout
182 _stderr = PIPE if stderr is None else stderr
183 else:
184 _stdout, _stderr = stdout, stderr
185
186 # Spawn a kernel.
187 if sys.platform == 'win32':
188 from IPython.zmq.parentpoller import ParentPollerWindows
189 # Create a Win32 event for interrupting the kernel.
190 interrupt_event = ParentPollerWindows.create_interrupt_event()
191 if ipython_kernel:
192 cmd += [ '--interrupt=%i' % interrupt_event ]
193
194 # If the kernel is running on pythonw and stdout/stderr are not been
195 # re-directed, it will crash when more than 4KB of data is written to
196 # stdout or stderr. This is a bug that has been with Python for a very
197 # long time; see http://bugs.python.org/issue706263.
198 # A cleaner solution to this problem would be to pass os.devnull to
199 # Popen directly. Unfortunately, that does not work.
200 if cmd[0].endswith('pythonw.exe'):
201 if stdout is None:
202 cmd.append('--no-stdout')
203 if stderr is None:
204 cmd.append('--no-stderr')
205
206 # Launch the kernel process.
207 if independent:
208 proc = Popen(cmd,
209 creationflags=512, # CREATE_NEW_PROCESS_GROUP
210 stdin=_stdin, stdout=_stdout, stderr=_stderr)
211 else:
212 if ipython_kernel:
213 try:
214 from _winapi import DuplicateHandle, GetCurrentProcess, \
215 DUPLICATE_SAME_ACCESS
216 except:
217 from _subprocess import DuplicateHandle, GetCurrentProcess, \
218 DUPLICATE_SAME_ACCESS
219 pid = GetCurrentProcess()
220 handle = DuplicateHandle(pid, pid, pid, 0,
221 True, # Inheritable by new processes.
222 DUPLICATE_SAME_ACCESS)
223 cmd +=[ '--parent=%i' % handle ]
224
225
226 proc = Popen(cmd,
227 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
228
229 # Attach the interrupt event to the Popen objet so it can be used later.
230 proc.win32_interrupt_event = interrupt_event
231
232 else:
233 if independent:
234 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
235 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
236 else:
237 if ipython_kernel:
238 cmd += ['--parent=1']
239 proc = Popen(cmd,
240 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
241
242 # Clean up pipes created to work around Popen bug.
243 if redirect_in:
244 if stdin is None:
245 proc.stdin.close()
246 if redirect_out:
247 if stdout is None:
248 proc.stdout.close()
249 if stderr is None:
250 proc.stderr.close()
251
252 return proc
253
1 NO CONTENT: new file 100644
@@ -24,7 +24,7 b' import nose.tools as nt'
24 24
25 25 # Our own imports
26 26 from IPython.testing import decorators as dec
27 from IPython.utils import kernel
27 from IPython.kernel.launcher import swallow_argv
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Classes and functions
@@ -51,7 +51,7 b' def test_swallow_argv():'
51 51 (['bar'], ['--foo=5', 'bar'], ['foo'], None),
52 52 ]
53 53 for expected, argv, aliases, flags in tests:
54 stripped = kernel.swallow_argv(argv, aliases=aliases, flags=flags)
54 stripped = swallow_argv(argv, aliases=aliases, flags=flags)
55 55 message = '\n'.join(['',
56 56 "argv: %r" % argv,
57 57 "aliases: %r" % aliases,
@@ -430,7 +430,7 b' def make_runners(inc_slow=False):'
430 430 """
431 431
432 432 # Packages to be tested via nose, that only depend on the stdlib
433 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
433 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'kernel', 'lib',
434 434 'testing', 'utils', 'nbformat', 'inprocess' ]
435 435
436 436 if have['zmq']:
General Comments 0
You need to be logged in to leave comments. Login now