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