##// END OF EJS Templates
flush messages printed during startup...
MinRK -
Show More
@@ -1,322 +1,325
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Pylab (matplotlib) support utilities.
2 """Pylab (matplotlib) support utilities.
3
3
4 Authors
4 Authors
5 -------
5 -------
6
6
7 * Fernando Perez.
7 * Fernando Perez.
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2009 The IPython Development Team
12 # Copyright (C) 2009 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import sys
22 from io import BytesIO
23 from io import BytesIO
23
24
24 from IPython.utils.decorators import flag_calls
25 from IPython.utils.decorators import flag_calls
25
26
26 # If user specifies a GUI, that dictates the backend, otherwise we read the
27 # If user specifies a GUI, that dictates the backend, otherwise we read the
27 # user's mpl default from the mpl rc structure
28 # user's mpl default from the mpl rc structure
28 backends = {'tk': 'TkAgg',
29 backends = {'tk': 'TkAgg',
29 'gtk': 'GTKAgg',
30 'gtk': 'GTKAgg',
30 'wx': 'WXAgg',
31 'wx': 'WXAgg',
31 'qt': 'Qt4Agg', # qt3 not supported
32 'qt': 'Qt4Agg', # qt3 not supported
32 'qt4': 'Qt4Agg',
33 'qt4': 'Qt4Agg',
33 'osx': 'MacOSX',
34 'osx': 'MacOSX',
34 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
35 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
35
36
36 # We also need a reverse backends2guis mapping that will properly choose which
37 # We also need a reverse backends2guis mapping that will properly choose which
37 # GUI support to activate based on the desired matplotlib backend. For the
38 # GUI support to activate based on the desired matplotlib backend. For the
38 # most part it's just a reverse of the above dict, but we also need to add a
39 # most part it's just a reverse of the above dict, but we also need to add a
39 # few others that map to the same GUI manually:
40 # few others that map to the same GUI manually:
40 backend2gui = dict(zip(backends.values(), backends.keys()))
41 backend2gui = dict(zip(backends.values(), backends.keys()))
41 # In the reverse mapping, there are a few extra valid matplotlib backends that
42 # In the reverse mapping, there are a few extra valid matplotlib backends that
42 # map to the same GUI support
43 # map to the same GUI support
43 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
44 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
44 backend2gui['WX'] = 'wx'
45 backend2gui['WX'] = 'wx'
45 backend2gui['CocoaAgg'] = 'osx'
46 backend2gui['CocoaAgg'] = 'osx'
46
47
47 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
48 # Matplotlib utilities
49 # Matplotlib utilities
49 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
50
51
51
52
52 def getfigs(*fig_nums):
53 def getfigs(*fig_nums):
53 """Get a list of matplotlib figures by figure numbers.
54 """Get a list of matplotlib figures by figure numbers.
54
55
55 If no arguments are given, all available figures are returned. If the
56 If no arguments are given, all available figures are returned. If the
56 argument list contains references to invalid figures, a warning is printed
57 argument list contains references to invalid figures, a warning is printed
57 but the function continues pasting further figures.
58 but the function continues pasting further figures.
58
59
59 Parameters
60 Parameters
60 ----------
61 ----------
61 figs : tuple
62 figs : tuple
62 A tuple of ints giving the figure numbers of the figures to return.
63 A tuple of ints giving the figure numbers of the figures to return.
63 """
64 """
64 from matplotlib._pylab_helpers import Gcf
65 from matplotlib._pylab_helpers import Gcf
65 if not fig_nums:
66 if not fig_nums:
66 fig_managers = Gcf.get_all_fig_managers()
67 fig_managers = Gcf.get_all_fig_managers()
67 return [fm.canvas.figure for fm in fig_managers]
68 return [fm.canvas.figure for fm in fig_managers]
68 else:
69 else:
69 figs = []
70 figs = []
70 for num in fig_nums:
71 for num in fig_nums:
71 f = Gcf.figs.get(num)
72 f = Gcf.figs.get(num)
72 if f is None:
73 if f is None:
73 print('Warning: figure %s not available.' % num)
74 print('Warning: figure %s not available.' % num)
74 else:
75 else:
75 figs.append(f.canvas.figure)
76 figs.append(f.canvas.figure)
76 return figs
77 return figs
77
78
78
79
79 def figsize(sizex, sizey):
80 def figsize(sizex, sizey):
80 """Set the default figure size to be [sizex, sizey].
81 """Set the default figure size to be [sizex, sizey].
81
82
82 This is just an easy to remember, convenience wrapper that sets::
83 This is just an easy to remember, convenience wrapper that sets::
83
84
84 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
85 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
85 """
86 """
86 import matplotlib
87 import matplotlib
87 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
88 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
88
89
89
90
90 def print_figure(fig, fmt='png'):
91 def print_figure(fig, fmt='png'):
91 """Convert a figure to svg or png for inline display."""
92 """Convert a figure to svg or png for inline display."""
92 # When there's an empty figure, we shouldn't return anything, otherwise we
93 # When there's an empty figure, we shouldn't return anything, otherwise we
93 # get big blank areas in the qt console.
94 # get big blank areas in the qt console.
94 if not fig.axes:
95 if not fig.axes:
95 return
96 return
96
97
97 fc = fig.get_facecolor()
98 fc = fig.get_facecolor()
98 ec = fig.get_edgecolor()
99 ec = fig.get_edgecolor()
99 fig.set_facecolor('white')
100 fig.set_facecolor('white')
100 fig.set_edgecolor('white')
101 fig.set_edgecolor('white')
101 try:
102 try:
102 bytes_io = BytesIO()
103 bytes_io = BytesIO()
103 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight')
104 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight')
104 data = bytes_io.getvalue()
105 data = bytes_io.getvalue()
105 finally:
106 finally:
106 fig.set_facecolor(fc)
107 fig.set_facecolor(fc)
107 fig.set_edgecolor(ec)
108 fig.set_edgecolor(ec)
108 return data
109 return data
109
110
110
111
111 # We need a little factory function here to create the closure where
112 # We need a little factory function here to create the closure where
112 # safe_execfile can live.
113 # safe_execfile can live.
113 def mpl_runner(safe_execfile):
114 def mpl_runner(safe_execfile):
114 """Factory to return a matplotlib-enabled runner for %run.
115 """Factory to return a matplotlib-enabled runner for %run.
115
116
116 Parameters
117 Parameters
117 ----------
118 ----------
118 safe_execfile : function
119 safe_execfile : function
119 This must be a function with the same interface as the
120 This must be a function with the same interface as the
120 :meth:`safe_execfile` method of IPython.
121 :meth:`safe_execfile` method of IPython.
121
122
122 Returns
123 Returns
123 -------
124 -------
124 A function suitable for use as the ``runner`` argument of the %run magic
125 A function suitable for use as the ``runner`` argument of the %run magic
125 function.
126 function.
126 """
127 """
127
128
128 def mpl_execfile(fname,*where,**kw):
129 def mpl_execfile(fname,*where,**kw):
129 """matplotlib-aware wrapper around safe_execfile.
130 """matplotlib-aware wrapper around safe_execfile.
130
131
131 Its interface is identical to that of the :func:`execfile` builtin.
132 Its interface is identical to that of the :func:`execfile` builtin.
132
133
133 This is ultimately a call to execfile(), but wrapped in safeties to
134 This is ultimately a call to execfile(), but wrapped in safeties to
134 properly handle interactive rendering."""
135 properly handle interactive rendering."""
135
136
136 import matplotlib
137 import matplotlib
137 import matplotlib.pylab as pylab
138 import matplotlib.pylab as pylab
138
139
139 #print '*** Matplotlib runner ***' # dbg
140 #print '*** Matplotlib runner ***' # dbg
140 # turn off rendering until end of script
141 # turn off rendering until end of script
141 is_interactive = matplotlib.rcParams['interactive']
142 is_interactive = matplotlib.rcParams['interactive']
142 matplotlib.interactive(False)
143 matplotlib.interactive(False)
143 safe_execfile(fname,*where,**kw)
144 safe_execfile(fname,*where,**kw)
144 matplotlib.interactive(is_interactive)
145 matplotlib.interactive(is_interactive)
145 # make rendering call now, if the user tried to do it
146 # make rendering call now, if the user tried to do it
146 if pylab.draw_if_interactive.called:
147 if pylab.draw_if_interactive.called:
147 pylab.draw()
148 pylab.draw()
148 pylab.draw_if_interactive.called = False
149 pylab.draw_if_interactive.called = False
149
150
150 return mpl_execfile
151 return mpl_execfile
151
152
152
153
153 def select_figure_format(shell, fmt):
154 def select_figure_format(shell, fmt):
154 """Select figure format for inline backend, either 'png' or 'svg'.
155 """Select figure format for inline backend, either 'png' or 'svg'.
155
156
156 Using this method ensures only one figure format is active at a time.
157 Using this method ensures only one figure format is active at a time.
157 """
158 """
158 from matplotlib.figure import Figure
159 from matplotlib.figure import Figure
159 from IPython.zmq.pylab import backend_inline
160 from IPython.zmq.pylab import backend_inline
160
161
161 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
162 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
162 png_formatter = shell.display_formatter.formatters['image/png']
163 png_formatter = shell.display_formatter.formatters['image/png']
163
164
164 if fmt=='png':
165 if fmt=='png':
165 svg_formatter.type_printers.pop(Figure, None)
166 svg_formatter.type_printers.pop(Figure, None)
166 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
167 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
167 elif fmt=='svg':
168 elif fmt=='svg':
168 png_formatter.type_printers.pop(Figure, None)
169 png_formatter.type_printers.pop(Figure, None)
169 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
170 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
170 else:
171 else:
171 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
172 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
172
173
173 # set the format to be used in the backend()
174 # set the format to be used in the backend()
174 backend_inline._figure_format = fmt
175 backend_inline._figure_format = fmt
175
176
176 #-----------------------------------------------------------------------------
177 #-----------------------------------------------------------------------------
177 # Code for initializing matplotlib and importing pylab
178 # Code for initializing matplotlib and importing pylab
178 #-----------------------------------------------------------------------------
179 #-----------------------------------------------------------------------------
179
180
180
181
181 def find_gui_and_backend(gui=None):
182 def find_gui_and_backend(gui=None):
182 """Given a gui string return the gui and mpl backend.
183 """Given a gui string return the gui and mpl backend.
183
184
184 Parameters
185 Parameters
185 ----------
186 ----------
186 gui : str
187 gui : str
187 Can be one of ('tk','gtk','wx','qt','qt4','inline').
188 Can be one of ('tk','gtk','wx','qt','qt4','inline').
188
189
189 Returns
190 Returns
190 -------
191 -------
191 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
192 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
192 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
193 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
193 """
194 """
194
195
195 import matplotlib
196 import matplotlib
196
197
197 if gui and gui != 'auto':
198 if gui and gui != 'auto':
198 # select backend based on requested gui
199 # select backend based on requested gui
199 backend = backends[gui]
200 backend = backends[gui]
200 else:
201 else:
201 backend = matplotlib.rcParams['backend']
202 backend = matplotlib.rcParams['backend']
202 # In this case, we need to find what the appropriate gui selection call
203 # In this case, we need to find what the appropriate gui selection call
203 # should be for IPython, so we can activate inputhook accordingly
204 # should be for IPython, so we can activate inputhook accordingly
204 gui = backend2gui.get(backend, None)
205 gui = backend2gui.get(backend, None)
205 return gui, backend
206 return gui, backend
206
207
207
208
208 def activate_matplotlib(backend):
209 def activate_matplotlib(backend):
209 """Activate the given backend and set interactive to True."""
210 """Activate the given backend and set interactive to True."""
210
211
211 import matplotlib
212 import matplotlib
212 if backend.startswith('module://'):
213 if backend.startswith('module://'):
213 # Work around bug in matplotlib: matplotlib.use converts the
214 # Work around bug in matplotlib: matplotlib.use converts the
214 # backend_id to lowercase even if a module name is specified!
215 # backend_id to lowercase even if a module name is specified!
215 matplotlib.rcParams['backend'] = backend
216 matplotlib.rcParams['backend'] = backend
216 else:
217 else:
217 matplotlib.use(backend)
218 matplotlib.use(backend)
218 matplotlib.interactive(True)
219 matplotlib.interactive(True)
219
220
220 # This must be imported last in the matplotlib series, after
221 # This must be imported last in the matplotlib series, after
221 # backend/interactivity choices have been made
222 # backend/interactivity choices have been made
222 import matplotlib.pylab as pylab
223 import matplotlib.pylab as pylab
223
224
224 # XXX For now leave this commented out, but depending on discussions with
225 # XXX For now leave this commented out, but depending on discussions with
225 # mpl-dev, we may be able to allow interactive switching...
226 # mpl-dev, we may be able to allow interactive switching...
226 #import matplotlib.pyplot
227 #import matplotlib.pyplot
227 #matplotlib.pyplot.switch_backend(backend)
228 #matplotlib.pyplot.switch_backend(backend)
228
229
229 pylab.show._needmain = False
230 pylab.show._needmain = False
230 # We need to detect at runtime whether show() is called by the user.
231 # We need to detect at runtime whether show() is called by the user.
231 # For this, we wrap it into a decorator which adds a 'called' flag.
232 # For this, we wrap it into a decorator which adds a 'called' flag.
232 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
233 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
233
234
234 def import_pylab(user_ns, backend, import_all=True, shell=None):
235 def import_pylab(user_ns, backend, import_all=True, shell=None):
235 """Import the standard pylab symbols into user_ns."""
236 """Import the standard pylab symbols into user_ns."""
236
237
237 # Import numpy as np/pyplot as plt are conventions we're trying to
238 # Import numpy as np/pyplot as plt are conventions we're trying to
238 # somewhat standardize on. Making them available to users by default
239 # somewhat standardize on. Making them available to users by default
239 # will greatly help this.
240 # will greatly help this.
240 s = ("import numpy\n"
241 s = ("import numpy\n"
241 "import matplotlib\n"
242 "import matplotlib\n"
242 "from matplotlib import pylab, mlab, pyplot\n"
243 "from matplotlib import pylab, mlab, pyplot\n"
243 "np = numpy\n"
244 "np = numpy\n"
244 "plt = pyplot\n"
245 "plt = pyplot\n"
245 )
246 )
246 exec s in user_ns
247 exec s in user_ns
247
248
248 if shell is not None:
249 if shell is not None:
249 exec s in shell.user_ns_hidden
250 exec s in shell.user_ns_hidden
250 # If using our svg payload backend, register the post-execution
251 # If using our svg payload backend, register the post-execution
251 # function that will pick up the results for display. This can only be
252 # function that will pick up the results for display. This can only be
252 # done with access to the real shell object.
253 # done with access to the real shell object.
253 #
254 #
254 from IPython.zmq.pylab.backend_inline import InlineBackend
255 from IPython.zmq.pylab.backend_inline import InlineBackend
255
256
256 cfg = InlineBackend.instance(config=shell.config)
257 cfg = InlineBackend.instance(config=shell.config)
257 cfg.shell = shell
258 cfg.shell = shell
258 if cfg not in shell.configurables:
259 if cfg not in shell.configurables:
259 shell.configurables.append(cfg)
260 shell.configurables.append(cfg)
260
261
261 if backend == backends['inline']:
262 if backend == backends['inline']:
262 from IPython.zmq.pylab.backend_inline import flush_figures
263 from IPython.zmq.pylab.backend_inline import flush_figures
263 from matplotlib import pyplot
264 from matplotlib import pyplot
264 shell.register_post_execute(flush_figures)
265 shell.register_post_execute(flush_figures)
265 # load inline_rc
266 # load inline_rc
266 pyplot.rcParams.update(cfg.rc)
267 pyplot.rcParams.update(cfg.rc)
267
268
268 # Add 'figsize' to pyplot and to the user's namespace
269 # Add 'figsize' to pyplot and to the user's namespace
269 user_ns['figsize'] = pyplot.figsize = figsize
270 user_ns['figsize'] = pyplot.figsize = figsize
270 shell.user_ns_hidden['figsize'] = figsize
271 shell.user_ns_hidden['figsize'] = figsize
271
272
272 # Setup the default figure format
273 # Setup the default figure format
273 fmt = cfg.figure_format
274 fmt = cfg.figure_format
274 select_figure_format(shell, fmt)
275 select_figure_format(shell, fmt)
275
276
276 # The old pastefig function has been replaced by display
277 # The old pastefig function has been replaced by display
277 from IPython.core.display import display
278 from IPython.core.display import display
278 # Add display and display_png to the user's namespace
279 # Add display and display_png to the user's namespace
279 user_ns['display'] = display
280 user_ns['display'] = display
280 shell.user_ns_hidden['display'] = display
281 shell.user_ns_hidden['display'] = display
281 user_ns['getfigs'] = getfigs
282 user_ns['getfigs'] = getfigs
282 shell.user_ns_hidden['getfigs'] = getfigs
283 shell.user_ns_hidden['getfigs'] = getfigs
283
284
284 if import_all:
285 if import_all:
285 s = ("from matplotlib.pylab import *\n"
286 s = ("from matplotlib.pylab import *\n"
286 "from numpy import *\n")
287 "from numpy import *\n")
287 exec s in user_ns
288 exec s in user_ns
288 if shell is not None:
289 if shell is not None:
289 exec s in shell.user_ns_hidden
290 exec s in shell.user_ns_hidden
290
291
291
292
292 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
293 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
293 """Activate pylab mode in the user's namespace.
294 """Activate pylab mode in the user's namespace.
294
295
295 Loads and initializes numpy, matplotlib and friends for interactive use.
296 Loads and initializes numpy, matplotlib and friends for interactive use.
296
297
297 Parameters
298 Parameters
298 ----------
299 ----------
299 user_ns : dict
300 user_ns : dict
300 Namespace where the imports will occur.
301 Namespace where the imports will occur.
301
302
302 gui : optional, string
303 gui : optional, string
303 A valid gui name following the conventions of the %gui magic.
304 A valid gui name following the conventions of the %gui magic.
304
305
305 import_all : optional, boolean
306 import_all : optional, boolean
306 If true, an 'import *' is done from numpy and pylab.
307 If true, an 'import *' is done from numpy and pylab.
307
308
308 Returns
309 Returns
309 -------
310 -------
310 The actual gui used (if not given as input, it was obtained from matplotlib
311 The actual gui used (if not given as input, it was obtained from matplotlib
311 itself, and will be needed next to configure IPython's gui integration.
312 itself, and will be needed next to configure IPython's gui integration.
312 """
313 """
313 gui, backend = find_gui_and_backend(gui)
314 gui, backend = find_gui_and_backend(gui)
314 activate_matplotlib(backend)
315 activate_matplotlib(backend)
315 import_pylab(user_ns, backend, import_all, shell)
316 import_pylab(user_ns, backend, import_all, shell)
316
317
317 print """
318 print """
318 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
319 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
319 For more information, type 'help(pylab)'.""" % backend
320 For more information, type 'help(pylab)'.""" % backend
321 # flush stdout, just to be safe
322 sys.stdout.flush()
320
323
321 return gui
324 return gui
322
325
@@ -1,299 +1,303
1 """An Application for launching a kernel
1 """An Application for launching a kernel
2
2
3 Authors
3 Authors
4 -------
4 -------
5 * MinRK
5 * MinRK
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2011 The IPython Development Team
8 # Copyright (C) 2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING.txt, distributed as part of this software.
11 # the file COPYING.txt, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import json
19 import json
20 import os
20 import os
21 import sys
21 import sys
22
22
23 # System library imports.
23 # System library imports.
24 import zmq
24 import zmq
25
25
26 # IPython imports.
26 # IPython imports.
27 from IPython.core.ultratb import FormattedTB
27 from IPython.core.ultratb import FormattedTB
28 from IPython.core.application import (
28 from IPython.core.application import (
29 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
29 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
30 )
30 )
31 from IPython.utils import io
31 from IPython.utils import io
32 from IPython.utils.localinterfaces import LOCALHOST
32 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.path import filefind
33 from IPython.utils.path import filefind
34 from IPython.utils.py3compat import str_to_bytes
34 from IPython.utils.py3compat import str_to_bytes
35 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Integer, Bool,
35 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Integer, Bool,
36 DottedObjectName)
36 DottedObjectName)
37 from IPython.utils.importstring import import_item
37 from IPython.utils.importstring import import_item
38 # local imports
38 # local imports
39 from IPython.zmq.entry_point import write_connection_file
39 from IPython.zmq.entry_point import write_connection_file
40 from IPython.zmq.heartbeat import Heartbeat
40 from IPython.zmq.heartbeat import Heartbeat
41 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
41 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
42 from IPython.zmq.session import (
42 from IPython.zmq.session import (
43 Session, session_flags, session_aliases, default_secure,
43 Session, session_flags, session_aliases, default_secure,
44 )
44 )
45
45
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Flags and Aliases
48 # Flags and Aliases
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 kernel_aliases = dict(base_aliases)
51 kernel_aliases = dict(base_aliases)
52 kernel_aliases.update({
52 kernel_aliases.update({
53 'ip' : 'KernelApp.ip',
53 'ip' : 'KernelApp.ip',
54 'hb' : 'KernelApp.hb_port',
54 'hb' : 'KernelApp.hb_port',
55 'shell' : 'KernelApp.shell_port',
55 'shell' : 'KernelApp.shell_port',
56 'iopub' : 'KernelApp.iopub_port',
56 'iopub' : 'KernelApp.iopub_port',
57 'stdin' : 'KernelApp.stdin_port',
57 'stdin' : 'KernelApp.stdin_port',
58 'f' : 'KernelApp.connection_file',
58 'f' : 'KernelApp.connection_file',
59 'parent': 'KernelApp.parent',
59 'parent': 'KernelApp.parent',
60 })
60 })
61 if sys.platform.startswith('win'):
61 if sys.platform.startswith('win'):
62 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
62 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
63
63
64 kernel_flags = dict(base_flags)
64 kernel_flags = dict(base_flags)
65 kernel_flags.update({
65 kernel_flags.update({
66 'no-stdout' : (
66 'no-stdout' : (
67 {'KernelApp' : {'no_stdout' : True}},
67 {'KernelApp' : {'no_stdout' : True}},
68 "redirect stdout to the null device"),
68 "redirect stdout to the null device"),
69 'no-stderr' : (
69 'no-stderr' : (
70 {'KernelApp' : {'no_stderr' : True}},
70 {'KernelApp' : {'no_stderr' : True}},
71 "redirect stderr to the null device"),
71 "redirect stderr to the null device"),
72 })
72 })
73
73
74 # inherit flags&aliases for Sessions
74 # inherit flags&aliases for Sessions
75 kernel_aliases.update(session_aliases)
75 kernel_aliases.update(session_aliases)
76 kernel_flags.update(session_flags)
76 kernel_flags.update(session_flags)
77
77
78
78
79
79
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81 # Application class for starting a Kernel
81 # Application class for starting a Kernel
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83
83
84 class KernelApp(BaseIPythonApplication):
84 class KernelApp(BaseIPythonApplication):
85 name='pykernel'
85 name='pykernel'
86 aliases = Dict(kernel_aliases)
86 aliases = Dict(kernel_aliases)
87 flags = Dict(kernel_flags)
87 flags = Dict(kernel_flags)
88 classes = [Session]
88 classes = [Session]
89 # the kernel class, as an importstring
89 # the kernel class, as an importstring
90 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
90 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
91 kernel = Any()
91 kernel = Any()
92 poller = Any() # don't restrict this even though current pollers are all Threads
92 poller = Any() # don't restrict this even though current pollers are all Threads
93 heartbeat = Instance(Heartbeat)
93 heartbeat = Instance(Heartbeat)
94 session = Instance('IPython.zmq.session.Session')
94 session = Instance('IPython.zmq.session.Session')
95 ports = Dict()
95 ports = Dict()
96
96
97 # inherit config file name from parent:
97 # inherit config file name from parent:
98 parent_appname = Unicode(config=True)
98 parent_appname = Unicode(config=True)
99 def _parent_appname_changed(self, name, old, new):
99 def _parent_appname_changed(self, name, old, new):
100 if self.config_file_specified:
100 if self.config_file_specified:
101 # it was manually specified, ignore
101 # it was manually specified, ignore
102 return
102 return
103 self.config_file_name = new.replace('-','_') + u'_config.py'
103 self.config_file_name = new.replace('-','_') + u'_config.py'
104 # don't let this count as specifying the config file
104 # don't let this count as specifying the config file
105 self.config_file_specified = False
105 self.config_file_specified = False
106
106
107 # connection info:
107 # connection info:
108 ip = Unicode(LOCALHOST, config=True,
108 ip = Unicode(LOCALHOST, config=True,
109 help="Set the IP or interface on which the kernel will listen.")
109 help="Set the IP or interface on which the kernel will listen.")
110 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
110 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
111 shell_port = Integer(0, config=True, help="set the shell (XREP) port [default: random]")
111 shell_port = Integer(0, config=True, help="set the shell (XREP) port [default: random]")
112 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
112 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
113 stdin_port = Integer(0, config=True, help="set the stdin (XREQ) port [default: random]")
113 stdin_port = Integer(0, config=True, help="set the stdin (XREQ) port [default: random]")
114 connection_file = Unicode('', config=True,
114 connection_file = Unicode('', config=True,
115 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
115 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
116
116
117 This file will contain the IP, ports, and authentication key needed to connect
117 This file will contain the IP, ports, and authentication key needed to connect
118 clients to this kernel. By default, this file will be created in the security-dir
118 clients to this kernel. By default, this file will be created in the security-dir
119 of the current profile, but can be specified by absolute path.
119 of the current profile, but can be specified by absolute path.
120 """)
120 """)
121
121
122 # streams, etc.
122 # streams, etc.
123 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
123 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
124 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
124 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
125 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
125 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
126 config=True, help="The importstring for the OutStream factory")
126 config=True, help="The importstring for the OutStream factory")
127 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
127 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
128 config=True, help="The importstring for the DisplayHook factory")
128 config=True, help="The importstring for the DisplayHook factory")
129
129
130 # polling
130 # polling
131 parent = Integer(0, config=True,
131 parent = Integer(0, config=True,
132 help="""kill this process if its parent dies. On Windows, the argument
132 help="""kill this process if its parent dies. On Windows, the argument
133 specifies the HANDLE of the parent process, otherwise it is simply boolean.
133 specifies the HANDLE of the parent process, otherwise it is simply boolean.
134 """)
134 """)
135 interrupt = Integer(0, config=True,
135 interrupt = Integer(0, config=True,
136 help="""ONLY USED ON WINDOWS
136 help="""ONLY USED ON WINDOWS
137 Interrupt this process when the parent is signalled.
137 Interrupt this process when the parent is signalled.
138 """)
138 """)
139
139
140 def init_crash_handler(self):
140 def init_crash_handler(self):
141 # Install minimal exception handling
141 # Install minimal exception handling
142 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
142 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
143 ostream=sys.__stdout__)
143 ostream=sys.__stdout__)
144
144
145 def init_poller(self):
145 def init_poller(self):
146 if sys.platform == 'win32':
146 if sys.platform == 'win32':
147 if self.interrupt or self.parent:
147 if self.interrupt or self.parent:
148 self.poller = ParentPollerWindows(self.interrupt, self.parent)
148 self.poller = ParentPollerWindows(self.interrupt, self.parent)
149 elif self.parent:
149 elif self.parent:
150 self.poller = ParentPollerUnix()
150 self.poller = ParentPollerUnix()
151
151
152 def _bind_socket(self, s, port):
152 def _bind_socket(self, s, port):
153 iface = 'tcp://%s' % self.ip
153 iface = 'tcp://%s' % self.ip
154 if port <= 0:
154 if port <= 0:
155 port = s.bind_to_random_port(iface)
155 port = s.bind_to_random_port(iface)
156 else:
156 else:
157 s.bind(iface + ':%i'%port)
157 s.bind(iface + ':%i'%port)
158 return port
158 return port
159
159
160 def load_connection_file(self):
160 def load_connection_file(self):
161 """load ip/port/hmac config from JSON connection file"""
161 """load ip/port/hmac config from JSON connection file"""
162 try:
162 try:
163 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
163 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
164 except IOError:
164 except IOError:
165 self.log.debug("Connection file not found: %s", self.connection_file)
165 self.log.debug("Connection file not found: %s", self.connection_file)
166 return
166 return
167 self.log.debug(u"Loading connection file %s", fname)
167 self.log.debug(u"Loading connection file %s", fname)
168 with open(fname) as f:
168 with open(fname) as f:
169 s = f.read()
169 s = f.read()
170 cfg = json.loads(s)
170 cfg = json.loads(s)
171 if self.ip == LOCALHOST and 'ip' in cfg:
171 if self.ip == LOCALHOST and 'ip' in cfg:
172 # not overridden by config or cl_args
172 # not overridden by config or cl_args
173 self.ip = cfg['ip']
173 self.ip = cfg['ip']
174 for channel in ('hb', 'shell', 'iopub', 'stdin'):
174 for channel in ('hb', 'shell', 'iopub', 'stdin'):
175 name = channel + '_port'
175 name = channel + '_port'
176 if getattr(self, name) == 0 and name in cfg:
176 if getattr(self, name) == 0 and name in cfg:
177 # not overridden by config or cl_args
177 # not overridden by config or cl_args
178 setattr(self, name, cfg[name])
178 setattr(self, name, cfg[name])
179 if 'key' in cfg:
179 if 'key' in cfg:
180 self.config.Session.key = str_to_bytes(cfg['key'])
180 self.config.Session.key = str_to_bytes(cfg['key'])
181
181
182 def write_connection_file(self):
182 def write_connection_file(self):
183 """write connection info to JSON file"""
183 """write connection info to JSON file"""
184 if os.path.basename(self.connection_file) == self.connection_file:
184 if os.path.basename(self.connection_file) == self.connection_file:
185 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
185 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
186 else:
186 else:
187 cf = self.connection_file
187 cf = self.connection_file
188 write_connection_file(cf, ip=self.ip, key=self.session.key,
188 write_connection_file(cf, ip=self.ip, key=self.session.key,
189 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
189 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
190 iopub_port=self.iopub_port)
190 iopub_port=self.iopub_port)
191
191
192 def init_connection_file(self):
192 def init_connection_file(self):
193 if not self.connection_file:
193 if not self.connection_file:
194 self.connection_file = "kernel-%s.json"%os.getpid()
194 self.connection_file = "kernel-%s.json"%os.getpid()
195 try:
195 try:
196 self.load_connection_file()
196 self.load_connection_file()
197 except Exception:
197 except Exception:
198 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
198 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
199 self.exit(1)
199 self.exit(1)
200
200
201 def init_sockets(self):
201 def init_sockets(self):
202 # Create a context, a session, and the kernel sockets.
202 # Create a context, a session, and the kernel sockets.
203 self.log.info("Starting the kernel at pid: %i", os.getpid())
203 self.log.info("Starting the kernel at pid: %i", os.getpid())
204 context = zmq.Context.instance()
204 context = zmq.Context.instance()
205 # Uncomment this to try closing the context.
205 # Uncomment this to try closing the context.
206 # atexit.register(context.term)
206 # atexit.register(context.term)
207
207
208 self.shell_socket = context.socket(zmq.ROUTER)
208 self.shell_socket = context.socket(zmq.ROUTER)
209 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
209 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
210 self.log.debug("shell ROUTER Channel on port: %i"%self.shell_port)
210 self.log.debug("shell ROUTER Channel on port: %i"%self.shell_port)
211
211
212 self.iopub_socket = context.socket(zmq.PUB)
212 self.iopub_socket = context.socket(zmq.PUB)
213 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
213 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
214 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
214 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
215
215
216 self.stdin_socket = context.socket(zmq.ROUTER)
216 self.stdin_socket = context.socket(zmq.ROUTER)
217 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
217 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
218 self.log.debug("stdin ROUTER Channel on port: %i"%self.stdin_port)
218 self.log.debug("stdin ROUTER Channel on port: %i"%self.stdin_port)
219
219
220 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
220 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
221 self.hb_port = self.heartbeat.port
221 self.hb_port = self.heartbeat.port
222 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
222 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
223
223
224 # Helper to make it easier to connect to an existing kernel.
224 # Helper to make it easier to connect to an existing kernel.
225 # set log-level to critical, to make sure it is output
225 # set log-level to critical, to make sure it is output
226 self.log.critical("To connect another client to this kernel, use:")
226 self.log.critical("To connect another client to this kernel, use:")
227
227
228 basename = os.path.basename(self.connection_file)
228 basename = os.path.basename(self.connection_file)
229 if basename == self.connection_file or \
229 if basename == self.connection_file or \
230 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
230 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
231 # use shortname
231 # use shortname
232 tail = basename
232 tail = basename
233 if self.profile != 'default':
233 if self.profile != 'default':
234 tail += " --profile %s" % self.profile
234 tail += " --profile %s" % self.profile
235 else:
235 else:
236 tail = self.connection_file
236 tail = self.connection_file
237 self.log.critical("--existing %s", tail)
237 self.log.critical("--existing %s", tail)
238
238
239
239
240 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
240 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
241 stdin=self.stdin_port, hb=self.hb_port)
241 stdin=self.stdin_port, hb=self.hb_port)
242
242
243 def init_session(self):
243 def init_session(self):
244 """create our session object"""
244 """create our session object"""
245 default_secure(self.config)
245 default_secure(self.config)
246 self.session = Session(config=self.config, username=u'kernel')
246 self.session = Session(config=self.config, username=u'kernel')
247
247
248 def init_blackhole(self):
248 def init_blackhole(self):
249 """redirects stdout/stderr to devnull if necessary"""
249 """redirects stdout/stderr to devnull if necessary"""
250 if self.no_stdout or self.no_stderr:
250 if self.no_stdout or self.no_stderr:
251 blackhole = file(os.devnull, 'w')
251 blackhole = file(os.devnull, 'w')
252 if self.no_stdout:
252 if self.no_stdout:
253 sys.stdout = sys.__stdout__ = blackhole
253 sys.stdout = sys.__stdout__ = blackhole
254 if self.no_stderr:
254 if self.no_stderr:
255 sys.stderr = sys.__stderr__ = blackhole
255 sys.stderr = sys.__stderr__ = blackhole
256
256
257 def init_io(self):
257 def init_io(self):
258 """Redirect input streams and set a display hook."""
258 """Redirect input streams and set a display hook."""
259 if self.outstream_class:
259 if self.outstream_class:
260 outstream_factory = import_item(str(self.outstream_class))
260 outstream_factory = import_item(str(self.outstream_class))
261 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
261 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
262 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
262 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
263 if self.displayhook_class:
263 if self.displayhook_class:
264 displayhook_factory = import_item(str(self.displayhook_class))
264 displayhook_factory = import_item(str(self.displayhook_class))
265 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
265 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
266
266
267 def init_kernel(self):
267 def init_kernel(self):
268 """Create the Kernel object itself"""
268 """Create the Kernel object itself"""
269 kernel_factory = import_item(str(self.kernel_class))
269 kernel_factory = import_item(str(self.kernel_class))
270 self.kernel = kernel_factory(config=self.config, session=self.session,
270 self.kernel = kernel_factory(config=self.config, session=self.session,
271 shell_socket=self.shell_socket,
271 shell_socket=self.shell_socket,
272 iopub_socket=self.iopub_socket,
272 iopub_socket=self.iopub_socket,
273 stdin_socket=self.stdin_socket,
273 stdin_socket=self.stdin_socket,
274 log=self.log
274 log=self.log
275 )
275 )
276 self.kernel.record_ports(self.ports)
276 self.kernel.record_ports(self.ports)
277
277
278 @catch_config_error
278 @catch_config_error
279 def initialize(self, argv=None):
279 def initialize(self, argv=None):
280 super(KernelApp, self).initialize(argv)
280 super(KernelApp, self).initialize(argv)
281 self.init_blackhole()
281 self.init_blackhole()
282 self.init_connection_file()
282 self.init_connection_file()
283 self.init_session()
283 self.init_session()
284 self.init_poller()
284 self.init_poller()
285 self.init_sockets()
285 self.init_sockets()
286 # writing connection file must be *after* init_sockets
286 # writing connection file must be *after* init_sockets
287 self.write_connection_file()
287 self.write_connection_file()
288 self.init_io()
288 self.init_io()
289 self.init_kernel()
289 self.init_kernel()
290 # flush stdout/stderr, so that anything written to these streams during
291 # initialization do not get associated with the first execution request
292 sys.stdout.flush()
293 sys.stderr.flush()
290
294
291 def start(self):
295 def start(self):
292 self.heartbeat.start()
296 self.heartbeat.start()
293 if self.poller is not None:
297 if self.poller is not None:
294 self.poller.start()
298 self.poller.start()
295 try:
299 try:
296 self.kernel.start()
300 self.kernel.start()
297 except KeyboardInterrupt:
301 except KeyboardInterrupt:
298 pass
302 pass
299
303
General Comments 0
You need to be logged in to leave comments. Login now