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