##// END OF EJS Templates
Merge branch 'newkernel' of github.com:ipython/ipython into upstream-newkernel
Brian Granger -
r2946:c7e6d9ea merge
parent child Browse files
Show More
@@ -1,211 +1,217 b''
1 1 """ Defines helper functions for creating kernel entry points and process
2 2 launchers.
3 3 """
4 4
5 5 # Standard library imports.
6 6 import os
7 7 import socket
8 from subprocess import Popen
8 from subprocess import Popen, PIPE
9 9 import sys
10 10
11 11 # System library imports.
12 12 import zmq
13 13
14 14 # Local imports.
15 15 from IPython.core.ultratb import FormattedTB
16 16 from IPython.external.argparse import ArgumentParser
17 17 from IPython.utils import io
18 18 from exitpoller import ExitPollerUnix, ExitPollerWindows
19 19 from displayhook import DisplayHook
20 20 from iostream import OutStream
21 21 from session import Session
22 22 from heartbeat import Heartbeat
23 23
24 24 def bind_port(socket, ip, port):
25 25 """ Binds the specified ZMQ socket. If the port is zero, a random port is
26 26 chosen. Returns the port that was bound.
27 27 """
28 28 connection = 'tcp://%s' % ip
29 29 if port <= 0:
30 30 port = socket.bind_to_random_port(connection)
31 31 else:
32 32 connection += ':%i' % port
33 33 socket.bind(connection)
34 34 return port
35 35
36 36
37 37 def make_argument_parser():
38 38 """ Creates an ArgumentParser for the generic arguments supported by all
39 39 kernel entry points.
40 40 """
41 41 parser = ArgumentParser()
42 42 parser.add_argument('--ip', type=str, default='127.0.0.1',
43 43 help='set the kernel\'s IP address [default: local]')
44 44 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
45 45 help='set the XREP channel port [default: random]')
46 46 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
47 47 help='set the PUB channel port [default: random]')
48 48 parser.add_argument('--req', type=int, metavar='PORT', default=0,
49 49 help='set the REQ channel port [default: random]')
50 50 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
51 51 help='set the heartbeat port [default: random]')
52 52
53 53 if sys.platform == 'win32':
54 54 parser.add_argument('--parent', type=int, metavar='HANDLE',
55 55 default=0, help='kill this process if the process '
56 56 'with HANDLE dies')
57 57 else:
58 58 parser.add_argument('--parent', action='store_true',
59 59 help='kill this process if its parent dies')
60 60
61 61 return parser
62 62
63 63
64 64 def make_kernel(namespace, kernel_factory,
65 65 out_stream_factory=None, display_hook_factory=None):
66 66 """ Creates a kernel.
67 67 """
68 68 # Install minimal exception handling
69 69 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
70 70 ostream=sys.__stdout__)
71 71
72 72 # Create a context, a session, and the kernel sockets.
73 73 io.raw_print("Starting the kernel at pid:", os.getpid())
74 74 context = zmq.Context()
75 75 session = Session(username=u'kernel')
76 76
77 77 reply_socket = context.socket(zmq.XREP)
78 78 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
79 79 io.raw_print("XREP Channel on port", xrep_port)
80 80
81 81 pub_socket = context.socket(zmq.PUB)
82 82 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
83 83 io.raw_print("PUB Channel on port", pub_port)
84 84
85 85 req_socket = context.socket(zmq.XREQ)
86 86 req_port = bind_port(req_socket, namespace.ip, namespace.req)
87 87 io.raw_print("REQ Channel on port", req_port)
88 88
89 89 hb = Heartbeat(context, (namespace.ip, namespace.hb))
90 90 hb.start()
91 91 io.raw_print("Heartbeat REP Channel on port", hb.port)
92 92
93 93 # Redirect input streams and set a display hook.
94 94 if out_stream_factory:
95 95 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
96 96 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
97 97 if display_hook_factory:
98 98 sys.displayhook = display_hook_factory(session, pub_socket)
99 99
100 100 # Create the kernel.
101 101 return kernel_factory(session=session, reply_socket=reply_socket,
102 102 pub_socket=pub_socket, req_socket=req_socket)
103 103
104 104
105 105 def start_kernel(namespace, kernel):
106 106 """ Starts a kernel.
107 107 """
108 108 # Configure this kernel/process to die on parent termination, if necessary.
109 109 if namespace.parent:
110 110 if sys.platform == 'win32':
111 111 poller = ExitPollerWindows(namespace.parent)
112 112 else:
113 113 poller = ExitPollerUnix()
114 114 poller.start()
115 115
116 116 # Start the kernel mainloop.
117 117 kernel.start()
118 118
119 119
120 120 def make_default_main(kernel_factory):
121 121 """ Creates the simplest possible kernel entry point.
122 122 """
123 123 def main():
124 124 namespace = make_argument_parser().parse_args()
125 125 kernel = make_kernel(namespace, kernel_factory, OutStream, DisplayHook)
126 126 start_kernel(namespace, kernel)
127 127 return main
128 128
129 129
130 130 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
131 131 independent=False, extra_arguments=[]):
132 132 """ Launches a localhost kernel, binding to the specified ports.
133 133
134 134 Parameters
135 135 ----------
136 136 code : str,
137 137 A string of Python code that imports and executes a kernel entry point.
138 138
139 139 xrep_port : int, optional
140 140 The port to use for XREP channel.
141 141
142 142 pub_port : int, optional
143 143 The port to use for the SUB channel.
144 144
145 145 req_port : int, optional
146 146 The port to use for the REQ (raw input) channel.
147 147
148 148 hb_port : int, optional
149 149 The port to use for the hearbeat REP channel.
150 150
151 151 independent : bool, optional (default False)
152 152 If set, the kernel process is guaranteed to survive if this process
153 153 dies. If not set, an effort is made to ensure that the kernel is killed
154 154 when this process dies. Note that in this case it is still good practice
155 155 to kill kernels manually before exiting.
156 156
157 157 extra_arguments = list, optional
158 158 A list of extra arguments to pass when executing the launch code.
159 159
160 160 Returns
161 161 -------
162 162 A tuple of form:
163 163 (kernel_process, xrep_port, pub_port, req_port)
164 164 where kernel_process is a Popen object and the ports are integers.
165 165 """
166 166 # Find open ports as necessary.
167 167 ports = []
168 168 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + \
169 169 int(req_port <= 0) + int(hb_port <= 0)
170 170 for i in xrange(ports_needed):
171 171 sock = socket.socket()
172 172 sock.bind(('', 0))
173 173 ports.append(sock)
174 174 for i, sock in enumerate(ports):
175 175 port = sock.getsockname()[1]
176 176 sock.close()
177 177 ports[i] = port
178 178 if xrep_port <= 0:
179 179 xrep_port = ports.pop(0)
180 180 if pub_port <= 0:
181 181 pub_port = ports.pop(0)
182 182 if req_port <= 0:
183 183 req_port = ports.pop(0)
184 184 if hb_port <= 0:
185 185 hb_port = ports.pop(0)
186 186
187 187 # Build the kernel launch command.
188 188 arguments = [ sys.executable, '-c', code, '--xrep', str(xrep_port),
189 189 '--pub', str(pub_port), '--req', str(req_port),
190 190 '--hb', str(hb_port) ]
191 191 arguments.extend(extra_arguments)
192 192
193 193 # Spawn a kernel.
194 if independent:
195 if sys.platform == 'win32':
196 proc = Popen(['start', '/b'] + arguments, shell=True)
194 if sys.platform == 'win32':
195 # If using pythonw, stdin, stdout, and stderr are invalid. Popen will
196 # fail unless they are suitably redirected. We don't read from the
197 # pipes, but they must exist.
198 redirect = PIPE if sys.executable.endswith('pythonw.exe') else None
199 if independent:
200 proc = Popen(['start', '/b'] + arguments, shell=True,
201 stdout=redirect, stderr=redirect, stdin=redirect)
197 202 else:
198 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
199 else:
200 if sys.platform == 'win32':
201 203 from _subprocess import DuplicateHandle, GetCurrentProcess, \
202 204 DUPLICATE_SAME_ACCESS
203 205 pid = GetCurrentProcess()
204 206 handle = DuplicateHandle(pid, pid, pid, 0,
205 207 True, # Inheritable by new processes.
206 208 DUPLICATE_SAME_ACCESS)
207 proc = Popen(arguments + ['--parent', str(int(handle))])
209 proc = Popen(arguments + ['--parent', str(int(handle))],
210 stdout=redirect, stderr=redirect, stdin=redirect)
211 else:
212 if independent:
213 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
208 214 else:
209 215 proc = Popen(arguments + ['--parent'])
210 216
211 217 return proc, xrep_port, pub_port, req_port, hb_port
General Comments 0
You need to be logged in to leave comments. Login now