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