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