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