##// END OF EJS Templates
define InlineBackend configurable in its own file...
MinRK -
Show More
@@ -0,0 +1,85 b''
1 """Configurable for configuring the IPython inline backend
2
3 This module does not import anything from matplotlib.
4 """
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2011 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 from IPython.config.configurable import SingletonConfigurable
17 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool
18 from IPython.utils.warn import warn
19
20 #-----------------------------------------------------------------------------
21 # Configurable for inline backend options
22 #-----------------------------------------------------------------------------
23
24 # inherit from InlineBackendConfig for deprecation purposes
25 class InlineBackendConfig(SingletonConfigurable):
26 pass
27
28 class InlineBackend(InlineBackendConfig):
29 """An object to store configuration of the inline backend."""
30
31 def _config_changed(self, name, old, new):
32 # warn on change of renamed config section
33 if new.InlineBackendConfig != old.InlineBackendConfig:
34 warn("InlineBackendConfig has been renamed to InlineBackend")
35 super(InlineBackend, self)._config_changed(name, old, new)
36
37 # The typical default figure size is too large for inline use,
38 # so we shrink the figure size to 6x4, and tweak fonts to
39 # make that fit.
40 rc = Dict({'figure.figsize': (6.0,4.0),
41 # play nicely with white background in the Qt and notebook frontend
42 'figure.facecolor': 'white',
43 'figure.edgecolor': 'white',
44 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
45 'font.size': 10,
46 # 72 dpi matches SVG/qtconsole
47 # this only affects PNG export, as SVG has no dpi setting
48 'savefig.dpi': 72,
49 # 10pt still needs a little more room on the xlabel:
50 'figure.subplot.bottom' : .125
51 }, config=True,
52 help="""Subset of matplotlib rcParams that should be different for the
53 inline backend."""
54 )
55
56 figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True,
57 help="The image format for figures with the inline backend.")
58
59 def _figure_format_changed(self, name, old, new):
60 from IPython.core.pylabtools import select_figure_format
61 if self.shell is None:
62 return
63 else:
64 select_figure_format(self.shell, new)
65
66 close_figures = Bool(True, config=True,
67 help="""Close all figures at the end of each cell.
68
69 When True, ensures that each cell starts with no active figures, but it
70 also means that one must keep track of references in order to edit or
71 redraw figures in subsequent cells. This mode is ideal for the notebook,
72 where residual plots from other cells might be surprising.
73
74 When False, one must call figure() to create new figures. This means
75 that gcf() and getfigs() can reference figures created in other cells,
76 and the active figure can continue to be edited with pylab/pyplot
77 methods that reference the current active figure. This mode facilitates
78 iterative editing of figures, and behaves most consistently with
79 other matplotlib backends, but figure barriers between cells must
80 be explicit.
81 """)
82
83 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
84
85
@@ -1,393 +1,387 b''
1 """ A minimal application base mixin for all ZMQ based IPython frontends.
1 """ A minimal application base mixin for all ZMQ based IPython frontends.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations. This is a
4 input, there is no real readline support, among other limitations. This is a
5 refactoring of what used to be the IPython/qt/console/qtconsoleapp.py
5 refactoring of what used to be the IPython/qt/console/qtconsoleapp.py
6
6
7 Authors:
7 Authors:
8
8
9 * Evan Patterson
9 * Evan Patterson
10 * Min RK
10 * Min RK
11 * Erik Tollerud
11 * Erik Tollerud
12 * Fernando Perez
12 * Fernando Perez
13 * Bussonnier Matthias
13 * Bussonnier Matthias
14 * Thomas Kluyver
14 * Thomas Kluyver
15 * Paul Ivanov
15 * Paul Ivanov
16
16
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 # stdlib imports
23 # stdlib imports
24 import atexit
24 import atexit
25 import json
25 import json
26 import os
26 import os
27 import signal
27 import signal
28 import sys
28 import sys
29 import uuid
29 import uuid
30
30
31
31
32 # Local imports
32 # Local imports
33 from IPython.config.application import boolean_flag
33 from IPython.config.application import boolean_flag
34 from IPython.core.profiledir import ProfileDir
34 from IPython.core.profiledir import ProfileDir
35 from IPython.kernel.blocking import BlockingKernelClient
35 from IPython.kernel.blocking import BlockingKernelClient
36 from IPython.kernel import KernelManager
36 from IPython.kernel import KernelManager
37 from IPython.kernel import tunnel_to_kernel, find_connection_file, swallow_argv
37 from IPython.kernel import tunnel_to_kernel, find_connection_file, swallow_argv
38 from IPython.utils.path import filefind
38 from IPython.utils.path import filefind
39 from IPython.utils.py3compat import str_to_bytes
39 from IPython.utils.py3compat import str_to_bytes
40 from IPython.utils.traitlets import (
40 from IPython.utils.traitlets import (
41 Dict, List, Unicode, CUnicode, Int, CBool, Any
41 Dict, List, Unicode, CUnicode, Int, CBool, Any
42 )
42 )
43 from IPython.kernel.zmq.kernelapp import (
43 from IPython.kernel.zmq.kernelapp import (
44 kernel_flags,
44 kernel_flags,
45 kernel_aliases,
45 kernel_aliases,
46 IPKernelApp
46 IPKernelApp
47 )
47 )
48 from IPython.kernel.zmq.pylab.config import InlineBackend
48 from IPython.kernel.zmq.session import Session, default_secure
49 from IPython.kernel.zmq.session import Session, default_secure
49 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
50 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
50 from IPython.kernel.connect import ConnectionFileMixin
51 from IPython.kernel.connect import ConnectionFileMixin
51
52
52 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
53 # Network Constants
54 # Network Constants
54 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
55
56
56 from IPython.utils.localinterfaces import localhost
57 from IPython.utils.localinterfaces import localhost
57
58
58 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
59 # Globals
60 # Globals
60 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
61
62
62
63
63 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
64 # Aliases and Flags
65 # Aliases and Flags
65 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
66
67
67 flags = dict(kernel_flags)
68 flags = dict(kernel_flags)
68
69
69 # the flags that are specific to the frontend
70 # the flags that are specific to the frontend
70 # these must be scrubbed before being passed to the kernel,
71 # these must be scrubbed before being passed to the kernel,
71 # or it will raise an error on unrecognized flags
72 # or it will raise an error on unrecognized flags
72 app_flags = {
73 app_flags = {
73 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
74 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
74 "Connect to an existing kernel. If no argument specified, guess most recent"),
75 "Connect to an existing kernel. If no argument specified, guess most recent"),
75 }
76 }
76 app_flags.update(boolean_flag(
77 app_flags.update(boolean_flag(
77 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
78 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
78 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
79 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
79 to force a direct exit without any confirmation.
80 to force a direct exit without any confirmation.
80 """,
81 """,
81 """Don't prompt the user when exiting. This will terminate the kernel
82 """Don't prompt the user when exiting. This will terminate the kernel
82 if it is owned by the frontend, and leave it alive if it is external.
83 if it is owned by the frontend, and leave it alive if it is external.
83 """
84 """
84 ))
85 ))
85 flags.update(app_flags)
86 flags.update(app_flags)
86
87
87 aliases = dict(kernel_aliases)
88 aliases = dict(kernel_aliases)
88
89
89 # also scrub aliases from the frontend
90 # also scrub aliases from the frontend
90 app_aliases = dict(
91 app_aliases = dict(
91 ip = 'IPythonConsoleApp.ip',
92 ip = 'IPythonConsoleApp.ip',
92 transport = 'IPythonConsoleApp.transport',
93 transport = 'IPythonConsoleApp.transport',
93 hb = 'IPythonConsoleApp.hb_port',
94 hb = 'IPythonConsoleApp.hb_port',
94 shell = 'IPythonConsoleApp.shell_port',
95 shell = 'IPythonConsoleApp.shell_port',
95 iopub = 'IPythonConsoleApp.iopub_port',
96 iopub = 'IPythonConsoleApp.iopub_port',
96 stdin = 'IPythonConsoleApp.stdin_port',
97 stdin = 'IPythonConsoleApp.stdin_port',
97 existing = 'IPythonConsoleApp.existing',
98 existing = 'IPythonConsoleApp.existing',
98 f = 'IPythonConsoleApp.connection_file',
99 f = 'IPythonConsoleApp.connection_file',
99
100
100
101
101 ssh = 'IPythonConsoleApp.sshserver',
102 ssh = 'IPythonConsoleApp.sshserver',
102 )
103 )
103 aliases.update(app_aliases)
104 aliases.update(app_aliases)
104
105
105 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
106 # Classes
107 # Classes
107 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
108
109
109 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
110 # IPythonConsole
111 # IPythonConsole
111 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
112
113
113 classes = [IPKernelApp, ZMQInteractiveShell, KernelManager, ProfileDir, Session]
114 classes = [IPKernelApp, ZMQInteractiveShell, KernelManager, ProfileDir, Session, InlineBackend]
114
115 try:
116 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
117 except ImportError:
118 pass
119 else:
120 classes.append(InlineBackend)
121
115
122 class IPythonConsoleApp(ConnectionFileMixin):
116 class IPythonConsoleApp(ConnectionFileMixin):
123 name = 'ipython-console-mixin'
117 name = 'ipython-console-mixin'
124
118
125 description = """
119 description = """
126 The IPython Mixin Console.
120 The IPython Mixin Console.
127
121
128 This class contains the common portions of console client (QtConsole,
122 This class contains the common portions of console client (QtConsole,
129 ZMQ-based terminal console, etc). It is not a full console, in that
123 ZMQ-based terminal console, etc). It is not a full console, in that
130 launched terminal subprocesses will not be able to accept input.
124 launched terminal subprocesses will not be able to accept input.
131
125
132 The Console using this mixing supports various extra features beyond
126 The Console using this mixing supports various extra features beyond
133 the single-process Terminal IPython shell, such as connecting to
127 the single-process Terminal IPython shell, such as connecting to
134 existing kernel, via:
128 existing kernel, via:
135
129
136 ipython <appname> --existing
130 ipython <appname> --existing
137
131
138 as well as tunnel via SSH
132 as well as tunnel via SSH
139
133
140 """
134 """
141
135
142 classes = classes
136 classes = classes
143 flags = Dict(flags)
137 flags = Dict(flags)
144 aliases = Dict(aliases)
138 aliases = Dict(aliases)
145 kernel_manager_class = KernelManager
139 kernel_manager_class = KernelManager
146 kernel_client_class = BlockingKernelClient
140 kernel_client_class = BlockingKernelClient
147
141
148 kernel_argv = List(Unicode)
142 kernel_argv = List(Unicode)
149 # frontend flags&aliases to be stripped when building kernel_argv
143 # frontend flags&aliases to be stripped when building kernel_argv
150 frontend_flags = Any(app_flags)
144 frontend_flags = Any(app_flags)
151 frontend_aliases = Any(app_aliases)
145 frontend_aliases = Any(app_aliases)
152
146
153 # create requested profiles by default, if they don't exist:
147 # create requested profiles by default, if they don't exist:
154 auto_create = CBool(True)
148 auto_create = CBool(True)
155 # connection info:
149 # connection info:
156
150
157 sshserver = Unicode('', config=True,
151 sshserver = Unicode('', config=True,
158 help="""The SSH server to use to connect to the kernel.""")
152 help="""The SSH server to use to connect to the kernel.""")
159 sshkey = Unicode('', config=True,
153 sshkey = Unicode('', config=True,
160 help="""Path to the ssh key to use for logging in to the ssh server.""")
154 help="""Path to the ssh key to use for logging in to the ssh server.""")
161
155
162 hb_port = Int(0, config=True,
156 hb_port = Int(0, config=True,
163 help="set the heartbeat port [default: random]")
157 help="set the heartbeat port [default: random]")
164 shell_port = Int(0, config=True,
158 shell_port = Int(0, config=True,
165 help="set the shell (ROUTER) port [default: random]")
159 help="set the shell (ROUTER) port [default: random]")
166 iopub_port = Int(0, config=True,
160 iopub_port = Int(0, config=True,
167 help="set the iopub (PUB) port [default: random]")
161 help="set the iopub (PUB) port [default: random]")
168 stdin_port = Int(0, config=True,
162 stdin_port = Int(0, config=True,
169 help="set the stdin (DEALER) port [default: random]")
163 help="set the stdin (DEALER) port [default: random]")
170 connection_file = Unicode('', config=True,
164 connection_file = Unicode('', config=True,
171 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
165 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
172
166
173 This file will contain the IP, ports, and authentication key needed to connect
167 This file will contain the IP, ports, and authentication key needed to connect
174 clients to this kernel. By default, this file will be created in the security-dir
168 clients to this kernel. By default, this file will be created in the security-dir
175 of the current profile, but can be specified by absolute path.
169 of the current profile, but can be specified by absolute path.
176 """)
170 """)
177 def _connection_file_default(self):
171 def _connection_file_default(self):
178 return 'kernel-%i.json' % os.getpid()
172 return 'kernel-%i.json' % os.getpid()
179
173
180 existing = CUnicode('', config=True,
174 existing = CUnicode('', config=True,
181 help="""Connect to an already running kernel""")
175 help="""Connect to an already running kernel""")
182
176
183 confirm_exit = CBool(True, config=True,
177 confirm_exit = CBool(True, config=True,
184 help="""
178 help="""
185 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
179 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
186 to force a direct exit without any confirmation.""",
180 to force a direct exit without any confirmation.""",
187 )
181 )
188
182
189
183
190 def build_kernel_argv(self, argv=None):
184 def build_kernel_argv(self, argv=None):
191 """build argv to be passed to kernel subprocess"""
185 """build argv to be passed to kernel subprocess"""
192 if argv is None:
186 if argv is None:
193 argv = sys.argv[1:]
187 argv = sys.argv[1:]
194 self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags)
188 self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags)
195 # kernel should inherit default config file from frontend
189 # kernel should inherit default config file from frontend
196 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
190 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
197
191
198 def init_connection_file(self):
192 def init_connection_file(self):
199 """find the connection file, and load the info if found.
193 """find the connection file, and load the info if found.
200
194
201 The current working directory and the current profile's security
195 The current working directory and the current profile's security
202 directory will be searched for the file if it is not given by
196 directory will be searched for the file if it is not given by
203 absolute path.
197 absolute path.
204
198
205 When attempting to connect to an existing kernel and the `--existing`
199 When attempting to connect to an existing kernel and the `--existing`
206 argument does not match an existing file, it will be interpreted as a
200 argument does not match an existing file, it will be interpreted as a
207 fileglob, and the matching file in the current profile's security dir
201 fileglob, and the matching file in the current profile's security dir
208 with the latest access time will be used.
202 with the latest access time will be used.
209
203
210 After this method is called, self.connection_file contains the *full path*
204 After this method is called, self.connection_file contains the *full path*
211 to the connection file, never just its name.
205 to the connection file, never just its name.
212 """
206 """
213 if self.existing:
207 if self.existing:
214 try:
208 try:
215 cf = find_connection_file(self.existing)
209 cf = find_connection_file(self.existing)
216 except Exception:
210 except Exception:
217 self.log.critical("Could not find existing kernel connection file %s", self.existing)
211 self.log.critical("Could not find existing kernel connection file %s", self.existing)
218 self.exit(1)
212 self.exit(1)
219 self.log.info("Connecting to existing kernel: %s" % cf)
213 self.log.info("Connecting to existing kernel: %s" % cf)
220 self.connection_file = cf
214 self.connection_file = cf
221 else:
215 else:
222 # not existing, check if we are going to write the file
216 # not existing, check if we are going to write the file
223 # and ensure that self.connection_file is a full path, not just the shortname
217 # and ensure that self.connection_file is a full path, not just the shortname
224 try:
218 try:
225 cf = find_connection_file(self.connection_file)
219 cf = find_connection_file(self.connection_file)
226 except Exception:
220 except Exception:
227 # file might not exist
221 # file might not exist
228 if self.connection_file == os.path.basename(self.connection_file):
222 if self.connection_file == os.path.basename(self.connection_file):
229 # just shortname, put it in security dir
223 # just shortname, put it in security dir
230 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
224 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
231 else:
225 else:
232 cf = self.connection_file
226 cf = self.connection_file
233 self.connection_file = cf
227 self.connection_file = cf
234
228
235 # should load_connection_file only be used for existing?
229 # should load_connection_file only be used for existing?
236 # as it is now, this allows reusing ports if an existing
230 # as it is now, this allows reusing ports if an existing
237 # file is requested
231 # file is requested
238 try:
232 try:
239 self.load_connection_file()
233 self.load_connection_file()
240 except Exception:
234 except Exception:
241 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
235 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
242 self.exit(1)
236 self.exit(1)
243
237
244 def load_connection_file(self):
238 def load_connection_file(self):
245 """load ip/port/hmac config from JSON connection file"""
239 """load ip/port/hmac config from JSON connection file"""
246 # this is identical to IPKernelApp.load_connection_file
240 # this is identical to IPKernelApp.load_connection_file
247 # perhaps it can be centralized somewhere?
241 # perhaps it can be centralized somewhere?
248 try:
242 try:
249 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
243 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
250 except IOError:
244 except IOError:
251 self.log.debug("Connection File not found: %s", self.connection_file)
245 self.log.debug("Connection File not found: %s", self.connection_file)
252 return
246 return
253 self.log.debug(u"Loading connection file %s", fname)
247 self.log.debug(u"Loading connection file %s", fname)
254 with open(fname) as f:
248 with open(fname) as f:
255 cfg = json.load(f)
249 cfg = json.load(f)
256 self.transport = cfg.get('transport', 'tcp')
250 self.transport = cfg.get('transport', 'tcp')
257 self.ip = cfg.get('ip', localhost())
251 self.ip = cfg.get('ip', localhost())
258
252
259 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
253 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
260 name = channel + '_port'
254 name = channel + '_port'
261 if getattr(self, name) == 0 and name in cfg:
255 if getattr(self, name) == 0 and name in cfg:
262 # not overridden by config or cl_args
256 # not overridden by config or cl_args
263 setattr(self, name, cfg[name])
257 setattr(self, name, cfg[name])
264 if 'key' in cfg:
258 if 'key' in cfg:
265 self.config.Session.key = str_to_bytes(cfg['key'])
259 self.config.Session.key = str_to_bytes(cfg['key'])
266 if 'signature_scheme' in cfg:
260 if 'signature_scheme' in cfg:
267 self.config.Session.signature_scheme = cfg['signature_scheme']
261 self.config.Session.signature_scheme = cfg['signature_scheme']
268
262
269 def init_ssh(self):
263 def init_ssh(self):
270 """set up ssh tunnels, if needed."""
264 """set up ssh tunnels, if needed."""
271 if not self.existing or (not self.sshserver and not self.sshkey):
265 if not self.existing or (not self.sshserver and not self.sshkey):
272 return
266 return
273 self.load_connection_file()
267 self.load_connection_file()
274
268
275 transport = self.transport
269 transport = self.transport
276 ip = self.ip
270 ip = self.ip
277
271
278 if transport != 'tcp':
272 if transport != 'tcp':
279 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
273 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
280 sys.exit(-1)
274 sys.exit(-1)
281
275
282 if self.sshkey and not self.sshserver:
276 if self.sshkey and not self.sshserver:
283 # specifying just the key implies that we are connecting directly
277 # specifying just the key implies that we are connecting directly
284 self.sshserver = ip
278 self.sshserver = ip
285 ip = localhost()
279 ip = localhost()
286
280
287 # build connection dict for tunnels:
281 # build connection dict for tunnels:
288 info = dict(ip=ip,
282 info = dict(ip=ip,
289 shell_port=self.shell_port,
283 shell_port=self.shell_port,
290 iopub_port=self.iopub_port,
284 iopub_port=self.iopub_port,
291 stdin_port=self.stdin_port,
285 stdin_port=self.stdin_port,
292 hb_port=self.hb_port
286 hb_port=self.hb_port
293 )
287 )
294
288
295 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
289 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
296
290
297 # tunnels return a new set of ports, which will be on localhost:
291 # tunnels return a new set of ports, which will be on localhost:
298 self.ip = localhost()
292 self.ip = localhost()
299 try:
293 try:
300 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
294 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
301 except:
295 except:
302 # even catch KeyboardInterrupt
296 # even catch KeyboardInterrupt
303 self.log.error("Could not setup tunnels", exc_info=True)
297 self.log.error("Could not setup tunnels", exc_info=True)
304 self.exit(1)
298 self.exit(1)
305
299
306 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
300 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
307
301
308 cf = self.connection_file
302 cf = self.connection_file
309 base,ext = os.path.splitext(cf)
303 base,ext = os.path.splitext(cf)
310 base = os.path.basename(base)
304 base = os.path.basename(base)
311 self.connection_file = os.path.basename(base)+'-ssh'+ext
305 self.connection_file = os.path.basename(base)+'-ssh'+ext
312 self.log.critical("To connect another client via this tunnel, use:")
306 self.log.critical("To connect another client via this tunnel, use:")
313 self.log.critical("--existing %s" % self.connection_file)
307 self.log.critical("--existing %s" % self.connection_file)
314
308
315 def _new_connection_file(self):
309 def _new_connection_file(self):
316 cf = ''
310 cf = ''
317 while not cf:
311 while not cf:
318 # we don't need a 128b id to distinguish kernels, use more readable
312 # we don't need a 128b id to distinguish kernels, use more readable
319 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
313 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
320 # kernels can subclass.
314 # kernels can subclass.
321 ident = str(uuid.uuid4()).split('-')[-1]
315 ident = str(uuid.uuid4()).split('-')[-1]
322 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
316 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
323 # only keep if it's actually new. Protect against unlikely collision
317 # only keep if it's actually new. Protect against unlikely collision
324 # in 48b random search space
318 # in 48b random search space
325 cf = cf if not os.path.exists(cf) else ''
319 cf = cf if not os.path.exists(cf) else ''
326 return cf
320 return cf
327
321
328 def init_kernel_manager(self):
322 def init_kernel_manager(self):
329 # Don't let Qt or ZMQ swallow KeyboardInterupts.
323 # Don't let Qt or ZMQ swallow KeyboardInterupts.
330 if self.existing:
324 if self.existing:
331 self.kernel_manager = None
325 self.kernel_manager = None
332 return
326 return
333 signal.signal(signal.SIGINT, signal.SIG_DFL)
327 signal.signal(signal.SIGINT, signal.SIG_DFL)
334
328
335 # Create a KernelManager and start a kernel.
329 # Create a KernelManager and start a kernel.
336 self.kernel_manager = self.kernel_manager_class(
330 self.kernel_manager = self.kernel_manager_class(
337 ip=self.ip,
331 ip=self.ip,
338 transport=self.transport,
332 transport=self.transport,
339 shell_port=self.shell_port,
333 shell_port=self.shell_port,
340 iopub_port=self.iopub_port,
334 iopub_port=self.iopub_port,
341 stdin_port=self.stdin_port,
335 stdin_port=self.stdin_port,
342 hb_port=self.hb_port,
336 hb_port=self.hb_port,
343 connection_file=self.connection_file,
337 connection_file=self.connection_file,
344 parent=self,
338 parent=self,
345 )
339 )
346 self.kernel_manager.client_factory = self.kernel_client_class
340 self.kernel_manager.client_factory = self.kernel_client_class
347 self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv)
341 self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv)
348 atexit.register(self.kernel_manager.cleanup_ipc_files)
342 atexit.register(self.kernel_manager.cleanup_ipc_files)
349
343
350 if self.sshserver:
344 if self.sshserver:
351 # ssh, write new connection file
345 # ssh, write new connection file
352 self.kernel_manager.write_connection_file()
346 self.kernel_manager.write_connection_file()
353
347
354 # in case KM defaults / ssh writing changes things:
348 # in case KM defaults / ssh writing changes things:
355 km = self.kernel_manager
349 km = self.kernel_manager
356 self.shell_port=km.shell_port
350 self.shell_port=km.shell_port
357 self.iopub_port=km.iopub_port
351 self.iopub_port=km.iopub_port
358 self.stdin_port=km.stdin_port
352 self.stdin_port=km.stdin_port
359 self.hb_port=km.hb_port
353 self.hb_port=km.hb_port
360 self.connection_file = km.connection_file
354 self.connection_file = km.connection_file
361
355
362 atexit.register(self.kernel_manager.cleanup_connection_file)
356 atexit.register(self.kernel_manager.cleanup_connection_file)
363
357
364 def init_kernel_client(self):
358 def init_kernel_client(self):
365 if self.kernel_manager is not None:
359 if self.kernel_manager is not None:
366 self.kernel_client = self.kernel_manager.client()
360 self.kernel_client = self.kernel_manager.client()
367 else:
361 else:
368 self.kernel_client = self.kernel_client_class(
362 self.kernel_client = self.kernel_client_class(
369 ip=self.ip,
363 ip=self.ip,
370 transport=self.transport,
364 transport=self.transport,
371 shell_port=self.shell_port,
365 shell_port=self.shell_port,
372 iopub_port=self.iopub_port,
366 iopub_port=self.iopub_port,
373 stdin_port=self.stdin_port,
367 stdin_port=self.stdin_port,
374 hb_port=self.hb_port,
368 hb_port=self.hb_port,
375 connection_file=self.connection_file,
369 connection_file=self.connection_file,
376 parent=self,
370 parent=self,
377 )
371 )
378
372
379 self.kernel_client.start_channels()
373 self.kernel_client.start_channels()
380
374
381
375
382
376
383 def initialize(self, argv=None):
377 def initialize(self, argv=None):
384 """
378 """
385 Classes which mix this class in should call:
379 Classes which mix this class in should call:
386 IPythonConsoleApp.initialize(self,argv)
380 IPythonConsoleApp.initialize(self,argv)
387 """
381 """
388 self.init_connection_file()
382 self.init_connection_file()
389 default_secure(self.config)
383 default_secure(self.config)
390 self.init_ssh()
384 self.init_ssh()
391 self.init_kernel_manager()
385 self.init_kernel_manager()
392 self.init_kernel_client()
386 self.init_kernel_client()
393
387
@@ -1,214 +1,153 b''
1 """Produce SVG versions of active plots for display by the rich Qt frontend.
1 """A matplotlib backend for publishing figures via display_data"""
2 """
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2011 The IPython Development Team
4 #
5 # Distributed under the terms of the BSD License. The full license is in
6 # the file COPYING, distributed as part of this software.
7 #-----------------------------------------------------------------------------
8
3 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
4 # Imports
10 # Imports
5 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
6 from __future__ import print_function
12 from __future__ import print_function
7
13
8 # Third-party imports
14 # Third-party imports
9 import matplotlib
15 import matplotlib
10 from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg
16 from matplotlib.backends.backend_agg import FigureCanvasAgg
11 from matplotlib._pylab_helpers import Gcf
17 from matplotlib._pylab_helpers import Gcf
12
18
13 # Local imports.
19 # Local imports
14 from IPython.config.configurable import SingletonConfigurable
20 from IPython.core.getipython import get_ipython
15 from IPython.core.display import display
21 from IPython.core.display import display
16 from IPython.core.displaypub import publish_display_data
17 from IPython.core.pylabtools import print_figure, select_figure_format
18 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool
19 from IPython.utils.warn import warn
20
21 #-----------------------------------------------------------------------------
22 # Configurable for inline backend options
23 #-----------------------------------------------------------------------------
24 # inherit from InlineBackendConfig for deprecation purposes
25 class InlineBackendConfig(SingletonConfigurable):
26 pass
27
28 class InlineBackend(InlineBackendConfig):
29 """An object to store configuration of the inline backend."""
30
31 def _config_changed(self, name, old, new):
32 # warn on change of renamed config section
33 if new.InlineBackendConfig != old.InlineBackendConfig:
34 warn("InlineBackendConfig has been renamed to InlineBackend")
35 super(InlineBackend, self)._config_changed(name, old, new)
36
37 # The typical default figure size is too large for inline use,
38 # so we shrink the figure size to 6x4, and tweak fonts to
39 # make that fit.
40 rc = Dict({'figure.figsize': (6.0,4.0),
41 # play nicely with white background in the Qt and notebook frontend
42 'figure.facecolor': 'white',
43 'figure.edgecolor': 'white',
44 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
45 'font.size': 10,
46 # 72 dpi matches SVG/qtconsole
47 # this only affects PNG export, as SVG has no dpi setting
48 'savefig.dpi': 72,
49 # 10pt still needs a little more room on the xlabel:
50 'figure.subplot.bottom' : .125
51 }, config=True,
52 help="""Subset of matplotlib rcParams that should be different for the
53 inline backend."""
54 )
55
56 figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True,
57 help="The image format for figures with the inline backend.")
58
59 def _figure_format_changed(self, name, old, new):
60 if self.shell is None:
61 return
62 else:
63 select_figure_format(self.shell, new)
64
65 close_figures = Bool(True, config=True,
66 help="""Close all figures at the end of each cell.
67
68 When True, ensures that each cell starts with no active figures, but it
69 also means that one must keep track of references in order to edit or
70 redraw figures in subsequent cells. This mode is ideal for the notebook,
71 where residual plots from other cells might be surprising.
72
73 When False, one must call figure() to create new figures. This means
74 that gcf() and getfigs() can reference figures created in other cells,
75 and the active figure can continue to be edited with pylab/pyplot
76 methods that reference the current active figure. This mode facilitates
77 iterative editing of figures, and behaves most consistently with
78 other matplotlib backends, but figure barriers between cells must
79 be explicit.
80 """)
81
82 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
83
22
23 from .config import InlineBackend
84
24
85 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
86 # Functions
26 # Functions
87 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
88
28
89 def show(close=None):
29 def show(close=None):
90 """Show all figures as SVG/PNG payloads sent to the IPython clients.
30 """Show all figures as SVG/PNG payloads sent to the IPython clients.
91
31
92 Parameters
32 Parameters
93 ----------
33 ----------
94 close : bool, optional
34 close : bool, optional
95 If true, a ``plt.close('all')`` call is automatically issued after
35 If true, a ``plt.close('all')`` call is automatically issued after
96 sending all the figures. If this is set, the figures will entirely
36 sending all the figures. If this is set, the figures will entirely
97 removed from the internal list of figures.
37 removed from the internal list of figures.
98 """
38 """
99 if close is None:
39 if close is None:
100 close = InlineBackend.instance().close_figures
40 close = InlineBackend.instance().close_figures
101 try:
41 try:
102 for figure_manager in Gcf.get_all_fig_managers():
42 for figure_manager in Gcf.get_all_fig_managers():
103 display(figure_manager.canvas.figure)
43 display(figure_manager.canvas.figure)
104 finally:
44 finally:
105 show._to_draw = []
45 show._to_draw = []
106 if close:
46 if close:
107 matplotlib.pyplot.close('all')
47 matplotlib.pyplot.close('all')
108
48
109
49
110
111 # This flag will be reset by draw_if_interactive when called
50 # This flag will be reset by draw_if_interactive when called
112 show._draw_called = False
51 show._draw_called = False
113 # list of figures to draw when flush_figures is called
52 # list of figures to draw when flush_figures is called
114 show._to_draw = []
53 show._to_draw = []
115
54
116
55
117 def draw_if_interactive():
56 def draw_if_interactive():
118 """
57 """
119 Is called after every pylab drawing command
58 Is called after every pylab drawing command
120 """
59 """
121 # signal that the current active figure should be sent at the end of
60 # signal that the current active figure should be sent at the end of
122 # execution. Also sets the _draw_called flag, signaling that there will be
61 # execution. Also sets the _draw_called flag, signaling that there will be
123 # something to send. At the end of the code execution, a separate call to
62 # something to send. At the end of the code execution, a separate call to
124 # flush_figures() will act upon these values
63 # flush_figures() will act upon these values
125 manager = Gcf.get_active()
64 manager = Gcf.get_active()
126 if manager is None:
65 if manager is None:
127 return
66 return
128 fig = manager.canvas.figure
67 fig = manager.canvas.figure
129
68
130 # Hack: matplotlib FigureManager objects in interacive backends (at least
69 # Hack: matplotlib FigureManager objects in interacive backends (at least
131 # in some of them) monkeypatch the figure object and add a .show() method
70 # in some of them) monkeypatch the figure object and add a .show() method
132 # to it. This applies the same monkeypatch in order to support user code
71 # to it. This applies the same monkeypatch in order to support user code
133 # that might expect `.show()` to be part of the official API of figure
72 # that might expect `.show()` to be part of the official API of figure
134 # objects.
73 # objects.
135 # For further reference:
74 # For further reference:
136 # https://github.com/ipython/ipython/issues/1612
75 # https://github.com/ipython/ipython/issues/1612
137 # https://github.com/matplotlib/matplotlib/issues/835
76 # https://github.com/matplotlib/matplotlib/issues/835
138
77
139 if not hasattr(fig, 'show'):
78 if not hasattr(fig, 'show'):
140 # Queue up `fig` for display
79 # Queue up `fig` for display
141 fig.show = lambda *a: display(fig)
80 fig.show = lambda *a: display(fig)
142
81
143 # If matplotlib was manually set to non-interactive mode, this function
82 # If matplotlib was manually set to non-interactive mode, this function
144 # should be a no-op (otherwise we'll generate duplicate plots, since a user
83 # should be a no-op (otherwise we'll generate duplicate plots, since a user
145 # who set ioff() manually expects to make separate draw/show calls).
84 # who set ioff() manually expects to make separate draw/show calls).
146 if not matplotlib.is_interactive():
85 if not matplotlib.is_interactive():
147 return
86 return
148
87
149 # ensure current figure will be drawn, and each subsequent call
88 # ensure current figure will be drawn, and each subsequent call
150 # of draw_if_interactive() moves the active figure to ensure it is
89 # of draw_if_interactive() moves the active figure to ensure it is
151 # drawn last
90 # drawn last
152 try:
91 try:
153 show._to_draw.remove(fig)
92 show._to_draw.remove(fig)
154 except ValueError:
93 except ValueError:
155 # ensure it only appears in the draw list once
94 # ensure it only appears in the draw list once
156 pass
95 pass
157 # Queue up the figure for drawing in next show() call
96 # Queue up the figure for drawing in next show() call
158 show._to_draw.append(fig)
97 show._to_draw.append(fig)
159 show._draw_called = True
98 show._draw_called = True
160
99
161
100
162 def flush_figures():
101 def flush_figures():
163 """Send all figures that changed
102 """Send all figures that changed
164
103
165 This is meant to be called automatically and will call show() if, during
104 This is meant to be called automatically and will call show() if, during
166 prior code execution, there had been any calls to draw_if_interactive.
105 prior code execution, there had been any calls to draw_if_interactive.
167
106
168 This function is meant to be used as a post_execute callback in IPython,
107 This function is meant to be used as a post_execute callback in IPython,
169 so user-caused errors are handled with showtraceback() instead of being
108 so user-caused errors are handled with showtraceback() instead of being
170 allowed to raise. If this function is not called from within IPython,
109 allowed to raise. If this function is not called from within IPython,
171 then these exceptions will raise.
110 then these exceptions will raise.
172 """
111 """
173 if not show._draw_called:
112 if not show._draw_called:
174 return
113 return
175
114
176 if InlineBackend.instance().close_figures:
115 if InlineBackend.instance().close_figures:
177 # ignore the tracking, just draw and close all figures
116 # ignore the tracking, just draw and close all figures
178 try:
117 try:
179 return show(True)
118 return show(True)
180 except Exception as e:
119 except Exception as e:
181 # safely show traceback if in IPython, else raise
120 # safely show traceback if in IPython, else raise
182 try:
121 try:
183 get_ipython
122 get_ipython
184 except NameError:
123 except NameError:
185 raise e
124 raise e
186 else:
125 else:
187 get_ipython().showtraceback()
126 get_ipython().showtraceback()
188 return
127 return
189 try:
128 try:
190 # exclude any figures that were closed:
129 # exclude any figures that were closed:
191 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
130 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
192 for fig in [ fig for fig in show._to_draw if fig in active ]:
131 for fig in [ fig for fig in show._to_draw if fig in active ]:
193 try:
132 try:
194 display(fig)
133 display(fig)
195 except Exception as e:
134 except Exception as e:
196 # safely show traceback if in IPython, else raise
135 # safely show traceback if in IPython, else raise
197 try:
136 try:
198 get_ipython
137 get_ipython
199 except NameError:
138 except NameError:
200 raise e
139 raise e
201 else:
140 else:
202 get_ipython().showtraceback()
141 get_ipython().showtraceback()
203 break
142 break
204 finally:
143 finally:
205 # clear flags for next round
144 # clear flags for next round
206 show._to_draw = []
145 show._to_draw = []
207 show._draw_called = False
146 show._draw_called = False
208
147
209
148
210 # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
149 # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
211 # figurecanvas. This is set here to a Agg canvas
150 # figurecanvas. This is set here to a Agg canvas
212 # See https://github.com/matplotlib/matplotlib/pull/1125
151 # See https://github.com/matplotlib/matplotlib/pull/1125
213 FigureCanvas = FigureCanvasAgg
152 FigureCanvas = FigureCanvasAgg
214
153
General Comments 0
You need to be logged in to leave comments. Login now