Show More
@@ -17,6 +17,7 b' Authors:' | |||||
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 |
|
18 | |||
19 | # stdlib imports |
|
19 | # stdlib imports | |
|
20 | import glob | |||
20 | import os |
|
21 | import os | |
21 | import signal |
|
22 | import signal | |
22 | import sys |
|
23 | import sys | |
@@ -25,6 +26,7 b' from getpass import getpass' | |||||
25 | # System library imports |
|
26 | # System library imports | |
26 | from IPython.external.qt import QtGui |
|
27 | from IPython.external.qt import QtGui | |
27 | from pygments.styles import get_all_styles |
|
28 | from pygments.styles import get_all_styles | |
|
29 | from zmq.utils import jsonapi as json | |||
28 |
|
30 | |||
29 | # external imports |
|
31 | # external imports | |
30 | from IPython.external.ssh import tunnel |
|
32 | from IPython.external.ssh import tunnel | |
@@ -39,6 +41,7 b' from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget' | |||||
39 | from IPython.frontend.qt.console import styles |
|
41 | from IPython.frontend.qt.console import styles | |
40 | from IPython.frontend.qt.kernelmanager import QtKernelManager |
|
42 | from IPython.frontend.qt.kernelmanager import QtKernelManager | |
41 | from IPython.parallel.util import select_random_ports |
|
43 | from IPython.parallel.util import select_random_ports | |
|
44 | from IPython.utils.path import filefind | |||
42 | from IPython.utils.traitlets import ( |
|
45 | from IPython.utils.traitlets import ( | |
43 | Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any |
|
46 | Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any | |
44 | ) |
|
47 | ) | |
@@ -182,8 +185,8 b' class MainWindow(QtGui.QMainWindow):' | |||||
182 |
|
185 | |||
183 | flags = dict(ipkernel_flags) |
|
186 | flags = dict(ipkernel_flags) | |
184 | qt_flags = { |
|
187 | qt_flags = { | |
185 |
'existing' : ({'IPythonQtConsoleApp' : {'existing' : |
|
188 | 'existing' : ({'IPythonQtConsoleApp' : {'existing' : 'kernel*.json'}}, | |
186 | "Connect to an existing kernel."), |
|
189 | "Connect to an existing kernel. If no argument specified, guess most recent"), | |
187 | 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}}, |
|
190 | 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}}, | |
188 | "Use a pure Python kernel instead of an IPython kernel."), |
|
191 | "Use a pure Python kernel instead of an IPython kernel."), | |
189 | 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}}, |
|
192 | 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}}, | |
@@ -213,6 +216,8 b' qt_aliases = dict(' | |||||
213 | iopub = 'IPythonQtConsoleApp.iopub_port', |
|
216 | iopub = 'IPythonQtConsoleApp.iopub_port', | |
214 | stdin = 'IPythonQtConsoleApp.stdin_port', |
|
217 | stdin = 'IPythonQtConsoleApp.stdin_port', | |
215 | ip = 'IPythonQtConsoleApp.ip', |
|
218 | ip = 'IPythonQtConsoleApp.ip', | |
|
219 | existing = 'IPythonQtConsoleApp.existing', | |||
|
220 | f = 'IPythonQtConsoleApp.connection_file', | |||
216 |
|
221 | |||
217 | style = 'IPythonWidget.syntax_style', |
|
222 | style = 'IPythonWidget.syntax_style', | |
218 | stylesheet = 'IPythonQtConsoleApp.stylesheet', |
|
223 | stylesheet = 'IPythonQtConsoleApp.stylesheet', | |
@@ -280,9 +285,18 b' class IPythonQtConsoleApp(BaseIPythonApplication):' | |||||
280 | help="set the iopub (PUB) port [default: random]") |
|
285 | help="set the iopub (PUB) port [default: random]") | |
281 | stdin_port = Int(0, config=True, |
|
286 | stdin_port = Int(0, config=True, | |
282 | help="set the stdin (XREQ) port [default: random]") |
|
287 | help="set the stdin (XREQ) port [default: random]") | |
|
288 | connection_file = Unicode('', config=True, | |||
|
289 | help="""JSON file in which to store connection info [default: kernel-<pid>.json] | |||
283 |
|
290 | |||
284 | existing = CBool(False, config=True, |
|
291 | This file will contain the IP, ports, and authentication key needed to connect | |
285 | help="Whether to connect to an already running Kernel.") |
|
292 | clients to this kernel. By default, this file will be created in the security-dir | |
|
293 | of the current profile, but can be specified by absolute path. | |||
|
294 | """) | |||
|
295 | def _connection_file_default(self): | |||
|
296 | return 'kernel-%i.json' % os.getpid() | |||
|
297 | ||||
|
298 | existing = Unicode('', config=True, | |||
|
299 | help="""Connect to an already running kernel""") | |||
286 |
|
300 | |||
287 | stylesheet = Unicode('', config=True, |
|
301 | stylesheet = Unicode('', config=True, | |
288 | help="path to a custom CSS stylesheet") |
|
302 | help="path to a custom CSS stylesheet") | |
@@ -340,6 +354,58 b' class IPythonQtConsoleApp(BaseIPythonApplication):' | |||||
340 | # alias passed with arg via space |
|
354 | # alias passed with arg via space | |
341 | swallow_next = True |
|
355 | swallow_next = True | |
342 |
|
356 | |||
|
357 | def init_connection_file(self): | |||
|
358 | sec = self.profile_dir.security_dir | |||
|
359 | if self.existing: | |||
|
360 | try: | |||
|
361 | # first, try explicit name | |||
|
362 | cf = filefind(self.existing, ['.', sec]) | |||
|
363 | except IOError: | |||
|
364 | # not found by full name | |||
|
365 | if '*' in self.existing: | |||
|
366 | # given as a glob already | |||
|
367 | pat = self.existing | |||
|
368 | else: | |||
|
369 | # accept any substring match | |||
|
370 | pat = '*%s*' | |||
|
371 | matches = glob.glob( os.path.join(sec, pat) ) | |||
|
372 | if not matches: | |||
|
373 | self.log.critical("Could not find existing kernel connection file %s", self.existing) | |||
|
374 | self.exit(1) | |||
|
375 | else: | |||
|
376 | # get most recent match: | |||
|
377 | cf = sorted(matches, key=lambda f: os.stat(f).st_atime)[-1] | |||
|
378 | self.log.info("Connecting to existing kernel: %s" % cf) | |||
|
379 | self.connection_file = cf | |||
|
380 | # should load_connection_file only be used for existing? | |||
|
381 | # as it is now, this allows reusing ports if an existing | |||
|
382 | # file is requested | |||
|
383 | self.load_connection_file() | |||
|
384 | ||||
|
385 | def load_connection_file(self): | |||
|
386 | """load ip/port/hmac config from JSON connection file""" | |||
|
387 | # this is identical to KernelApp.load_connection_file | |||
|
388 | # perhaps it can be centralized somewhere? | |||
|
389 | try: | |||
|
390 | fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir]) | |||
|
391 | except IOError: | |||
|
392 | self.log.debug("Connection File not found: %s", self.connection_file) | |||
|
393 | return | |||
|
394 | self.log.debug(u"Loading connection file %s", fname) | |||
|
395 | with open(fname) as f: | |||
|
396 | s = f.read() | |||
|
397 | cfg = json.loads(s) | |||
|
398 | if self.ip == LOCALHOST and 'ip' in cfg: | |||
|
399 | # not overridden by config or cl_args | |||
|
400 | self.ip = cfg['ip'] | |||
|
401 | for channel in ('hb', 'shell', 'iopub', 'stdin'): | |||
|
402 | name = channel + '_port' | |||
|
403 | if getattr(self, name) == 0 and name in cfg: | |||
|
404 | # not overridden by config or cl_args | |||
|
405 | setattr(self, name, cfg[name]) | |||
|
406 | if 'key' in cfg: | |||
|
407 | self.config.Session.key = cfg['key'] | |||
|
408 | ||||
343 | def init_ssh(self): |
|
409 | def init_ssh(self): | |
344 | """set up ssh tunnels, if needed.""" |
|
410 | """set up ssh tunnels, if needed.""" | |
345 | if not self.sshserver and not self.sshkey: |
|
411 | if not self.sshserver and not self.sshkey: | |
@@ -374,18 +440,30 b' class IPythonQtConsoleApp(BaseIPythonApplication):' | |||||
374 | def init_kernel_manager(self): |
|
440 | def init_kernel_manager(self): | |
375 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
|
441 | # Don't let Qt or ZMQ swallow KeyboardInterupts. | |
376 | signal.signal(signal.SIGINT, signal.SIG_DFL) |
|
442 | signal.signal(signal.SIGINT, signal.SIG_DFL) | |
|
443 | sec = self.profile_dir.security_dir | |||
|
444 | try: | |||
|
445 | cf = filefind(self.connection_file, ['.', sec]) | |||
|
446 | except IOError: | |||
|
447 | # file might not exist, use | |||
|
448 | if self.connection_file == os.path.basename(self.connection_file): | |||
|
449 | # just shortname, put it in security dir | |||
|
450 | cf = os.path.join(sec, self.connection_file) | |||
|
451 | else: | |||
|
452 | cf = self.connection_file | |||
377 |
|
453 | |||
378 | # Create a KernelManager and start a kernel. |
|
454 | # Create a KernelManager and start a kernel. | |
379 |
self.kernel_manager = QtKernelManager( |
|
455 | self.kernel_manager = QtKernelManager( | |
|
456 | ip=self.ip, | |||
380 | shell_port=self.shell_port, |
|
457 | shell_port=self.shell_port, | |
381 | sub_port=self.iopub_port, |
|
458 | sub_port=self.iopub_port, | |
382 | stdin_port=self.stdin_port, |
|
459 | stdin_port=self.stdin_port, | |
383 | hb_port=self.hb_port, |
|
460 | hb_port=self.hb_port, | |
|
461 | connection_file=cf, | |||
384 | config=self.config, |
|
462 | config=self.config, | |
385 | ) |
|
463 | ) | |
386 | # start the kernel |
|
464 | # start the kernel | |
387 | if not self.existing: |
|
465 | if not self.existing: | |
388 |
kwargs = dict( |
|
466 | kwargs = dict(ipython=not self.pure) | |
389 | kwargs['extra_arguments'] = self.kernel_argv |
|
467 | kwargs['extra_arguments'] = self.kernel_argv | |
390 | self.kernel_manager.start_kernel(**kwargs) |
|
468 | self.kernel_manager.start_kernel(**kwargs) | |
391 | self.kernel_manager.start_channels() |
|
469 | self.kernel_manager.start_channels() | |
@@ -469,6 +547,7 b' class IPythonQtConsoleApp(BaseIPythonApplication):' | |||||
469 | def initialize(self, argv=None): |
|
547 | def initialize(self, argv=None): | |
470 | super(IPythonQtConsoleApp, self).initialize(argv) |
|
548 | super(IPythonQtConsoleApp, self).initialize(argv) | |
471 | self.init_ssh() |
|
549 | self.init_ssh() | |
|
550 | self.init_connection_file() | |||
472 | self.init_kernel_manager() |
|
551 | self.init_kernel_manager() | |
473 | self.init_qt_elements() |
|
552 | self.init_qt_elements() | |
474 | self.init_colors() |
|
553 | self.init_colors() |
@@ -8,20 +8,26 b' import os' | |||||
8 | import socket |
|
8 | import socket | |
9 | from subprocess import Popen, PIPE |
|
9 | from subprocess import Popen, PIPE | |
10 | import sys |
|
10 | import sys | |
|
11 | import tempfile | |||
11 |
|
12 | |||
12 | # Local imports. |
|
13 | # System library imports | |
13 | from parentpoller import ParentPollerWindows |
|
14 | from zmq.utils import jsonapi as json | |
14 |
|
15 | |||
|
16 | # IPython imports | |||
|
17 | from IPython.utils.localinterfaces import LOCALHOST | |||
15 |
|
18 | |||
16 | def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0, |
|
19 | # Local imports. | |
17 | ip=None, stdin=None, stdout=None, stderr=None, |
|
20 | from parentpoller import ParentPollerWindows | |
18 | executable=None, independent=False, extra_arguments=[]): |
|
|||
19 | """ Launches a localhost kernel, binding to the specified ports. |
|
|||
20 |
|
21 | |||
|
22 | def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0, | |||
|
23 | ip=LOCALHOST, key=''): | |||
|
24 | """Generates a JSON config file, including the selection of random ports. | |||
|
25 | ||||
21 | Parameters |
|
26 | Parameters | |
22 | ---------- |
|
27 | ---------- | |
23 | code : str, |
|
28 | ||
24 | A string of Python code that imports and executes a kernel entry point. |
|
29 | fname : unicode | |
|
30 | The path to the file to write | |||
25 |
|
31 | |||
26 | shell_port : int, optional |
|
32 | shell_port : int, optional | |
27 | The port to use for XREP channel. |
|
33 | The port to use for XREP channel. | |
@@ -38,27 +44,14 b' def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0' | |||||
38 | ip : str, optional |
|
44 | ip : str, optional | |
39 | The ip address the kernel will bind to. |
|
45 | The ip address the kernel will bind to. | |
40 |
|
46 | |||
41 | stdin, stdout, stderr : optional (default None) |
|
47 | key : str, optional | |
42 | Standards streams, as defined in subprocess.Popen. |
|
48 | The Session key used for HMAC authentication. | |
43 |
|
49 | |||
44 | executable : str, optional (default sys.executable) |
|
|||
45 | The Python executable to use for the kernel process. |
|
|||
46 |
|
||||
47 | independent : bool, optional (default False) |
|
|||
48 | If set, the kernel process is guaranteed to survive if this process |
|
|||
49 | dies. If not set, an effort is made to ensure that the kernel is killed |
|
|||
50 | when this process dies. Note that in this case it is still good practice |
|
|||
51 | to kill kernels manually before exiting. |
|
|||
52 |
|
||||
53 | extra_arguments = list, optional |
|
|||
54 | A list of extra arguments to pass when executing the launch code. |
|
|||
55 |
|
||||
56 | Returns |
|
|||
57 | ------- |
|
|||
58 | A tuple of form: |
|
|||
59 | (kernel_process, shell_port, iopub_port, stdin_port, hb_port) |
|
|||
60 | where kernel_process is a Popen object and the ports are integers. |
|
|||
61 | """ |
|
50 | """ | |
|
51 | # default to temporary connector file | |||
|
52 | if not fname: | |||
|
53 | fname = tempfile.mktemp('.json') | |||
|
54 | ||||
62 | # Find open ports as necessary. |
|
55 | # Find open ports as necessary. | |
63 | ports = [] |
|
56 | ports = [] | |
64 | ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \ |
|
57 | ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \ | |
@@ -79,15 +72,62 b' def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0' | |||||
79 | stdin_port = ports.pop(0) |
|
72 | stdin_port = ports.pop(0) | |
80 | if hb_port <= 0: |
|
73 | if hb_port <= 0: | |
81 | hb_port = ports.pop(0) |
|
74 | hb_port = ports.pop(0) | |
|
75 | ||||
|
76 | cfg = dict( shell_port=shell_port, | |||
|
77 | iopub_port=iopub_port, | |||
|
78 | stdin_port=stdin_port, | |||
|
79 | hb_port=hb_port, | |||
|
80 | ) | |||
|
81 | cfg['ip'] = ip | |||
|
82 | cfg['key'] = key | |||
|
83 | ||||
|
84 | with open(fname, 'wb') as f: | |||
|
85 | f.write(json.dumps(cfg, indent=2)) | |||
|
86 | ||||
|
87 | return fname, cfg | |||
|
88 | ||||
|
89 | ||||
|
90 | def base_launch_kernel(code, fname, stdin=None, stdout=None, stderr=None, | |||
|
91 | executable=None, independent=False, extra_arguments=[]): | |||
|
92 | """ Launches a localhost kernel, binding to the specified ports. | |||
|
93 | ||||
|
94 | Parameters | |||
|
95 | ---------- | |||
|
96 | code : str, | |||
|
97 | A string of Python code that imports and executes a kernel entry point. | |||
82 |
|
|
98 | ||
|
99 | stdin, stdout, stderr : optional (default None) | |||
|
100 | Standards streams, as defined in subprocess.Popen. | |||
|
101 | ||||
|
102 | fname : unicode, optional | |||
|
103 | The JSON connector file, containing ip/port/hmac key information. | |||
|
104 | ||||
|
105 | key : str, optional | |||
|
106 | The Session key used for HMAC authentication. | |||
|
107 | ||||
|
108 | executable : str, optional (default sys.executable) | |||
|
109 | The Python executable to use for the kernel process. | |||
|
110 | ||||
|
111 | independent : bool, optional (default False) | |||
|
112 | If set, the kernel process is guaranteed to survive if this process | |||
|
113 | dies. If not set, an effort is made to ensure that the kernel is killed | |||
|
114 | when this process dies. Note that in this case it is still good practice | |||
|
115 | to kill kernels manually before exiting. | |||
|
116 | ||||
|
117 | extra_arguments = list, optional | |||
|
118 | A list of extra arguments to pass when executing the launch code. | |||
|
119 | ||||
|
120 | Returns | |||
|
121 | ------- | |||
|
122 | A tuple of form: | |||
|
123 | (kernel_process, shell_port, iopub_port, stdin_port, hb_port) | |||
|
124 | where kernel_process is a Popen object and the ports are integers. | |||
|
125 | """ | |||
|
126 | ||||
83 | # Build the kernel launch command. |
|
127 | # Build the kernel launch command. | |
84 | if executable is None: |
|
128 | if executable is None: | |
85 | executable = sys.executable |
|
129 | executable = sys.executable | |
86 |
arguments = [ executable, '-c', code, '- |
|
130 | arguments = [ executable, '-c', code, '-f', fname ] | |
87 | '--iopub=%i'%iopub_port, '--stdin=%i'%stdin_port, |
|
|||
88 | '--hb=%i'%hb_port ] |
|
|||
89 | if ip is not None: |
|
|||
90 | arguments.append('--ip=%s'%ip) |
|
|||
91 | arguments.extend(extra_arguments) |
|
131 | arguments.extend(extra_arguments) | |
92 |
|
132 | |||
93 | # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr |
|
133 | # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr | |
@@ -164,4 +204,4 b' def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0' | |||||
164 | if stderr is None: |
|
204 | if stderr is None: | |
165 | proc.stderr.close() |
|
205 | proc.stderr.close() | |
166 |
|
206 | |||
167 | return proc, shell_port, iopub_port, stdin_port, hb_port |
|
207 | return proc |
@@ -21,6 +21,7 b' import sys' | |||||
21 |
|
21 | |||
22 | # System library imports. |
|
22 | # System library imports. | |
23 | import zmq |
|
23 | import zmq | |
|
24 | from zmq.utils import jsonapi as json | |||
24 |
|
25 | |||
25 | # IPython imports. |
|
26 | # IPython imports. | |
26 | from IPython.core.ultratb import FormattedTB |
|
27 | from IPython.core.ultratb import FormattedTB | |
@@ -29,10 +30,12 b' from IPython.core.application import (' | |||||
29 | ) |
|
30 | ) | |
30 | from IPython.utils import io |
|
31 | from IPython.utils import io | |
31 | from IPython.utils.localinterfaces import LOCALHOST |
|
32 | from IPython.utils.localinterfaces import LOCALHOST | |
|
33 | from IPython.utils.path import filefind | |||
32 | from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Int, Bool, |
|
34 | from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Int, Bool, | |
33 | DottedObjectName) |
|
35 | DottedObjectName) | |
34 | from IPython.utils.importstring import import_item |
|
36 | from IPython.utils.importstring import import_item | |
35 | # local imports |
|
37 | # local imports | |
|
38 | from IPython.zmq.entry_point import write_connection_file | |||
36 | from IPython.zmq.heartbeat import Heartbeat |
|
39 | from IPython.zmq.heartbeat import Heartbeat | |
37 | from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows |
|
40 | from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows | |
38 | from IPython.zmq.session import Session |
|
41 | from IPython.zmq.session import Session | |
@@ -49,6 +52,7 b' kernel_aliases.update({' | |||||
49 | 'shell' : 'KernelApp.shell_port', |
|
52 | 'shell' : 'KernelApp.shell_port', | |
50 | 'iopub' : 'KernelApp.iopub_port', |
|
53 | 'iopub' : 'KernelApp.iopub_port', | |
51 | 'stdin' : 'KernelApp.stdin_port', |
|
54 | 'stdin' : 'KernelApp.stdin_port', | |
|
55 | 'f' : 'KernelApp.connection_file', | |||
52 | 'parent': 'KernelApp.parent', |
|
56 | 'parent': 'KernelApp.parent', | |
53 | }) |
|
57 | }) | |
54 | if sys.platform.startswith('win'): |
|
58 | if sys.platform.startswith('win'): | |
@@ -99,6 +103,13 b' class KernelApp(BaseIPythonApplication):' | |||||
99 | shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]") |
|
103 | shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]") | |
100 | iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]") |
|
104 | iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]") | |
101 | stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]") |
|
105 | stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]") | |
|
106 | connection_file = Unicode('', config=True, | |||
|
107 | help="""JSON file in which to store connection info [default: kernel-<pid>.json] | |||
|
108 | ||||
|
109 | This file will contain the IP, ports, and authentication key needed to connect | |||
|
110 | clients to this kernel. By default, this file will be created in the security-dir | |||
|
111 | of the current profile, but can be specified by absolute path. | |||
|
112 | """) | |||
102 |
|
113 | |||
103 | # streams, etc. |
|
114 | # streams, etc. | |
104 | no_stdout = Bool(False, config=True, help="redirect stdout to the null device") |
|
115 | no_stdout = Bool(False, config=True, help="redirect stdout to the null device") | |
@@ -138,6 +149,44 b' class KernelApp(BaseIPythonApplication):' | |||||
138 | s.bind(iface + ':%i'%port) |
|
149 | s.bind(iface + ':%i'%port) | |
139 | return port |
|
150 | return port | |
140 |
|
151 | |||
|
152 | def load_connection_file(self): | |||
|
153 | """load ip/port/hmac config from JSON connection file""" | |||
|
154 | try: | |||
|
155 | fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir]) | |||
|
156 | except IOError: | |||
|
157 | self.log.debug("Connection file not found: %s", self.connection_file) | |||
|
158 | return | |||
|
159 | self.log.debug(u"Loading connection file %s", fname) | |||
|
160 | with open(fname) as f: | |||
|
161 | s = f.read() | |||
|
162 | cfg = json.loads(s) | |||
|
163 | if self.ip == LOCALHOST and 'ip' in cfg: | |||
|
164 | # not overridden by config or cl_args | |||
|
165 | self.ip = cfg['ip'] | |||
|
166 | for channel in ('hb', 'shell', 'iopub', 'stdin'): | |||
|
167 | name = channel + '_port' | |||
|
168 | if getattr(self, name) == 0 and name in cfg: | |||
|
169 | # not overridden by config or cl_args | |||
|
170 | setattr(self, name, cfg[name]) | |||
|
171 | if 'key' in cfg: | |||
|
172 | self.config.Session.key = cfg['key'] | |||
|
173 | ||||
|
174 | def write_connection_file(self): | |||
|
175 | """write connection info to JSON file""" | |||
|
176 | if os.path.basename(self.connection_file) == self.connection_file: | |||
|
177 | cf = os.path.join(self.profile_dir.security_dir, self.connection_file) | |||
|
178 | else: | |||
|
179 | cf = self.connection_file | |||
|
180 | write_connection_file(cf, ip=self.ip, key=self.session.key, | |||
|
181 | shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port, | |||
|
182 | iopub_port=self.iopub_port) | |||
|
183 | ||||
|
184 | def init_connection_file(self): | |||
|
185 | if not self.connection_file: | |||
|
186 | self.connection_file = "kernel-%s.json"%os.getpid() | |||
|
187 | ||||
|
188 | self.load_connection_file() | |||
|
189 | ||||
141 | def init_sockets(self): |
|
190 | def init_sockets(self): | |
142 | # Create a context, a session, and the kernel sockets. |
|
191 | # Create a context, a session, and the kernel sockets. | |
143 | self.log.info("Starting the kernel at pid: %i", os.getpid()) |
|
192 | self.log.info("Starting the kernel at pid: %i", os.getpid()) | |
@@ -161,12 +210,17 b' class KernelApp(BaseIPythonApplication):' | |||||
161 | self.hb_port = self.heartbeat.port |
|
210 | self.hb_port = self.heartbeat.port | |
162 | self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port) |
|
211 | self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port) | |
163 |
|
212 | |||
164 |
# Helper to make it easier to connect to an existing kernel |
|
213 | # Helper to make it easier to connect to an existing kernel. | |
165 | # single-port connection negotiation fully implemented. |
|
|||
166 | # set log-level to critical, to make sure it is output |
|
214 | # set log-level to critical, to make sure it is output | |
167 | self.log.critical("To connect another client to this kernel, use:") |
|
215 | self.log.critical("To connect another client to this kernel, use:") | |
168 | self.log.critical("--existing --shell={0} --iopub={1} --stdin={2} --hb={3}".format( |
|
216 | if os.path.dirname(self.connection_file) == self.profile_dir.security_dir: | |
169 | self.shell_port, self.iopub_port, self.stdin_port, self.hb_port)) |
|
217 | # use shortname | |
|
218 | tail = os.path.basename(self.connection_file) | |||
|
219 | if self.profile != 'default': | |||
|
220 | tail += " --profile %s" % self.profile_name | |||
|
221 | else: | |||
|
222 | tail = self.connection_file | |||
|
223 | self.log.critical("--existing %s", tail) | |||
170 |
|
224 | |||
171 |
|
225 | |||
172 | self.ports = dict(shell=self.shell_port, iopub=self.iopub_port, |
|
226 | self.ports = dict(shell=self.shell_port, iopub=self.iopub_port, | |
@@ -209,9 +263,12 b' class KernelApp(BaseIPythonApplication):' | |||||
209 | def initialize(self, argv=None): |
|
263 | def initialize(self, argv=None): | |
210 | super(KernelApp, self).initialize(argv) |
|
264 | super(KernelApp, self).initialize(argv) | |
211 | self.init_blackhole() |
|
265 | self.init_blackhole() | |
|
266 | self.init_connection_file() | |||
212 | self.init_session() |
|
267 | self.init_session() | |
213 | self.init_poller() |
|
268 | self.init_poller() | |
214 | self.init_sockets() |
|
269 | self.init_sockets() | |
|
270 | # writing connection file must be *after* init_sockets | |||
|
271 | self.write_connection_file() | |||
215 | self.init_io() |
|
272 | self.init_io() | |
216 | self.init_kernel() |
|
273 | self.init_kernel() | |
217 |
|
274 |
@@ -19,6 +19,7 b' TODO' | |||||
19 | import errno |
|
19 | import errno | |
20 | from Queue import Queue, Empty |
|
20 | from Queue import Queue, Empty | |
21 | from subprocess import Popen |
|
21 | from subprocess import Popen | |
|
22 | import os | |||
22 | import signal |
|
23 | import signal | |
23 | import sys |
|
24 | import sys | |
24 | from threading import Thread |
|
25 | from threading import Thread | |
@@ -32,7 +33,10 b' from zmq.eventloop import ioloop' | |||||
32 | # Local imports. |
|
33 | # Local imports. | |
33 | from IPython.config.loader import Config |
|
34 | from IPython.config.loader import Config | |
34 | from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS |
|
35 | from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS | |
35 |
from IPython.utils.traitlets import |
|
36 | from IPython.utils.traitlets import ( | |
|
37 | HasTraits, Any, Instance, Type, Unicode, Int, Bool | |||
|
38 | ) | |||
|
39 | from IPython.zmq.entry_point import write_connection_file | |||
36 | from session import Session |
|
40 | from session import Session | |
37 |
|
41 | |||
38 | #----------------------------------------------------------------------------- |
|
42 | #----------------------------------------------------------------------------- | |
@@ -702,6 +706,7 b' class KernelManager(HasTraits):' | |||||
702 | kernel = Instance(Popen) |
|
706 | kernel = Instance(Popen) | |
703 |
|
707 | |||
704 | # The addresses for the communication channels. |
|
708 | # The addresses for the communication channels. | |
|
709 | connection_file = Unicode('') | |||
705 | ip = Unicode(LOCALHOST) |
|
710 | ip = Unicode(LOCALHOST) | |
706 | shell_port = Int(0) |
|
711 | shell_port = Int(0) | |
707 | sub_port = Int(0) |
|
712 | sub_port = Int(0) | |
@@ -720,13 +725,22 b' class KernelManager(HasTraits):' | |||||
720 | _sub_channel = Any |
|
725 | _sub_channel = Any | |
721 | _stdin_channel = Any |
|
726 | _stdin_channel = Any | |
722 | _hb_channel = Any |
|
727 | _hb_channel = Any | |
|
728 | _connection_file_written=Bool(False) | |||
723 |
|
729 | |||
724 | def __init__(self, **kwargs): |
|
730 | def __init__(self, **kwargs): | |
725 | super(KernelManager, self).__init__(**kwargs) |
|
731 | super(KernelManager, self).__init__(**kwargs) | |
726 | if self.session is None: |
|
732 | if self.session is None: | |
727 | self.session = Session(config=self.config) |
|
733 | self.session = Session(config=self.config) | |
728 | # Uncomment this to try closing the context. |
|
734 | ||
729 | # atexit.register(self.context.term) |
|
735 | def __del__(self): | |
|
736 | if self._connection_file_written: | |||
|
737 | # cleanup connection files on full shutdown of kernel we started | |||
|
738 | self._connection_file_written = False | |||
|
739 | try: | |||
|
740 | os.remove(self.connection_file) | |||
|
741 | except IOError: | |||
|
742 | pass | |||
|
743 | ||||
730 |
|
744 | |||
731 | #-------------------------------------------------------------------------- |
|
745 | #-------------------------------------------------------------------------- | |
732 | # Channel management methods: |
|
746 | # Channel management methods: | |
@@ -773,7 +787,22 b' class KernelManager(HasTraits):' | |||||
773 | #-------------------------------------------------------------------------- |
|
787 | #-------------------------------------------------------------------------- | |
774 | # Kernel process management methods: |
|
788 | # Kernel process management methods: | |
775 | #-------------------------------------------------------------------------- |
|
789 | #-------------------------------------------------------------------------- | |
776 |
|
790 | |||
|
791 | def write_connection_file(self): | |||
|
792 | if self._connection_file_written: | |||
|
793 | return | |||
|
794 | self.connection_file,cfg = write_connection_file(self.connection_file, | |||
|
795 | ip=self.ip, key=self.session.key, | |||
|
796 | stdin_port=self.stdin_port, iopub_port=self.sub_port, | |||
|
797 | shell_port=self.shell_port, hb_port=self.hb_port) | |||
|
798 | # write_connection_file also sets default ports: | |||
|
799 | self.shell_port = cfg['shell_port'] | |||
|
800 | self.stdin_port = cfg['stdin_port'] | |||
|
801 | self.sub_port = cfg['iopub_port'] | |||
|
802 | self.hb_port = cfg['hb_port'] | |||
|
803 | ||||
|
804 | self._connection_file_written = True | |||
|
805 | ||||
777 | def start_kernel(self, **kw): |
|
806 | def start_kernel(self, **kw): | |
778 | """Starts a kernel process and configures the manager to use it. |
|
807 | """Starts a kernel process and configures the manager to use it. | |
779 |
|
808 | |||
@@ -799,6 +828,9 b' class KernelManager(HasTraits):' | |||||
799 | "configured properly. " |
|
828 | "configured properly. " | |
800 | "Currently valid addresses are: %s"%LOCAL_IPS |
|
829 | "Currently valid addresses are: %s"%LOCAL_IPS | |
801 | ) |
|
830 | ) | |
|
831 | ||||
|
832 | # write connection file / get default ports | |||
|
833 | self.write_connection_file() | |||
802 |
|
834 | |||
803 | self._launch_args = kw.copy() |
|
835 | self._launch_args = kw.copy() | |
804 | launch_kernel = kw.pop('launcher', None) |
|
836 | launch_kernel = kw.pop('launcher', None) | |
@@ -807,13 +839,7 b' class KernelManager(HasTraits):' | |||||
807 | from ipkernel import launch_kernel |
|
839 | from ipkernel import launch_kernel | |
808 | else: |
|
840 | else: | |
809 | from pykernel import launch_kernel |
|
841 | from pykernel import launch_kernel | |
810 | self.kernel, shell, sub, stdin, hb = launch_kernel( |
|
842 | self.kernel = launch_kernel(fname=self.connection_file, **kw) | |
811 | shell_port=self.shell_port, iopub_port=self.sub_port, |
|
|||
812 | stdin_port=self.stdin_port, hb_port=self.hb_port, **kw) |
|
|||
813 | self.shell_port = shell |
|
|||
814 | self.sub_port = sub |
|
|||
815 | self.stdin_port = stdin |
|
|||
816 | self.hb_port = hb |
|
|||
817 |
|
843 | |||
818 | def shutdown_kernel(self, restart=False): |
|
844 | def shutdown_kernel(self, restart=False): | |
819 | """ Attempts to the stop the kernel process cleanly. If the kernel |
|
845 | """ Attempts to the stop the kernel process cleanly. If the kernel | |
@@ -842,6 +868,14 b' class KernelManager(HasTraits):' | |||||
842 | if self.has_kernel: |
|
868 | if self.has_kernel: | |
843 | self.kill_kernel() |
|
869 | self.kill_kernel() | |
844 |
|
870 | |||
|
871 | if not restart and self._connection_file_written: | |||
|
872 | # cleanup connection files on full shutdown of kernel we started | |||
|
873 | self._connection_file_written = False | |||
|
874 | try: | |||
|
875 | os.remove(self.connection_file) | |||
|
876 | except IOError: | |||
|
877 | pass | |||
|
878 | ||||
845 | def restart_kernel(self, now=False, **kw): |
|
879 | def restart_kernel(self, now=False, **kw): | |
846 | """Restarts a kernel with the arguments that were used to launch it. |
|
880 | """Restarts a kernel with the arguments that were used to launch it. | |
847 |
|
881 |
General Comments 0
You need to be logged in to leave comments.
Login now