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