##// END OF EJS Templates
Merge pull request #4346 from takluyver/win-kernel-getpass...
Min RK -
r12914:a34479b9 merge
parent child Browse files
Show More
@@ -1,558 +1,558 b''
1 """Utilities for connecting to kernels
1 """Utilities for connecting to kernels
2
2
3 Authors:
3 Authors:
4
4
5 * Min Ragan-Kelley
5 * Min Ragan-Kelley
6
6
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2013 The IPython Development Team
10 # Copyright (C) 2013 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import absolute_import
20 from __future__ import absolute_import
21
21
22 import glob
22 import glob
23 import json
23 import json
24 import os
24 import os
25 import socket
25 import socket
26 import sys
26 import sys
27 from getpass import getpass
27 from getpass import getpass
28 from subprocess import Popen, PIPE
28 from subprocess import Popen, PIPE
29 import tempfile
29 import tempfile
30
30
31 import zmq
31 import zmq
32
32
33 # external imports
33 # external imports
34 from IPython.external.ssh import tunnel
34 from IPython.external.ssh import tunnel
35
35
36 # IPython imports
36 # IPython imports
37 from IPython.config import Configurable
37 from IPython.config import Configurable
38 from IPython.core.profiledir import ProfileDir
38 from IPython.core.profiledir import ProfileDir
39 from IPython.utils.localinterfaces import localhost
39 from IPython.utils.localinterfaces import localhost
40 from IPython.utils.path import filefind, get_ipython_dir
40 from IPython.utils.path import filefind, get_ipython_dir
41 from IPython.utils.py3compat import str_to_bytes, bytes_to_str
41 from IPython.utils.py3compat import str_to_bytes, bytes_to_str, cast_bytes_py2
42 from IPython.utils.traitlets import (
42 from IPython.utils.traitlets import (
43 Bool, Integer, Unicode, CaselessStrEnum,
43 Bool, Integer, Unicode, CaselessStrEnum,
44 )
44 )
45
45
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Working with Connection Files
48 # Working with Connection Files
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
51 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
52 control_port=0, ip='', key=b'', transport='tcp',
52 control_port=0, ip='', key=b'', transport='tcp',
53 signature_scheme='hmac-sha256',
53 signature_scheme='hmac-sha256',
54 ):
54 ):
55 """Generates a JSON config file, including the selection of random ports.
55 """Generates a JSON config file, including the selection of random ports.
56
56
57 Parameters
57 Parameters
58 ----------
58 ----------
59
59
60 fname : unicode
60 fname : unicode
61 The path to the file to write
61 The path to the file to write
62
62
63 shell_port : int, optional
63 shell_port : int, optional
64 The port to use for ROUTER (shell) channel.
64 The port to use for ROUTER (shell) channel.
65
65
66 iopub_port : int, optional
66 iopub_port : int, optional
67 The port to use for the SUB channel.
67 The port to use for the SUB channel.
68
68
69 stdin_port : int, optional
69 stdin_port : int, optional
70 The port to use for the ROUTER (raw input) channel.
70 The port to use for the ROUTER (raw input) channel.
71
71
72 control_port : int, optional
72 control_port : int, optional
73 The port to use for the ROUTER (control) channel.
73 The port to use for the ROUTER (control) channel.
74
74
75 hb_port : int, optional
75 hb_port : int, optional
76 The port to use for the heartbeat REP channel.
76 The port to use for the heartbeat REP channel.
77
77
78 ip : str, optional
78 ip : str, optional
79 The ip address the kernel will bind to.
79 The ip address the kernel will bind to.
80
80
81 key : str, optional
81 key : str, optional
82 The Session key used for message authentication.
82 The Session key used for message authentication.
83
83
84 signature_scheme : str, optional
84 signature_scheme : str, optional
85 The scheme used for message authentication.
85 The scheme used for message authentication.
86 This has the form 'digest-hash', where 'digest'
86 This has the form 'digest-hash', where 'digest'
87 is the scheme used for digests, and 'hash' is the name of the hash function
87 is the scheme used for digests, and 'hash' is the name of the hash function
88 used by the digest scheme.
88 used by the digest scheme.
89 Currently, 'hmac' is the only supported digest scheme,
89 Currently, 'hmac' is the only supported digest scheme,
90 and 'sha256' is the default hash function.
90 and 'sha256' is the default hash function.
91
91
92 """
92 """
93 if not ip:
93 if not ip:
94 ip = localhost()
94 ip = localhost()
95 # default to temporary connector file
95 # default to temporary connector file
96 if not fname:
96 if not fname:
97 fname = tempfile.mktemp('.json')
97 fname = tempfile.mktemp('.json')
98
98
99 # Find open ports as necessary.
99 # Find open ports as necessary.
100
100
101 ports = []
101 ports = []
102 ports_needed = int(shell_port <= 0) + \
102 ports_needed = int(shell_port <= 0) + \
103 int(iopub_port <= 0) + \
103 int(iopub_port <= 0) + \
104 int(stdin_port <= 0) + \
104 int(stdin_port <= 0) + \
105 int(control_port <= 0) + \
105 int(control_port <= 0) + \
106 int(hb_port <= 0)
106 int(hb_port <= 0)
107 if transport == 'tcp':
107 if transport == 'tcp':
108 for i in range(ports_needed):
108 for i in range(ports_needed):
109 sock = socket.socket()
109 sock = socket.socket()
110 sock.bind(('', 0))
110 sock.bind(('', 0))
111 ports.append(sock)
111 ports.append(sock)
112 for i, sock in enumerate(ports):
112 for i, sock in enumerate(ports):
113 port = sock.getsockname()[1]
113 port = sock.getsockname()[1]
114 sock.close()
114 sock.close()
115 ports[i] = port
115 ports[i] = port
116 else:
116 else:
117 N = 1
117 N = 1
118 for i in range(ports_needed):
118 for i in range(ports_needed):
119 while os.path.exists("%s-%s" % (ip, str(N))):
119 while os.path.exists("%s-%s" % (ip, str(N))):
120 N += 1
120 N += 1
121 ports.append(N)
121 ports.append(N)
122 N += 1
122 N += 1
123 if shell_port <= 0:
123 if shell_port <= 0:
124 shell_port = ports.pop(0)
124 shell_port = ports.pop(0)
125 if iopub_port <= 0:
125 if iopub_port <= 0:
126 iopub_port = ports.pop(0)
126 iopub_port = ports.pop(0)
127 if stdin_port <= 0:
127 if stdin_port <= 0:
128 stdin_port = ports.pop(0)
128 stdin_port = ports.pop(0)
129 if control_port <= 0:
129 if control_port <= 0:
130 control_port = ports.pop(0)
130 control_port = ports.pop(0)
131 if hb_port <= 0:
131 if hb_port <= 0:
132 hb_port = ports.pop(0)
132 hb_port = ports.pop(0)
133
133
134 cfg = dict( shell_port=shell_port,
134 cfg = dict( shell_port=shell_port,
135 iopub_port=iopub_port,
135 iopub_port=iopub_port,
136 stdin_port=stdin_port,
136 stdin_port=stdin_port,
137 control_port=control_port,
137 control_port=control_port,
138 hb_port=hb_port,
138 hb_port=hb_port,
139 )
139 )
140 cfg['ip'] = ip
140 cfg['ip'] = ip
141 cfg['key'] = bytes_to_str(key)
141 cfg['key'] = bytes_to_str(key)
142 cfg['transport'] = transport
142 cfg['transport'] = transport
143 cfg['signature_scheme'] = signature_scheme
143 cfg['signature_scheme'] = signature_scheme
144
144
145 with open(fname, 'w') as f:
145 with open(fname, 'w') as f:
146 f.write(json.dumps(cfg, indent=2))
146 f.write(json.dumps(cfg, indent=2))
147
147
148 return fname, cfg
148 return fname, cfg
149
149
150
150
151 def get_connection_file(app=None):
151 def get_connection_file(app=None):
152 """Return the path to the connection file of an app
152 """Return the path to the connection file of an app
153
153
154 Parameters
154 Parameters
155 ----------
155 ----------
156 app : IPKernelApp instance [optional]
156 app : IPKernelApp instance [optional]
157 If unspecified, the currently running app will be used
157 If unspecified, the currently running app will be used
158 """
158 """
159 if app is None:
159 if app is None:
160 from IPython.kernel.zmq.kernelapp import IPKernelApp
160 from IPython.kernel.zmq.kernelapp import IPKernelApp
161 if not IPKernelApp.initialized():
161 if not IPKernelApp.initialized():
162 raise RuntimeError("app not specified, and not in a running Kernel")
162 raise RuntimeError("app not specified, and not in a running Kernel")
163
163
164 app = IPKernelApp.instance()
164 app = IPKernelApp.instance()
165 return filefind(app.connection_file, ['.', app.profile_dir.security_dir])
165 return filefind(app.connection_file, ['.', app.profile_dir.security_dir])
166
166
167
167
168 def find_connection_file(filename, profile=None):
168 def find_connection_file(filename, profile=None):
169 """find a connection file, and return its absolute path.
169 """find a connection file, and return its absolute path.
170
170
171 The current working directory and the profile's security
171 The current working directory and the profile's security
172 directory will be searched for the file if it is not given by
172 directory will be searched for the file if it is not given by
173 absolute path.
173 absolute path.
174
174
175 If profile is unspecified, then the current running application's
175 If profile is unspecified, then the current running application's
176 profile will be used, or 'default', if not run from IPython.
176 profile will be used, or 'default', if not run from IPython.
177
177
178 If the argument does not match an existing file, it will be interpreted as a
178 If the argument does not match an existing file, it will be interpreted as a
179 fileglob, and the matching file in the profile's security dir with
179 fileglob, and the matching file in the profile's security dir with
180 the latest access time will be used.
180 the latest access time will be used.
181
181
182 Parameters
182 Parameters
183 ----------
183 ----------
184 filename : str
184 filename : str
185 The connection file or fileglob to search for.
185 The connection file or fileglob to search for.
186 profile : str [optional]
186 profile : str [optional]
187 The name of the profile to use when searching for the connection file,
187 The name of the profile to use when searching for the connection file,
188 if different from the current IPython session or 'default'.
188 if different from the current IPython session or 'default'.
189
189
190 Returns
190 Returns
191 -------
191 -------
192 str : The absolute path of the connection file.
192 str : The absolute path of the connection file.
193 """
193 """
194 from IPython.core.application import BaseIPythonApplication as IPApp
194 from IPython.core.application import BaseIPythonApplication as IPApp
195 try:
195 try:
196 # quick check for absolute path, before going through logic
196 # quick check for absolute path, before going through logic
197 return filefind(filename)
197 return filefind(filename)
198 except IOError:
198 except IOError:
199 pass
199 pass
200
200
201 if profile is None:
201 if profile is None:
202 # profile unspecified, check if running from an IPython app
202 # profile unspecified, check if running from an IPython app
203 if IPApp.initialized():
203 if IPApp.initialized():
204 app = IPApp.instance()
204 app = IPApp.instance()
205 profile_dir = app.profile_dir
205 profile_dir = app.profile_dir
206 else:
206 else:
207 # not running in IPython, use default profile
207 # not running in IPython, use default profile
208 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), 'default')
208 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), 'default')
209 else:
209 else:
210 # find profiledir by profile name:
210 # find profiledir by profile name:
211 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
211 profile_dir = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
212 security_dir = profile_dir.security_dir
212 security_dir = profile_dir.security_dir
213
213
214 try:
214 try:
215 # first, try explicit name
215 # first, try explicit name
216 return filefind(filename, ['.', security_dir])
216 return filefind(filename, ['.', security_dir])
217 except IOError:
217 except IOError:
218 pass
218 pass
219
219
220 # not found by full name
220 # not found by full name
221
221
222 if '*' in filename:
222 if '*' in filename:
223 # given as a glob already
223 # given as a glob already
224 pat = filename
224 pat = filename
225 else:
225 else:
226 # accept any substring match
226 # accept any substring match
227 pat = '*%s*' % filename
227 pat = '*%s*' % filename
228 matches = glob.glob( os.path.join(security_dir, pat) )
228 matches = glob.glob( os.path.join(security_dir, pat) )
229 if not matches:
229 if not matches:
230 raise IOError("Could not find %r in %r" % (filename, security_dir))
230 raise IOError("Could not find %r in %r" % (filename, security_dir))
231 elif len(matches) == 1:
231 elif len(matches) == 1:
232 return matches[0]
232 return matches[0]
233 else:
233 else:
234 # get most recent match, by access time:
234 # get most recent match, by access time:
235 return sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
235 return sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
236
236
237
237
238 def get_connection_info(connection_file=None, unpack=False, profile=None):
238 def get_connection_info(connection_file=None, unpack=False, profile=None):
239 """Return the connection information for the current Kernel.
239 """Return the connection information for the current Kernel.
240
240
241 Parameters
241 Parameters
242 ----------
242 ----------
243 connection_file : str [optional]
243 connection_file : str [optional]
244 The connection file to be used. Can be given by absolute path, or
244 The connection file to be used. Can be given by absolute path, or
245 IPython will search in the security directory of a given profile.
245 IPython will search in the security directory of a given profile.
246 If run from IPython,
246 If run from IPython,
247
247
248 If unspecified, the connection file for the currently running
248 If unspecified, the connection file for the currently running
249 IPython Kernel will be used, which is only allowed from inside a kernel.
249 IPython Kernel will be used, which is only allowed from inside a kernel.
250 unpack : bool [default: False]
250 unpack : bool [default: False]
251 if True, return the unpacked dict, otherwise just the string contents
251 if True, return the unpacked dict, otherwise just the string contents
252 of the file.
252 of the file.
253 profile : str [optional]
253 profile : str [optional]
254 The name of the profile to use when searching for the connection file,
254 The name of the profile to use when searching for the connection file,
255 if different from the current IPython session or 'default'.
255 if different from the current IPython session or 'default'.
256
256
257
257
258 Returns
258 Returns
259 -------
259 -------
260 The connection dictionary of the current kernel, as string or dict,
260 The connection dictionary of the current kernel, as string or dict,
261 depending on `unpack`.
261 depending on `unpack`.
262 """
262 """
263 if connection_file is None:
263 if connection_file is None:
264 # get connection file from current kernel
264 # get connection file from current kernel
265 cf = get_connection_file()
265 cf = get_connection_file()
266 else:
266 else:
267 # connection file specified, allow shortnames:
267 # connection file specified, allow shortnames:
268 cf = find_connection_file(connection_file, profile=profile)
268 cf = find_connection_file(connection_file, profile=profile)
269
269
270 with open(cf) as f:
270 with open(cf) as f:
271 info = f.read()
271 info = f.read()
272
272
273 if unpack:
273 if unpack:
274 info = json.loads(info)
274 info = json.loads(info)
275 # ensure key is bytes:
275 # ensure key is bytes:
276 info['key'] = str_to_bytes(info.get('key', ''))
276 info['key'] = str_to_bytes(info.get('key', ''))
277 return info
277 return info
278
278
279
279
280 def connect_qtconsole(connection_file=None, argv=None, profile=None):
280 def connect_qtconsole(connection_file=None, argv=None, profile=None):
281 """Connect a qtconsole to the current kernel.
281 """Connect a qtconsole to the current kernel.
282
282
283 This is useful for connecting a second qtconsole to a kernel, or to a
283 This is useful for connecting a second qtconsole to a kernel, or to a
284 local notebook.
284 local notebook.
285
285
286 Parameters
286 Parameters
287 ----------
287 ----------
288 connection_file : str [optional]
288 connection_file : str [optional]
289 The connection file to be used. Can be given by absolute path, or
289 The connection file to be used. Can be given by absolute path, or
290 IPython will search in the security directory of a given profile.
290 IPython will search in the security directory of a given profile.
291 If run from IPython,
291 If run from IPython,
292
292
293 If unspecified, the connection file for the currently running
293 If unspecified, the connection file for the currently running
294 IPython Kernel will be used, which is only allowed from inside a kernel.
294 IPython Kernel will be used, which is only allowed from inside a kernel.
295 argv : list [optional]
295 argv : list [optional]
296 Any extra args to be passed to the console.
296 Any extra args to be passed to the console.
297 profile : str [optional]
297 profile : str [optional]
298 The name of the profile to use when searching for the connection file,
298 The name of the profile to use when searching for the connection file,
299 if different from the current IPython session or 'default'.
299 if different from the current IPython session or 'default'.
300
300
301
301
302 Returns
302 Returns
303 -------
303 -------
304 subprocess.Popen instance running the qtconsole frontend
304 subprocess.Popen instance running the qtconsole frontend
305 """
305 """
306 argv = [] if argv is None else argv
306 argv = [] if argv is None else argv
307
307
308 if connection_file is None:
308 if connection_file is None:
309 # get connection file from current kernel
309 # get connection file from current kernel
310 cf = get_connection_file()
310 cf = get_connection_file()
311 else:
311 else:
312 cf = find_connection_file(connection_file, profile=profile)
312 cf = find_connection_file(connection_file, profile=profile)
313
313
314 cmd = ';'.join([
314 cmd = ';'.join([
315 "from IPython.qt.console import qtconsoleapp",
315 "from IPython.qt.console import qtconsoleapp",
316 "qtconsoleapp.main()"
316 "qtconsoleapp.main()"
317 ])
317 ])
318
318
319 return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv,
319 return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv,
320 stdout=PIPE, stderr=PIPE, close_fds=(sys.platform != 'win32'),
320 stdout=PIPE, stderr=PIPE, close_fds=(sys.platform != 'win32'),
321 )
321 )
322
322
323
323
324 def tunnel_to_kernel(connection_info, sshserver, sshkey=None):
324 def tunnel_to_kernel(connection_info, sshserver, sshkey=None):
325 """tunnel connections to a kernel via ssh
325 """tunnel connections to a kernel via ssh
326
326
327 This will open four SSH tunnels from localhost on this machine to the
327 This will open four SSH tunnels from localhost on this machine to the
328 ports associated with the kernel. They can be either direct
328 ports associated with the kernel. They can be either direct
329 localhost-localhost tunnels, or if an intermediate server is necessary,
329 localhost-localhost tunnels, or if an intermediate server is necessary,
330 the kernel must be listening on a public IP.
330 the kernel must be listening on a public IP.
331
331
332 Parameters
332 Parameters
333 ----------
333 ----------
334 connection_info : dict or str (path)
334 connection_info : dict or str (path)
335 Either a connection dict, or the path to a JSON connection file
335 Either a connection dict, or the path to a JSON connection file
336 sshserver : str
336 sshserver : str
337 The ssh sever to use to tunnel to the kernel. Can be a full
337 The ssh sever to use to tunnel to the kernel. Can be a full
338 `user@server:port` string. ssh config aliases are respected.
338 `user@server:port` string. ssh config aliases are respected.
339 sshkey : str [optional]
339 sshkey : str [optional]
340 Path to file containing ssh key to use for authentication.
340 Path to file containing ssh key to use for authentication.
341 Only necessary if your ssh config does not already associate
341 Only necessary if your ssh config does not already associate
342 a keyfile with the host.
342 a keyfile with the host.
343
343
344 Returns
344 Returns
345 -------
345 -------
346
346
347 (shell, iopub, stdin, hb) : ints
347 (shell, iopub, stdin, hb) : ints
348 The four ports on localhost that have been forwarded to the kernel.
348 The four ports on localhost that have been forwarded to the kernel.
349 """
349 """
350 if isinstance(connection_info, basestring):
350 if isinstance(connection_info, basestring):
351 # it's a path, unpack it
351 # it's a path, unpack it
352 with open(connection_info) as f:
352 with open(connection_info) as f:
353 connection_info = json.loads(f.read())
353 connection_info = json.loads(f.read())
354
354
355 cf = connection_info
355 cf = connection_info
356
356
357 lports = tunnel.select_random_ports(4)
357 lports = tunnel.select_random_ports(4)
358 rports = cf['shell_port'], cf['iopub_port'], cf['stdin_port'], cf['hb_port']
358 rports = cf['shell_port'], cf['iopub_port'], cf['stdin_port'], cf['hb_port']
359
359
360 remote_ip = cf['ip']
360 remote_ip = cf['ip']
361
361
362 if tunnel.try_passwordless_ssh(sshserver, sshkey):
362 if tunnel.try_passwordless_ssh(sshserver, sshkey):
363 password=False
363 password=False
364 else:
364 else:
365 password = getpass("SSH Password for %s: "%sshserver)
365 password = getpass("SSH Password for %s: " % cast_bytes_py2(sshserver))
366
366
367 for lp,rp in zip(lports, rports):
367 for lp,rp in zip(lports, rports):
368 tunnel.ssh_tunnel(lp, rp, sshserver, remote_ip, sshkey, password)
368 tunnel.ssh_tunnel(lp, rp, sshserver, remote_ip, sshkey, password)
369
369
370 return tuple(lports)
370 return tuple(lports)
371
371
372
372
373 #-----------------------------------------------------------------------------
373 #-----------------------------------------------------------------------------
374 # Mixin for classes that work with connection files
374 # Mixin for classes that work with connection files
375 #-----------------------------------------------------------------------------
375 #-----------------------------------------------------------------------------
376
376
377 channel_socket_types = {
377 channel_socket_types = {
378 'hb' : zmq.REQ,
378 'hb' : zmq.REQ,
379 'shell' : zmq.DEALER,
379 'shell' : zmq.DEALER,
380 'iopub' : zmq.SUB,
380 'iopub' : zmq.SUB,
381 'stdin' : zmq.DEALER,
381 'stdin' : zmq.DEALER,
382 'control': zmq.DEALER,
382 'control': zmq.DEALER,
383 }
383 }
384
384
385 port_names = [ "%s_port" % channel for channel in ('shell', 'stdin', 'iopub', 'hb', 'control')]
385 port_names = [ "%s_port" % channel for channel in ('shell', 'stdin', 'iopub', 'hb', 'control')]
386
386
387 class ConnectionFileMixin(Configurable):
387 class ConnectionFileMixin(Configurable):
388 """Mixin for configurable classes that work with connection files"""
388 """Mixin for configurable classes that work with connection files"""
389
389
390 # The addresses for the communication channels
390 # The addresses for the communication channels
391 connection_file = Unicode('')
391 connection_file = Unicode('')
392 _connection_file_written = Bool(False)
392 _connection_file_written = Bool(False)
393
393
394 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
394 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
395
395
396 ip = Unicode(config=True,
396 ip = Unicode(config=True,
397 help="""Set the kernel\'s IP address [default localhost].
397 help="""Set the kernel\'s IP address [default localhost].
398 If the IP address is something other than localhost, then
398 If the IP address is something other than localhost, then
399 Consoles on other machines will be able to connect
399 Consoles on other machines will be able to connect
400 to the Kernel, so be careful!"""
400 to the Kernel, so be careful!"""
401 )
401 )
402
402
403 def _ip_default(self):
403 def _ip_default(self):
404 if self.transport == 'ipc':
404 if self.transport == 'ipc':
405 if self.connection_file:
405 if self.connection_file:
406 return os.path.splitext(self.connection_file)[0] + '-ipc'
406 return os.path.splitext(self.connection_file)[0] + '-ipc'
407 else:
407 else:
408 return 'kernel-ipc'
408 return 'kernel-ipc'
409 else:
409 else:
410 return localhost()
410 return localhost()
411
411
412 def _ip_changed(self, name, old, new):
412 def _ip_changed(self, name, old, new):
413 if new == '*':
413 if new == '*':
414 self.ip = '0.0.0.0'
414 self.ip = '0.0.0.0'
415
415
416 # protected traits
416 # protected traits
417
417
418 shell_port = Integer(0)
418 shell_port = Integer(0)
419 iopub_port = Integer(0)
419 iopub_port = Integer(0)
420 stdin_port = Integer(0)
420 stdin_port = Integer(0)
421 control_port = Integer(0)
421 control_port = Integer(0)
422 hb_port = Integer(0)
422 hb_port = Integer(0)
423
423
424 @property
424 @property
425 def ports(self):
425 def ports(self):
426 return [ getattr(self, name) for name in port_names ]
426 return [ getattr(self, name) for name in port_names ]
427
427
428 #--------------------------------------------------------------------------
428 #--------------------------------------------------------------------------
429 # Connection and ipc file management
429 # Connection and ipc file management
430 #--------------------------------------------------------------------------
430 #--------------------------------------------------------------------------
431
431
432 def get_connection_info(self):
432 def get_connection_info(self):
433 """return the connection info as a dict"""
433 """return the connection info as a dict"""
434 return dict(
434 return dict(
435 transport=self.transport,
435 transport=self.transport,
436 ip=self.ip,
436 ip=self.ip,
437 shell_port=self.shell_port,
437 shell_port=self.shell_port,
438 iopub_port=self.iopub_port,
438 iopub_port=self.iopub_port,
439 stdin_port=self.stdin_port,
439 stdin_port=self.stdin_port,
440 hb_port=self.hb_port,
440 hb_port=self.hb_port,
441 control_port=self.control_port,
441 control_port=self.control_port,
442 signature_scheme=self.session.signature_scheme,
442 signature_scheme=self.session.signature_scheme,
443 key=self.session.key,
443 key=self.session.key,
444 )
444 )
445
445
446 def cleanup_connection_file(self):
446 def cleanup_connection_file(self):
447 """Cleanup connection file *if we wrote it*
447 """Cleanup connection file *if we wrote it*
448
448
449 Will not raise if the connection file was already removed somehow.
449 Will not raise if the connection file was already removed somehow.
450 """
450 """
451 if self._connection_file_written:
451 if self._connection_file_written:
452 # cleanup connection files on full shutdown of kernel we started
452 # cleanup connection files on full shutdown of kernel we started
453 self._connection_file_written = False
453 self._connection_file_written = False
454 try:
454 try:
455 os.remove(self.connection_file)
455 os.remove(self.connection_file)
456 except (IOError, OSError, AttributeError):
456 except (IOError, OSError, AttributeError):
457 pass
457 pass
458
458
459 def cleanup_ipc_files(self):
459 def cleanup_ipc_files(self):
460 """Cleanup ipc files if we wrote them."""
460 """Cleanup ipc files if we wrote them."""
461 if self.transport != 'ipc':
461 if self.transport != 'ipc':
462 return
462 return
463 for port in self.ports:
463 for port in self.ports:
464 ipcfile = "%s-%i" % (self.ip, port)
464 ipcfile = "%s-%i" % (self.ip, port)
465 try:
465 try:
466 os.remove(ipcfile)
466 os.remove(ipcfile)
467 except (IOError, OSError):
467 except (IOError, OSError):
468 pass
468 pass
469
469
470 def write_connection_file(self):
470 def write_connection_file(self):
471 """Write connection info to JSON dict in self.connection_file."""
471 """Write connection info to JSON dict in self.connection_file."""
472 if self._connection_file_written:
472 if self._connection_file_written:
473 return
473 return
474
474
475 self.connection_file, cfg = write_connection_file(self.connection_file,
475 self.connection_file, cfg = write_connection_file(self.connection_file,
476 transport=self.transport, ip=self.ip, key=self.session.key,
476 transport=self.transport, ip=self.ip, key=self.session.key,
477 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
477 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
478 shell_port=self.shell_port, hb_port=self.hb_port,
478 shell_port=self.shell_port, hb_port=self.hb_port,
479 control_port=self.control_port,
479 control_port=self.control_port,
480 signature_scheme=self.session.signature_scheme,
480 signature_scheme=self.session.signature_scheme,
481 )
481 )
482 # write_connection_file also sets default ports:
482 # write_connection_file also sets default ports:
483 for name in port_names:
483 for name in port_names:
484 setattr(self, name, cfg[name])
484 setattr(self, name, cfg[name])
485
485
486 self._connection_file_written = True
486 self._connection_file_written = True
487
487
488 def load_connection_file(self):
488 def load_connection_file(self):
489 """Load connection info from JSON dict in self.connection_file."""
489 """Load connection info from JSON dict in self.connection_file."""
490 with open(self.connection_file) as f:
490 with open(self.connection_file) as f:
491 cfg = json.loads(f.read())
491 cfg = json.loads(f.read())
492
492
493 self.transport = cfg.get('transport', 'tcp')
493 self.transport = cfg.get('transport', 'tcp')
494 self.ip = cfg['ip']
494 self.ip = cfg['ip']
495 for name in port_names:
495 for name in port_names:
496 setattr(self, name, cfg[name])
496 setattr(self, name, cfg[name])
497 if 'key' in cfg:
497 if 'key' in cfg:
498 self.session.key = str_to_bytes(cfg['key'])
498 self.session.key = str_to_bytes(cfg['key'])
499 if cfg.get('signature_scheme'):
499 if cfg.get('signature_scheme'):
500 self.session.signature_scheme = cfg['signature_scheme']
500 self.session.signature_scheme = cfg['signature_scheme']
501
501
502 #--------------------------------------------------------------------------
502 #--------------------------------------------------------------------------
503 # Creating connected sockets
503 # Creating connected sockets
504 #--------------------------------------------------------------------------
504 #--------------------------------------------------------------------------
505
505
506 def _make_url(self, channel):
506 def _make_url(self, channel):
507 """Make a ZeroMQ URL for a given channel."""
507 """Make a ZeroMQ URL for a given channel."""
508 transport = self.transport
508 transport = self.transport
509 ip = self.ip
509 ip = self.ip
510 port = getattr(self, '%s_port' % channel)
510 port = getattr(self, '%s_port' % channel)
511
511
512 if transport == 'tcp':
512 if transport == 'tcp':
513 return "tcp://%s:%i" % (ip, port)
513 return "tcp://%s:%i" % (ip, port)
514 else:
514 else:
515 return "%s://%s-%s" % (transport, ip, port)
515 return "%s://%s-%s" % (transport, ip, port)
516
516
517 def _create_connected_socket(self, channel, identity=None):
517 def _create_connected_socket(self, channel, identity=None):
518 """Create a zmq Socket and connect it to the kernel."""
518 """Create a zmq Socket and connect it to the kernel."""
519 url = self._make_url(channel)
519 url = self._make_url(channel)
520 socket_type = channel_socket_types[channel]
520 socket_type = channel_socket_types[channel]
521 self.log.info("Connecting to: %s" % url)
521 self.log.info("Connecting to: %s" % url)
522 sock = self.context.socket(socket_type)
522 sock = self.context.socket(socket_type)
523 if identity:
523 if identity:
524 sock.identity = identity
524 sock.identity = identity
525 sock.connect(url)
525 sock.connect(url)
526 return sock
526 return sock
527
527
528 def connect_iopub(self, identity=None):
528 def connect_iopub(self, identity=None):
529 """return zmq Socket connected to the IOPub channel"""
529 """return zmq Socket connected to the IOPub channel"""
530 sock = self._create_connected_socket('iopub', identity=identity)
530 sock = self._create_connected_socket('iopub', identity=identity)
531 sock.setsockopt(zmq.SUBSCRIBE, b'')
531 sock.setsockopt(zmq.SUBSCRIBE, b'')
532 return sock
532 return sock
533
533
534 def connect_shell(self, identity=None):
534 def connect_shell(self, identity=None):
535 """return zmq Socket connected to the Shell channel"""
535 """return zmq Socket connected to the Shell channel"""
536 return self._create_connected_socket('shell', identity=identity)
536 return self._create_connected_socket('shell', identity=identity)
537
537
538 def connect_stdin(self, identity=None):
538 def connect_stdin(self, identity=None):
539 """return zmq Socket connected to the StdIn channel"""
539 """return zmq Socket connected to the StdIn channel"""
540 return self._create_connected_socket('stdin', identity=identity)
540 return self._create_connected_socket('stdin', identity=identity)
541
541
542 def connect_hb(self, identity=None):
542 def connect_hb(self, identity=None):
543 """return zmq Socket connected to the Heartbeat channel"""
543 """return zmq Socket connected to the Heartbeat channel"""
544 return self._create_connected_socket('hb', identity=identity)
544 return self._create_connected_socket('hb', identity=identity)
545
545
546 def connect_control(self, identity=None):
546 def connect_control(self, identity=None):
547 """return zmq Socket connected to the Heartbeat channel"""
547 """return zmq Socket connected to the Heartbeat channel"""
548 return self._create_connected_socket('control', identity=identity)
548 return self._create_connected_socket('control', identity=identity)
549
549
550
550
551 __all__ = [
551 __all__ = [
552 'write_connection_file',
552 'write_connection_file',
553 'get_connection_file',
553 'get_connection_file',
554 'find_connection_file',
554 'find_connection_file',
555 'get_connection_info',
555 'get_connection_info',
556 'connect_qtconsole',
556 'connect_qtconsole',
557 'tunnel_to_kernel',
557 'tunnel_to_kernel',
558 ]
558 ]
General Comments 0
You need to be logged in to leave comments. Login now