##// END OF EJS Templates
Renamed 'payload-svg' matplotlib backend to 'inline'.
Fernando Perez -
Show More
@@ -1,144 +1,144
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2 """
2 """
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # Systemm library imports
8 # Systemm library imports
9 from PyQt4 import QtGui
9 from PyQt4 import QtGui
10
10
11 # Local imports
11 # Local imports
12 from IPython.external.argparse import ArgumentParser
12 from IPython.external.argparse import ArgumentParser
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Constants
19 # Constants
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 LOCALHOST = '127.0.0.1'
22 LOCALHOST = '127.0.0.1'
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class MainWindow(QtGui.QMainWindow):
28 class MainWindow(QtGui.QMainWindow):
29
29
30 #---------------------------------------------------------------------------
30 #---------------------------------------------------------------------------
31 # 'object' interface
31 # 'object' interface
32 #---------------------------------------------------------------------------
32 #---------------------------------------------------------------------------
33
33
34 def __init__(self, frontend):
34 def __init__(self, frontend):
35 """ Create a MainWindow for the specified FrontendWidget.
35 """ Create a MainWindow for the specified FrontendWidget.
36 """
36 """
37 super(MainWindow, self).__init__()
37 super(MainWindow, self).__init__()
38 self._frontend = frontend
38 self._frontend = frontend
39 self._frontend.exit_requested.connect(self.close)
39 self._frontend.exit_requested.connect(self.close)
40 self.setCentralWidget(frontend)
40 self.setCentralWidget(frontend)
41
41
42 #---------------------------------------------------------------------------
42 #---------------------------------------------------------------------------
43 # QWidget interface
43 # QWidget interface
44 #---------------------------------------------------------------------------
44 #---------------------------------------------------------------------------
45
45
46 def closeEvent(self, event):
46 def closeEvent(self, event):
47 """ Reimplemented to prompt the user and close the kernel cleanly.
47 """ Reimplemented to prompt the user and close the kernel cleanly.
48 """
48 """
49 kernel_manager = self._frontend.kernel_manager
49 kernel_manager = self._frontend.kernel_manager
50 if kernel_manager and kernel_manager.channels_running:
50 if kernel_manager and kernel_manager.channels_running:
51 title = self.window().windowTitle()
51 title = self.window().windowTitle()
52 reply = QtGui.QMessageBox.question(self, title,
52 reply = QtGui.QMessageBox.question(self, title,
53 'Close console?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
53 'Close console?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
54 if reply == QtGui.QMessageBox.Yes:
54 if reply == QtGui.QMessageBox.Yes:
55 kernel_manager.shutdown_kernel()
55 kernel_manager.shutdown_kernel()
56 #kernel_manager.stop_channels()
56 #kernel_manager.stop_channels()
57 event.accept()
57 event.accept()
58 else:
58 else:
59 event.ignore()
59 event.ignore()
60
60
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 # Main entry point
62 # Main entry point
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64
64
65 def main():
65 def main():
66 """ Entry point for application.
66 """ Entry point for application.
67 """
67 """
68 # Parse command line arguments.
68 # Parse command line arguments.
69 parser = ArgumentParser()
69 parser = ArgumentParser()
70 kgroup = parser.add_argument_group('kernel options')
70 kgroup = parser.add_argument_group('kernel options')
71 kgroup.add_argument('-e', '--existing', action='store_true',
71 kgroup.add_argument('-e', '--existing', action='store_true',
72 help='connect to an existing kernel')
72 help='connect to an existing kernel')
73 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
73 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
74 help='set the kernel\'s IP address [default localhost]')
74 help='set the kernel\'s IP address [default localhost]')
75 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
75 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
76 help='set the XREQ channel port [default random]')
76 help='set the XREQ channel port [default random]')
77 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
77 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
78 help='set the SUB channel port [default random]')
78 help='set the SUB channel port [default random]')
79 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
79 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
80 help='set the REP channel port [default random]')
80 help='set the REP channel port [default random]')
81 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
81 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
82 help='set the heartbeat port [default: random]')
82 help='set the heartbeat port [default: random]')
83
83
84 egroup = kgroup.add_mutually_exclusive_group()
84 egroup = kgroup.add_mutually_exclusive_group()
85 egroup.add_argument('--pure', action='store_true', help = \
85 egroup.add_argument('--pure', action='store_true', help = \
86 'use a pure Python kernel instead of an IPython kernel')
86 'use a pure Python kernel instead of an IPython kernel')
87 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
87 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
88 const='auto', help = \
88 const='auto', help = \
89 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
89 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
90 given, the GUI backend is matplotlib's, otherwise use one of: \
90 given, the GUI backend is matplotlib's, otherwise use one of: \
91 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
91 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
92
92
93 wgroup = parser.add_argument_group('widget options')
93 wgroup = parser.add_argument_group('widget options')
94 wgroup.add_argument('--paging', type=str, default='inside',
94 wgroup.add_argument('--paging', type=str, default='inside',
95 choices = ['inside', 'hsplit', 'vsplit', 'none'],
95 choices = ['inside', 'hsplit', 'vsplit', 'none'],
96 help='set the paging style [default inside]')
96 help='set the paging style [default inside]')
97 wgroup.add_argument('--rich', action='store_true',
97 wgroup.add_argument('--rich', action='store_true',
98 help='enable rich text support')
98 help='enable rich text support')
99 wgroup.add_argument('--gui-completion', action='store_true',
99 wgroup.add_argument('--gui-completion', action='store_true',
100 help='use a GUI widget for tab completion')
100 help='use a GUI widget for tab completion')
101
101
102 args = parser.parse_args()
102 args = parser.parse_args()
103
103
104 # Don't let Qt or ZMQ swallow KeyboardInterupts.
104 # Don't let Qt or ZMQ swallow KeyboardInterupts.
105 import signal
105 import signal
106 signal.signal(signal.SIGINT, signal.SIG_DFL)
106 signal.signal(signal.SIGINT, signal.SIG_DFL)
107
107
108 # Create a KernelManager and start a kernel.
108 # Create a KernelManager and start a kernel.
109 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
109 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
110 sub_address=(args.ip, args.sub),
110 sub_address=(args.ip, args.sub),
111 rep_address=(args.ip, args.rep),
111 rep_address=(args.ip, args.rep),
112 hb_address=(args.ip, args.hb))
112 hb_address=(args.ip, args.hb))
113 if args.ip == LOCALHOST and not args.existing:
113 if args.ip == LOCALHOST and not args.existing:
114 if args.pure:
114 if args.pure:
115 kernel_manager.start_kernel(ipython=False)
115 kernel_manager.start_kernel(ipython=False)
116 elif args.pylab:
116 elif args.pylab:
117 kernel_manager.start_kernel(pylab=args.pylab)
117 kernel_manager.start_kernel(pylab=args.pylab)
118 else:
118 else:
119 kernel_manager.start_kernel()
119 kernel_manager.start_kernel()
120 kernel_manager.start_channels()
120 kernel_manager.start_channels()
121
121
122 # Create the widget.
122 # Create the widget.
123 app = QtGui.QApplication([])
123 app = QtGui.QApplication([])
124 if args.pure:
124 if args.pure:
125 kind = 'rich' if args.rich else 'plain'
125 kind = 'rich' if args.rich else 'plain'
126 widget = FrontendWidget(kind=kind, paging=args.paging)
126 widget = FrontendWidget(kind=kind, paging=args.paging)
127 elif args.rich or args.pylab:
127 elif args.rich or args.pylab:
128 widget = RichIPythonWidget(paging=args.paging)
128 widget = RichIPythonWidget(paging=args.paging)
129 else:
129 else:
130 widget = IPythonWidget(paging=args.paging)
130 widget = IPythonWidget(paging=args.paging)
131 widget.gui_completion = args.gui_completion
131 widget.gui_completion = args.gui_completion
132 widget.kernel_manager = kernel_manager
132 widget.kernel_manager = kernel_manager
133
133
134 # Create the main window.
134 # Create the main window.
135 window = MainWindow(widget)
135 window = MainWindow(widget)
136 window.setWindowTitle('Python' if args.pure else 'IPython')
136 window.setWindowTitle('Python' if args.pure else 'IPython')
137 window.show()
137 window.show()
138
138
139 # Start the application main loop.
139 # Start the application main loop.
140 app.exec_()
140 app.exec_()
141
141
142
142
143 if __name__ == '__main__':
143 if __name__ == '__main__':
144 main()
144 main()
@@ -1,196 +1,196
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 from IPython.utils.decorators import flag_calls
22 from IPython.utils.decorators import flag_calls
23
23
24 # If user specifies a GUI, that dictates the backend, otherwise we read the
24 # If user specifies a GUI, that dictates the backend, otherwise we read the
25 # user's mpl default from the mpl rc structure
25 # user's mpl default from the mpl rc structure
26 backends = {'tk': 'TkAgg',
26 backends = {'tk': 'TkAgg',
27 'gtk': 'GTKAgg',
27 'gtk': 'GTKAgg',
28 'wx': 'WXAgg',
28 'wx': 'WXAgg',
29 'qt': 'Qt4Agg', # qt3 not supported
29 'qt': 'Qt4Agg', # qt3 not supported
30 'qt4': 'Qt4Agg',
30 'qt4': 'Qt4Agg',
31 'payload-svg' : 'module://IPython.zmq.pylab.backend_payload_svg'}
31 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Main classes and functions
34 # Main classes and functions
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37
37
38 def find_gui_and_backend(gui=None):
38 def find_gui_and_backend(gui=None):
39 """Given a gui string return the gui and mpl backend.
39 """Given a gui string return the gui and mpl backend.
40
40
41 Parameters
41 Parameters
42 ----------
42 ----------
43 gui : str
43 gui : str
44 Can be one of ('tk','gtk','wx','qt','qt4','payload-svg').
44 Can be one of ('tk','gtk','wx','qt','qt4','inline').
45
45
46 Returns
46 Returns
47 -------
47 -------
48 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
48 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
49 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_payload_svg').
49 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
50 """
50 """
51
51
52 import matplotlib
52 import matplotlib
53
53
54 if gui:
54 if gui:
55 # select backend based on requested gui
55 # select backend based on requested gui
56 backend = backends[gui]
56 backend = backends[gui]
57 else:
57 else:
58 backend = matplotlib.rcParams['backend']
58 backend = matplotlib.rcParams['backend']
59 # In this case, we need to find what the appropriate gui selection call
59 # In this case, we need to find what the appropriate gui selection call
60 # should be for IPython, so we can activate inputhook accordingly
60 # should be for IPython, so we can activate inputhook accordingly
61 g2b = backends # maps gui names to mpl backend names
61 g2b = backends # maps gui names to mpl backend names
62 b2g = dict(zip(g2b.values(), g2b.keys())) # reverse dict
62 b2g = dict(zip(g2b.values(), g2b.keys())) # reverse dict
63 gui = b2g.get(backend, None)
63 gui = b2g.get(backend, None)
64 return gui, backend
64 return gui, backend
65
65
66
66
67 def activate_matplotlib(backend):
67 def activate_matplotlib(backend):
68 """Activate the given backend and set interactive to True."""
68 """Activate the given backend and set interactive to True."""
69
69
70 import matplotlib
70 import matplotlib
71 if backend.startswith('module://'):
71 if backend.startswith('module://'):
72 # Work around bug in matplotlib: matplotlib.use converts the
72 # Work around bug in matplotlib: matplotlib.use converts the
73 # backend_id to lowercase even if a module name is specified!
73 # backend_id to lowercase even if a module name is specified!
74 matplotlib.rcParams['backend'] = backend
74 matplotlib.rcParams['backend'] = backend
75 else:
75 else:
76 matplotlib.use(backend)
76 matplotlib.use(backend)
77 matplotlib.interactive(True)
77 matplotlib.interactive(True)
78
78
79 # This must be imported last in the matplotlib series, after
79 # This must be imported last in the matplotlib series, after
80 # backend/interactivity choices have been made
80 # backend/interactivity choices have been made
81 import matplotlib.pylab as pylab
81 import matplotlib.pylab as pylab
82
82
83 # XXX For now leave this commented out, but depending on discussions with
83 # XXX For now leave this commented out, but depending on discussions with
84 # mpl-dev, we may be able to allow interactive switching...
84 # mpl-dev, we may be able to allow interactive switching...
85 #import matplotlib.pyplot
85 #import matplotlib.pyplot
86 #matplotlib.pyplot.switch_backend(backend)
86 #matplotlib.pyplot.switch_backend(backend)
87
87
88 pylab.show._needmain = False
88 pylab.show._needmain = False
89 # We need to detect at runtime whether show() is called by the user.
89 # We need to detect at runtime whether show() is called by the user.
90 # For this, we wrap it into a decorator which adds a 'called' flag.
90 # For this, we wrap it into a decorator which adds a 'called' flag.
91 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
91 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
92
92
93
93
94 def import_pylab(user_ns, backend, import_all=True, shell=None):
94 def import_pylab(user_ns, backend, import_all=True, shell=None):
95 """Import the standard pylab symbols into user_ns."""
95 """Import the standard pylab symbols into user_ns."""
96
96
97 # Import numpy as np/pyplot as plt are conventions we're trying to
97 # Import numpy as np/pyplot as plt are conventions we're trying to
98 # somewhat standardize on. Making them available to users by default
98 # somewhat standardize on. Making them available to users by default
99 # will greatly help this.
99 # will greatly help this.
100 exec ("import numpy\n"
100 exec ("import numpy\n"
101 "import matplotlib\n"
101 "import matplotlib\n"
102 "from matplotlib import pylab, mlab, pyplot\n"
102 "from matplotlib import pylab, mlab, pyplot\n"
103 "np = numpy\n"
103 "np = numpy\n"
104 "plt = pyplot\n"
104 "plt = pyplot\n"
105 ) in user_ns
105 ) in user_ns
106
106
107 if shell is not None:
107 if shell is not None:
108 # If using our svg payload backend, register the post-execution
108 # If using our svg payload backend, register the post-execution
109 # function that will pick up the results for display. This can only be
109 # function that will pick up the results for display. This can only be
110 # done with access to the real shell object.
110 # done with access to the real shell object.
111 if backend == backends['payload-svg']:
111 if backend == backends['inline']:
112 from IPython.zmq.pylab.backend_payload_svg import flush_svg
112 from IPython.zmq.pylab.backend_inline import flush_svg
113 shell.register_post_execute(flush_svg)
113 shell.register_post_execute(flush_svg)
114 else:
114 else:
115 from IPython.zmq.pylab.backend_payload_svg import paste
115 from IPython.zmq.pylab.backend_inline import paste
116 from matplotlib import pyplot
116 from matplotlib import pyplot
117 # Add 'paste' to pyplot and to the user's namespace
117 # Add 'paste' to pyplot and to the user's namespace
118 user_ns['paste'] = pyplot.paste = paste
118 user_ns['paste'] = pyplot.paste = paste
119
119
120 if import_all:
120 if import_all:
121 exec("from matplotlib.pylab import *\n"
121 exec("from matplotlib.pylab import *\n"
122 "from numpy import *\n") in user_ns
122 "from numpy import *\n") in user_ns
123
123
124
124
125 def pylab_activate(user_ns, gui=None, import_all=True):
125 def pylab_activate(user_ns, gui=None, import_all=True):
126 """Activate pylab mode in the user's namespace.
126 """Activate pylab mode in the user's namespace.
127
127
128 Loads and initializes numpy, matplotlib and friends for interactive use.
128 Loads and initializes numpy, matplotlib and friends for interactive use.
129
129
130 Parameters
130 Parameters
131 ----------
131 ----------
132 user_ns : dict
132 user_ns : dict
133 Namespace where the imports will occur.
133 Namespace where the imports will occur.
134
134
135 gui : optional, string
135 gui : optional, string
136 A valid gui name following the conventions of the %gui magic.
136 A valid gui name following the conventions of the %gui magic.
137
137
138 import_all : optional, boolean
138 import_all : optional, boolean
139 If true, an 'import *' is done from numpy and pylab.
139 If true, an 'import *' is done from numpy and pylab.
140
140
141 Returns
141 Returns
142 -------
142 -------
143 The actual gui used (if not given as input, it was obtained from matplotlib
143 The actual gui used (if not given as input, it was obtained from matplotlib
144 itself, and will be needed next to configure IPython's gui integration.
144 itself, and will be needed next to configure IPython's gui integration.
145 """
145 """
146 gui, backend = find_gui_and_backend(gui)
146 gui, backend = find_gui_and_backend(gui)
147 activate_matplotlib(backend)
147 activate_matplotlib(backend)
148 import_pylab(user_ns, backend)
148 import_pylab(user_ns, backend)
149
149
150 print """
150 print """
151 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
151 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
152 For more information, type 'help(pylab)'.""" % backend
152 For more information, type 'help(pylab)'.""" % backend
153
153
154 return gui
154 return gui
155
155
156 # We need a little factory function here to create the closure where
156 # We need a little factory function here to create the closure where
157 # safe_execfile can live.
157 # safe_execfile can live.
158 def mpl_runner(safe_execfile):
158 def mpl_runner(safe_execfile):
159 """Factory to return a matplotlib-enabled runner for %run.
159 """Factory to return a matplotlib-enabled runner for %run.
160
160
161 Parameters
161 Parameters
162 ----------
162 ----------
163 safe_execfile : function
163 safe_execfile : function
164 This must be a function with the same interface as the
164 This must be a function with the same interface as the
165 :meth:`safe_execfile` method of IPython.
165 :meth:`safe_execfile` method of IPython.
166
166
167 Returns
167 Returns
168 -------
168 -------
169 A function suitable for use as the ``runner`` argument of the %run magic
169 A function suitable for use as the ``runner`` argument of the %run magic
170 function.
170 function.
171 """
171 """
172
172
173 def mpl_execfile(fname,*where,**kw):
173 def mpl_execfile(fname,*where,**kw):
174 """matplotlib-aware wrapper around safe_execfile.
174 """matplotlib-aware wrapper around safe_execfile.
175
175
176 Its interface is identical to that of the :func:`execfile` builtin.
176 Its interface is identical to that of the :func:`execfile` builtin.
177
177
178 This is ultimately a call to execfile(), but wrapped in safeties to
178 This is ultimately a call to execfile(), but wrapped in safeties to
179 properly handle interactive rendering."""
179 properly handle interactive rendering."""
180
180
181 import matplotlib
181 import matplotlib
182 import matplotlib.pylab as pylab
182 import matplotlib.pylab as pylab
183
183
184 #print '*** Matplotlib runner ***' # dbg
184 #print '*** Matplotlib runner ***' # dbg
185 # turn off rendering until end of script
185 # turn off rendering until end of script
186 is_interactive = matplotlib.rcParams['interactive']
186 is_interactive = matplotlib.rcParams['interactive']
187 matplotlib.interactive(False)
187 matplotlib.interactive(False)
188 safe_execfile(fname,*where,**kw)
188 safe_execfile(fname,*where,**kw)
189 matplotlib.interactive(is_interactive)
189 matplotlib.interactive(is_interactive)
190 # make rendering call now, if the user tried to do it
190 # make rendering call now, if the user tried to do it
191 if pylab.draw_if_interactive.called:
191 if pylab.draw_if_interactive.called:
192 pylab.draw()
192 pylab.draw()
193 pylab.draw_if_interactive.called = False
193 pylab.draw_if_interactive.called = False
194
194
195 return mpl_execfile
195 return mpl_execfile
196
196
@@ -1,586 +1,586
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24
24
25 # System library imports.
25 # System library imports.
26 import zmq
26 import zmq
27
27
28 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.utils import io
30 from IPython.utils import io
31 from IPython.utils.jsonutil import json_clean
31 from IPython.utils.jsonutil import json_clean
32 from IPython.lib import pylabtools
32 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
33 from IPython.utils.traitlets import Instance, Float
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 start_kernel)
35 start_kernel)
36 from iostream import OutStream
36 from iostream import OutStream
37 from session import Session, Message
37 from session import Session, Message
38 from zmqshell import ZMQInteractiveShell
38 from zmqshell import ZMQInteractiveShell
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Main kernel class
41 # Main kernel class
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 class Kernel(Configurable):
44 class Kernel(Configurable):
45
45
46 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
47 # Kernel interface
47 # Kernel interface
48 #---------------------------------------------------------------------------
48 #---------------------------------------------------------------------------
49
49
50 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
50 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
51 session = Instance(Session)
51 session = Instance(Session)
52 reply_socket = Instance('zmq.Socket')
52 reply_socket = Instance('zmq.Socket')
53 pub_socket = Instance('zmq.Socket')
53 pub_socket = Instance('zmq.Socket')
54 req_socket = Instance('zmq.Socket')
54 req_socket = Instance('zmq.Socket')
55
55
56 # Private interface
56 # Private interface
57
57
58 # Time to sleep after flushing the stdout/err buffers in each execute
58 # Time to sleep after flushing the stdout/err buffers in each execute
59 # cycle. While this introduces a hard limit on the minimal latency of the
59 # cycle. While this introduces a hard limit on the minimal latency of the
60 # execute cycle, it helps prevent output synchronization problems for
60 # execute cycle, it helps prevent output synchronization problems for
61 # clients.
61 # clients.
62 # Units are in seconds. The minimum zmq latency on local host is probably
62 # Units are in seconds. The minimum zmq latency on local host is probably
63 # ~150 microseconds, set this to 500us for now. We may need to increase it
63 # ~150 microseconds, set this to 500us for now. We may need to increase it
64 # a little if it's not enough after more interactive testing.
64 # a little if it's not enough after more interactive testing.
65 _execute_sleep = Float(0.0005, config=True)
65 _execute_sleep = Float(0.0005, config=True)
66
66
67 # Frequency of the kernel's event loop.
67 # Frequency of the kernel's event loop.
68 # Units are in seconds, kernel subclasses for GUI toolkits may need to
68 # Units are in seconds, kernel subclasses for GUI toolkits may need to
69 # adapt to milliseconds.
69 # adapt to milliseconds.
70 _poll_interval = Float(0.05, config=True)
70 _poll_interval = Float(0.05, config=True)
71
71
72 # If the shutdown was requested over the network, we leave here the
72 # If the shutdown was requested over the network, we leave here the
73 # necessary reply message so it can be sent by our registered atexit
73 # necessary reply message so it can be sent by our registered atexit
74 # handler. This ensures that the reply is only sent to clients truly at
74 # handler. This ensures that the reply is only sent to clients truly at
75 # the end of our shutdown process (which happens after the underlying
75 # the end of our shutdown process (which happens after the underlying
76 # IPython shell's own shutdown).
76 # IPython shell's own shutdown).
77 _shutdown_message = None
77 _shutdown_message = None
78
78
79 def __init__(self, **kwargs):
79 def __init__(self, **kwargs):
80 super(Kernel, self).__init__(**kwargs)
80 super(Kernel, self).__init__(**kwargs)
81
81
82 # Before we even start up the shell, register *first* our exit handlers
82 # Before we even start up the shell, register *first* our exit handlers
83 # so they come before the shell's
83 # so they come before the shell's
84 atexit.register(self._at_shutdown)
84 atexit.register(self._at_shutdown)
85
85
86 # Initialize the InteractiveShell subclass
86 # Initialize the InteractiveShell subclass
87 self.shell = ZMQInteractiveShell.instance()
87 self.shell = ZMQInteractiveShell.instance()
88 self.shell.displayhook.session = self.session
88 self.shell.displayhook.session = self.session
89 self.shell.displayhook.pub_socket = self.pub_socket
89 self.shell.displayhook.pub_socket = self.pub_socket
90
90
91 # TMP - hack while developing
91 # TMP - hack while developing
92 self.shell._reply_content = None
92 self.shell._reply_content = None
93
93
94 # Build dict of handlers for message types
94 # Build dict of handlers for message types
95 msg_types = [ 'execute_request', 'complete_request',
95 msg_types = [ 'execute_request', 'complete_request',
96 'object_info_request', 'history_request',
96 'object_info_request', 'history_request',
97 'shutdown_request']
97 'shutdown_request']
98 self.handlers = {}
98 self.handlers = {}
99 for msg_type in msg_types:
99 for msg_type in msg_types:
100 self.handlers[msg_type] = getattr(self, msg_type)
100 self.handlers[msg_type] = getattr(self, msg_type)
101
101
102 def do_one_iteration(self):
102 def do_one_iteration(self):
103 """Do one iteration of the kernel's evaluation loop.
103 """Do one iteration of the kernel's evaluation loop.
104 """
104 """
105 try:
105 try:
106 ident = self.reply_socket.recv(zmq.NOBLOCK)
106 ident = self.reply_socket.recv(zmq.NOBLOCK)
107 except zmq.ZMQError, e:
107 except zmq.ZMQError, e:
108 if e.errno == zmq.EAGAIN:
108 if e.errno == zmq.EAGAIN:
109 return
109 return
110 else:
110 else:
111 raise
111 raise
112 # FIXME: Bug in pyzmq/zmq?
112 # FIXME: Bug in pyzmq/zmq?
113 # assert self.reply_socket.rcvmore(), "Missing message part."
113 # assert self.reply_socket.rcvmore(), "Missing message part."
114 msg = self.reply_socket.recv_json()
114 msg = self.reply_socket.recv_json()
115
115
116 # Print some info about this message and leave a '--->' marker, so it's
116 # Print some info about this message and leave a '--->' marker, so it's
117 # easier to trace visually the message chain when debugging. Each
117 # easier to trace visually the message chain when debugging. Each
118 # handler prints its message at the end.
118 # handler prints its message at the end.
119 # Eventually we'll move these from stdout to a logger.
119 # Eventually we'll move these from stdout to a logger.
120 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
120 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
121 io.raw_print(' Content: ', msg['content'],
121 io.raw_print(' Content: ', msg['content'],
122 '\n --->\n ', sep='', end='')
122 '\n --->\n ', sep='', end='')
123
123
124 # Find and call actual handler for message
124 # Find and call actual handler for message
125 handler = self.handlers.get(msg['msg_type'], None)
125 handler = self.handlers.get(msg['msg_type'], None)
126 if handler is None:
126 if handler is None:
127 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
127 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
128 else:
128 else:
129 handler(ident, msg)
129 handler(ident, msg)
130
130
131 # Check whether we should exit, in case the incoming message set the
131 # Check whether we should exit, in case the incoming message set the
132 # exit flag on
132 # exit flag on
133 if self.shell.exit_now:
133 if self.shell.exit_now:
134 io.raw_print('\nExiting IPython kernel...')
134 io.raw_print('\nExiting IPython kernel...')
135 # We do a normal, clean exit, which allows any actions registered
135 # We do a normal, clean exit, which allows any actions registered
136 # via atexit (such as history saving) to take place.
136 # via atexit (such as history saving) to take place.
137 sys.exit(0)
137 sys.exit(0)
138
138
139
139
140 def start(self):
140 def start(self):
141 """ Start the kernel main loop.
141 """ Start the kernel main loop.
142 """
142 """
143 while True:
143 while True:
144 time.sleep(self._poll_interval)
144 time.sleep(self._poll_interval)
145 self.do_one_iteration()
145 self.do_one_iteration()
146
146
147 #---------------------------------------------------------------------------
147 #---------------------------------------------------------------------------
148 # Kernel request handlers
148 # Kernel request handlers
149 #---------------------------------------------------------------------------
149 #---------------------------------------------------------------------------
150
150
151 def _publish_pyin(self, code, parent):
151 def _publish_pyin(self, code, parent):
152 """Publish the code request on the pyin stream."""
152 """Publish the code request on the pyin stream."""
153
153
154 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
154 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
155 self.pub_socket.send_json(pyin_msg)
155 self.pub_socket.send_json(pyin_msg)
156
156
157 def execute_request(self, ident, parent):
157 def execute_request(self, ident, parent):
158 try:
158 try:
159 content = parent[u'content']
159 content = parent[u'content']
160 code = content[u'code']
160 code = content[u'code']
161 silent = content[u'silent']
161 silent = content[u'silent']
162 except:
162 except:
163 io.raw_print_err("Got bad msg: ")
163 io.raw_print_err("Got bad msg: ")
164 io.raw_print_err(Message(parent))
164 io.raw_print_err(Message(parent))
165 return
165 return
166
166
167 shell = self.shell # we'll need this a lot here
167 shell = self.shell # we'll need this a lot here
168
168
169 # Replace raw_input. Note that is not sufficient to replace
169 # Replace raw_input. Note that is not sufficient to replace
170 # raw_input in the user namespace.
170 # raw_input in the user namespace.
171 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
171 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
172 __builtin__.raw_input = raw_input
172 __builtin__.raw_input = raw_input
173
173
174 # Set the parent message of the display hook and out streams.
174 # Set the parent message of the display hook and out streams.
175 shell.displayhook.set_parent(parent)
175 shell.displayhook.set_parent(parent)
176 sys.stdout.set_parent(parent)
176 sys.stdout.set_parent(parent)
177 sys.stderr.set_parent(parent)
177 sys.stderr.set_parent(parent)
178
178
179 # Re-broadcast our input for the benefit of listening clients, and
179 # Re-broadcast our input for the benefit of listening clients, and
180 # start computing output
180 # start computing output
181 if not silent:
181 if not silent:
182 self._publish_pyin(code, parent)
182 self._publish_pyin(code, parent)
183
183
184 reply_content = {}
184 reply_content = {}
185 try:
185 try:
186 if silent:
186 if silent:
187 # runcode uses 'exec' mode, so no displayhook will fire, and it
187 # runcode uses 'exec' mode, so no displayhook will fire, and it
188 # doesn't call logging or history manipulations. Print
188 # doesn't call logging or history manipulations. Print
189 # statements in that code will obviously still execute.
189 # statements in that code will obviously still execute.
190 shell.runcode(code)
190 shell.runcode(code)
191 else:
191 else:
192 # FIXME: runlines calls the exception handler itself.
192 # FIXME: runlines calls the exception handler itself.
193 shell._reply_content = None
193 shell._reply_content = None
194
194
195 # For now leave this here until we're sure we can stop using it
195 # For now leave this here until we're sure we can stop using it
196 #shell.runlines(code)
196 #shell.runlines(code)
197
197
198 # Experimental: cell mode! Test more before turning into
198 # Experimental: cell mode! Test more before turning into
199 # default and removing the hacks around runlines.
199 # default and removing the hacks around runlines.
200 shell.run_cell(code)
200 shell.run_cell(code)
201 except:
201 except:
202 status = u'error'
202 status = u'error'
203 # FIXME: this code right now isn't being used yet by default,
203 # FIXME: this code right now isn't being used yet by default,
204 # because the runlines() call above directly fires off exception
204 # because the runlines() call above directly fires off exception
205 # reporting. This code, therefore, is only active in the scenario
205 # reporting. This code, therefore, is only active in the scenario
206 # where runlines itself has an unhandled exception. We need to
206 # where runlines itself has an unhandled exception. We need to
207 # uniformize this, for all exception construction to come from a
207 # uniformize this, for all exception construction to come from a
208 # single location in the codbase.
208 # single location in the codbase.
209 etype, evalue, tb = sys.exc_info()
209 etype, evalue, tb = sys.exc_info()
210 tb_list = traceback.format_exception(etype, evalue, tb)
210 tb_list = traceback.format_exception(etype, evalue, tb)
211 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
211 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
212 else:
212 else:
213 status = u'ok'
213 status = u'ok'
214
214
215 reply_content[u'status'] = status
215 reply_content[u'status'] = status
216 # Compute the execution counter so clients can display prompts
216 # Compute the execution counter so clients can display prompts
217 reply_content['execution_count'] = shell.displayhook.prompt_count
217 reply_content['execution_count'] = shell.displayhook.prompt_count
218
218
219 # FIXME - fish exception info out of shell, possibly left there by
219 # FIXME - fish exception info out of shell, possibly left there by
220 # runlines. We'll need to clean up this logic later.
220 # runlines. We'll need to clean up this logic later.
221 if shell._reply_content is not None:
221 if shell._reply_content is not None:
222 reply_content.update(shell._reply_content)
222 reply_content.update(shell._reply_content)
223
223
224 # At this point, we can tell whether the main code execution succeeded
224 # At this point, we can tell whether the main code execution succeeded
225 # or not. If it did, we proceed to evaluate user_variables/expressions
225 # or not. If it did, we proceed to evaluate user_variables/expressions
226 if reply_content['status'] == 'ok':
226 if reply_content['status'] == 'ok':
227 reply_content[u'user_variables'] = \
227 reply_content[u'user_variables'] = \
228 shell.get_user_variables(content[u'user_variables'])
228 shell.get_user_variables(content[u'user_variables'])
229 reply_content[u'user_expressions'] = \
229 reply_content[u'user_expressions'] = \
230 shell.eval_expressions(content[u'user_expressions'])
230 shell.eval_expressions(content[u'user_expressions'])
231 else:
231 else:
232 # If there was an error, don't even try to compute variables or
232 # If there was an error, don't even try to compute variables or
233 # expressions
233 # expressions
234 reply_content[u'user_variables'] = {}
234 reply_content[u'user_variables'] = {}
235 reply_content[u'user_expressions'] = {}
235 reply_content[u'user_expressions'] = {}
236
236
237 # Payloads should be retrieved regardless of outcome, so we can both
237 # Payloads should be retrieved regardless of outcome, so we can both
238 # recover partial output (that could have been generated early in a
238 # recover partial output (that could have been generated early in a
239 # block, before an error) and clear the payload system always.
239 # block, before an error) and clear the payload system always.
240 reply_content[u'payload'] = shell.payload_manager.read_payload()
240 reply_content[u'payload'] = shell.payload_manager.read_payload()
241 # Be agressive about clearing the payload because we don't want
241 # Be agressive about clearing the payload because we don't want
242 # it to sit in memory until the next execute_request comes in.
242 # it to sit in memory until the next execute_request comes in.
243 shell.payload_manager.clear_payload()
243 shell.payload_manager.clear_payload()
244
244
245 # Send the reply.
245 # Send the reply.
246 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
246 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
247 io.raw_print(reply_msg)
247 io.raw_print(reply_msg)
248
248
249 # Flush output before sending the reply.
249 # Flush output before sending the reply.
250 sys.stdout.flush()
250 sys.stdout.flush()
251 sys.stderr.flush()
251 sys.stderr.flush()
252 # FIXME: on rare occasions, the flush doesn't seem to make it to the
252 # FIXME: on rare occasions, the flush doesn't seem to make it to the
253 # clients... This seems to mitigate the problem, but we definitely need
253 # clients... This seems to mitigate the problem, but we definitely need
254 # to better understand what's going on.
254 # to better understand what's going on.
255 if self._execute_sleep:
255 if self._execute_sleep:
256 time.sleep(self._execute_sleep)
256 time.sleep(self._execute_sleep)
257
257
258 self.reply_socket.send(ident, zmq.SNDMORE)
258 self.reply_socket.send(ident, zmq.SNDMORE)
259 self.reply_socket.send_json(reply_msg)
259 self.reply_socket.send_json(reply_msg)
260 if reply_msg['content']['status'] == u'error':
260 if reply_msg['content']['status'] == u'error':
261 self._abort_queue()
261 self._abort_queue()
262
262
263 def complete_request(self, ident, parent):
263 def complete_request(self, ident, parent):
264 txt, matches = self._complete(parent)
264 txt, matches = self._complete(parent)
265 matches = {'matches' : matches,
265 matches = {'matches' : matches,
266 'matched_text' : txt,
266 'matched_text' : txt,
267 'status' : 'ok'}
267 'status' : 'ok'}
268 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
268 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
269 matches, parent, ident)
269 matches, parent, ident)
270 io.raw_print(completion_msg)
270 io.raw_print(completion_msg)
271
271
272 def object_info_request(self, ident, parent):
272 def object_info_request(self, ident, parent):
273 object_info = self.shell.object_inspect(parent['content']['oname'])
273 object_info = self.shell.object_inspect(parent['content']['oname'])
274 # Before we send this object over, we turn it into a dict and we scrub
274 # Before we send this object over, we turn it into a dict and we scrub
275 # it for JSON usage
275 # it for JSON usage
276 oinfo = json_clean(object_info._asdict())
276 oinfo = json_clean(object_info._asdict())
277 msg = self.session.send(self.reply_socket, 'object_info_reply',
277 msg = self.session.send(self.reply_socket, 'object_info_reply',
278 oinfo, parent, ident)
278 oinfo, parent, ident)
279 io.raw_print(msg)
279 io.raw_print(msg)
280
280
281 def history_request(self, ident, parent):
281 def history_request(self, ident, parent):
282 output = parent['content']['output']
282 output = parent['content']['output']
283 index = parent['content']['index']
283 index = parent['content']['index']
284 raw = parent['content']['raw']
284 raw = parent['content']['raw']
285 hist = self.shell.get_history(index=index, raw=raw, output=output)
285 hist = self.shell.get_history(index=index, raw=raw, output=output)
286 content = {'history' : hist}
286 content = {'history' : hist}
287 msg = self.session.send(self.reply_socket, 'history_reply',
287 msg = self.session.send(self.reply_socket, 'history_reply',
288 content, parent, ident)
288 content, parent, ident)
289 io.raw_print(msg)
289 io.raw_print(msg)
290
290
291 def shutdown_request(self, ident, parent):
291 def shutdown_request(self, ident, parent):
292 self.shell.exit_now = True
292 self.shell.exit_now = True
293 self._shutdown_message = self.session.msg(u'shutdown_reply', {}, parent)
293 self._shutdown_message = self.session.msg(u'shutdown_reply', {}, parent)
294 sys.exit(0)
294 sys.exit(0)
295
295
296 #---------------------------------------------------------------------------
296 #---------------------------------------------------------------------------
297 # Protected interface
297 # Protected interface
298 #---------------------------------------------------------------------------
298 #---------------------------------------------------------------------------
299
299
300 def _abort_queue(self):
300 def _abort_queue(self):
301 while True:
301 while True:
302 try:
302 try:
303 ident = self.reply_socket.recv(zmq.NOBLOCK)
303 ident = self.reply_socket.recv(zmq.NOBLOCK)
304 except zmq.ZMQError, e:
304 except zmq.ZMQError, e:
305 if e.errno == zmq.EAGAIN:
305 if e.errno == zmq.EAGAIN:
306 break
306 break
307 else:
307 else:
308 assert self.reply_socket.rcvmore(), \
308 assert self.reply_socket.rcvmore(), \
309 "Unexpected missing message part."
309 "Unexpected missing message part."
310 msg = self.reply_socket.recv_json()
310 msg = self.reply_socket.recv_json()
311 io.raw_print("Aborting:\n", Message(msg))
311 io.raw_print("Aborting:\n", Message(msg))
312 msg_type = msg['msg_type']
312 msg_type = msg['msg_type']
313 reply_type = msg_type.split('_')[0] + '_reply'
313 reply_type = msg_type.split('_')[0] + '_reply'
314 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
314 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
315 io.raw_print(reply_msg)
315 io.raw_print(reply_msg)
316 self.reply_socket.send(ident,zmq.SNDMORE)
316 self.reply_socket.send(ident,zmq.SNDMORE)
317 self.reply_socket.send_json(reply_msg)
317 self.reply_socket.send_json(reply_msg)
318 # We need to wait a bit for requests to come in. This can probably
318 # We need to wait a bit for requests to come in. This can probably
319 # be set shorter for true asynchronous clients.
319 # be set shorter for true asynchronous clients.
320 time.sleep(0.1)
320 time.sleep(0.1)
321
321
322 def _raw_input(self, prompt, ident, parent):
322 def _raw_input(self, prompt, ident, parent):
323 # Flush output before making the request.
323 # Flush output before making the request.
324 sys.stderr.flush()
324 sys.stderr.flush()
325 sys.stdout.flush()
325 sys.stdout.flush()
326
326
327 # Send the input request.
327 # Send the input request.
328 content = dict(prompt=prompt)
328 content = dict(prompt=prompt)
329 msg = self.session.msg(u'input_request', content, parent)
329 msg = self.session.msg(u'input_request', content, parent)
330 self.req_socket.send_json(msg)
330 self.req_socket.send_json(msg)
331
331
332 # Await a response.
332 # Await a response.
333 reply = self.req_socket.recv_json()
333 reply = self.req_socket.recv_json()
334 try:
334 try:
335 value = reply['content']['value']
335 value = reply['content']['value']
336 except:
336 except:
337 io.raw_print_err("Got bad raw_input reply: ")
337 io.raw_print_err("Got bad raw_input reply: ")
338 io.raw_print_err(Message(parent))
338 io.raw_print_err(Message(parent))
339 value = ''
339 value = ''
340 return value
340 return value
341
341
342 def _complete(self, msg):
342 def _complete(self, msg):
343 c = msg['content']
343 c = msg['content']
344 try:
344 try:
345 cpos = int(c['cursor_pos'])
345 cpos = int(c['cursor_pos'])
346 except:
346 except:
347 # If we don't get something that we can convert to an integer, at
347 # If we don't get something that we can convert to an integer, at
348 # least attempt the completion guessing the cursor is at the end of
348 # least attempt the completion guessing the cursor is at the end of
349 # the text, if there's any, and otherwise of the line
349 # the text, if there's any, and otherwise of the line
350 cpos = len(c['text'])
350 cpos = len(c['text'])
351 if cpos==0:
351 if cpos==0:
352 cpos = len(c['line'])
352 cpos = len(c['line'])
353 return self.shell.complete(c['text'], c['line'], cpos)
353 return self.shell.complete(c['text'], c['line'], cpos)
354
354
355 def _object_info(self, context):
355 def _object_info(self, context):
356 symbol, leftover = self._symbol_from_context(context)
356 symbol, leftover = self._symbol_from_context(context)
357 if symbol is not None and not leftover:
357 if symbol is not None and not leftover:
358 doc = getattr(symbol, '__doc__', '')
358 doc = getattr(symbol, '__doc__', '')
359 else:
359 else:
360 doc = ''
360 doc = ''
361 object_info = dict(docstring = doc)
361 object_info = dict(docstring = doc)
362 return object_info
362 return object_info
363
363
364 def _symbol_from_context(self, context):
364 def _symbol_from_context(self, context):
365 if not context:
365 if not context:
366 return None, context
366 return None, context
367
367
368 base_symbol_string = context[0]
368 base_symbol_string = context[0]
369 symbol = self.shell.user_ns.get(base_symbol_string, None)
369 symbol = self.shell.user_ns.get(base_symbol_string, None)
370 if symbol is None:
370 if symbol is None:
371 symbol = __builtin__.__dict__.get(base_symbol_string, None)
371 symbol = __builtin__.__dict__.get(base_symbol_string, None)
372 if symbol is None:
372 if symbol is None:
373 return None, context
373 return None, context
374
374
375 context = context[1:]
375 context = context[1:]
376 for i, name in enumerate(context):
376 for i, name in enumerate(context):
377 new_symbol = getattr(symbol, name, None)
377 new_symbol = getattr(symbol, name, None)
378 if new_symbol is None:
378 if new_symbol is None:
379 return symbol, context[i:]
379 return symbol, context[i:]
380 else:
380 else:
381 symbol = new_symbol
381 symbol = new_symbol
382
382
383 return symbol, []
383 return symbol, []
384
384
385 def _at_shutdown(self):
385 def _at_shutdown(self):
386 """Actions taken at shutdown by the kernel, called by python's atexit.
386 """Actions taken at shutdown by the kernel, called by python's atexit.
387 """
387 """
388 # io.rprint("Kernel at_shutdown") # dbg
388 # io.rprint("Kernel at_shutdown") # dbg
389 if self._shutdown_message is not None:
389 if self._shutdown_message is not None:
390 self.reply_socket.send_json(self._shutdown_message)
390 self.reply_socket.send_json(self._shutdown_message)
391 io.raw_print(self._shutdown_message)
391 io.raw_print(self._shutdown_message)
392 # A very short sleep to give zmq time to flush its message buffers
392 # A very short sleep to give zmq time to flush its message buffers
393 # before Python truly shuts down.
393 # before Python truly shuts down.
394 time.sleep(0.01)
394 time.sleep(0.01)
395
395
396
396
397 class QtKernel(Kernel):
397 class QtKernel(Kernel):
398 """A Kernel subclass with Qt support."""
398 """A Kernel subclass with Qt support."""
399
399
400 def start(self):
400 def start(self):
401 """Start a kernel with QtPy4 event loop integration."""
401 """Start a kernel with QtPy4 event loop integration."""
402
402
403 from PyQt4 import QtCore
403 from PyQt4 import QtCore
404 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
404 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
405
405
406 self.app = get_app_qt4([" "])
406 self.app = get_app_qt4([" "])
407 self.app.setQuitOnLastWindowClosed(False)
407 self.app.setQuitOnLastWindowClosed(False)
408 self.timer = QtCore.QTimer()
408 self.timer = QtCore.QTimer()
409 self.timer.timeout.connect(self.do_one_iteration)
409 self.timer.timeout.connect(self.do_one_iteration)
410 # Units for the timer are in milliseconds
410 # Units for the timer are in milliseconds
411 self.timer.start(1000*self._poll_interval)
411 self.timer.start(1000*self._poll_interval)
412 start_event_loop_qt4(self.app)
412 start_event_loop_qt4(self.app)
413
413
414
414
415 class WxKernel(Kernel):
415 class WxKernel(Kernel):
416 """A Kernel subclass with Wx support."""
416 """A Kernel subclass with Wx support."""
417
417
418 def start(self):
418 def start(self):
419 """Start a kernel with wx event loop support."""
419 """Start a kernel with wx event loop support."""
420
420
421 import wx
421 import wx
422 from IPython.lib.guisupport import start_event_loop_wx
422 from IPython.lib.guisupport import start_event_loop_wx
423
423
424 doi = self.do_one_iteration
424 doi = self.do_one_iteration
425 # Wx uses milliseconds
425 # Wx uses milliseconds
426 poll_interval = int(1000*self._poll_interval)
426 poll_interval = int(1000*self._poll_interval)
427
427
428 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
428 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
429 # We make the Frame hidden when we create it in the main app below.
429 # We make the Frame hidden when we create it in the main app below.
430 class TimerFrame(wx.Frame):
430 class TimerFrame(wx.Frame):
431 def __init__(self, func):
431 def __init__(self, func):
432 wx.Frame.__init__(self, None, -1)
432 wx.Frame.__init__(self, None, -1)
433 self.timer = wx.Timer(self)
433 self.timer = wx.Timer(self)
434 # Units for the timer are in milliseconds
434 # Units for the timer are in milliseconds
435 self.timer.Start(poll_interval)
435 self.timer.Start(poll_interval)
436 self.Bind(wx.EVT_TIMER, self.on_timer)
436 self.Bind(wx.EVT_TIMER, self.on_timer)
437 self.func = func
437 self.func = func
438
438
439 def on_timer(self, event):
439 def on_timer(self, event):
440 self.func()
440 self.func()
441
441
442 # We need a custom wx.App to create our Frame subclass that has the
442 # We need a custom wx.App to create our Frame subclass that has the
443 # wx.Timer to drive the ZMQ event loop.
443 # wx.Timer to drive the ZMQ event loop.
444 class IPWxApp(wx.App):
444 class IPWxApp(wx.App):
445 def OnInit(self):
445 def OnInit(self):
446 self.frame = TimerFrame(doi)
446 self.frame = TimerFrame(doi)
447 self.frame.Show(False)
447 self.frame.Show(False)
448 return True
448 return True
449
449
450 # The redirect=False here makes sure that wx doesn't replace
450 # The redirect=False here makes sure that wx doesn't replace
451 # sys.stdout/stderr with its own classes.
451 # sys.stdout/stderr with its own classes.
452 self.app = IPWxApp(redirect=False)
452 self.app = IPWxApp(redirect=False)
453 start_event_loop_wx(self.app)
453 start_event_loop_wx(self.app)
454
454
455
455
456 class TkKernel(Kernel):
456 class TkKernel(Kernel):
457 """A Kernel subclass with Tk support."""
457 """A Kernel subclass with Tk support."""
458
458
459 def start(self):
459 def start(self):
460 """Start a Tk enabled event loop."""
460 """Start a Tk enabled event loop."""
461
461
462 import Tkinter
462 import Tkinter
463 doi = self.do_one_iteration
463 doi = self.do_one_iteration
464 # Tk uses milliseconds
464 # Tk uses milliseconds
465 poll_interval = int(1000*self._poll_interval)
465 poll_interval = int(1000*self._poll_interval)
466 # For Tkinter, we create a Tk object and call its withdraw method.
466 # For Tkinter, we create a Tk object and call its withdraw method.
467 class Timer(object):
467 class Timer(object):
468 def __init__(self, func):
468 def __init__(self, func):
469 self.app = Tkinter.Tk()
469 self.app = Tkinter.Tk()
470 self.app.withdraw()
470 self.app.withdraw()
471 self.func = func
471 self.func = func
472
472
473 def on_timer(self):
473 def on_timer(self):
474 self.func()
474 self.func()
475 self.app.after(poll_interval, self.on_timer)
475 self.app.after(poll_interval, self.on_timer)
476
476
477 def start(self):
477 def start(self):
478 self.on_timer() # Call it once to get things going.
478 self.on_timer() # Call it once to get things going.
479 self.app.mainloop()
479 self.app.mainloop()
480
480
481 self.timer = Timer(doi)
481 self.timer = Timer(doi)
482 self.timer.start()
482 self.timer.start()
483
483
484
484
485 class GTKKernel(Kernel):
485 class GTKKernel(Kernel):
486 """A Kernel subclass with GTK support."""
486 """A Kernel subclass with GTK support."""
487
487
488 def start(self):
488 def start(self):
489 """Start the kernel, coordinating with the GTK event loop"""
489 """Start the kernel, coordinating with the GTK event loop"""
490 from .gui.gtkembed import GTKEmbed
490 from .gui.gtkembed import GTKEmbed
491
491
492 gtk_kernel = GTKEmbed(self)
492 gtk_kernel = GTKEmbed(self)
493 gtk_kernel.start()
493 gtk_kernel.start()
494
494
495
495
496 #-----------------------------------------------------------------------------
496 #-----------------------------------------------------------------------------
497 # Kernel main and launch functions
497 # Kernel main and launch functions
498 #-----------------------------------------------------------------------------
498 #-----------------------------------------------------------------------------
499
499
500 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
500 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
501 independent=False, pylab=False):
501 independent=False, pylab=False):
502 """Launches a localhost kernel, binding to the specified ports.
502 """Launches a localhost kernel, binding to the specified ports.
503
503
504 Parameters
504 Parameters
505 ----------
505 ----------
506 xrep_port : int, optional
506 xrep_port : int, optional
507 The port to use for XREP channel.
507 The port to use for XREP channel.
508
508
509 pub_port : int, optional
509 pub_port : int, optional
510 The port to use for the SUB channel.
510 The port to use for the SUB channel.
511
511
512 req_port : int, optional
512 req_port : int, optional
513 The port to use for the REQ (raw input) channel.
513 The port to use for the REQ (raw input) channel.
514
514
515 hb_port : int, optional
515 hb_port : int, optional
516 The port to use for the hearbeat REP channel.
516 The port to use for the hearbeat REP channel.
517
517
518 independent : bool, optional (default False)
518 independent : bool, optional (default False)
519 If set, the kernel process is guaranteed to survive if this process
519 If set, the kernel process is guaranteed to survive if this process
520 dies. If not set, an effort is made to ensure that the kernel is killed
520 dies. If not set, an effort is made to ensure that the kernel is killed
521 when this process dies. Note that in this case it is still good practice
521 when this process dies. Note that in this case it is still good practice
522 to kill kernels manually before exiting.
522 to kill kernels manually before exiting.
523
523
524 pylab : bool or string, optional (default False)
524 pylab : bool or string, optional (default False)
525 If not False, the kernel will be launched with pylab enabled. If a
525 If not False, the kernel will be launched with pylab enabled. If a
526 string is passed, matplotlib will use the specified backend. Otherwise,
526 string is passed, matplotlib will use the specified backend. Otherwise,
527 matplotlib's default backend will be used.
527 matplotlib's default backend will be used.
528
528
529 Returns
529 Returns
530 -------
530 -------
531 A tuple of form:
531 A tuple of form:
532 (kernel_process, xrep_port, pub_port, req_port)
532 (kernel_process, xrep_port, pub_port, req_port)
533 where kernel_process is a Popen object and the ports are integers.
533 where kernel_process is a Popen object and the ports are integers.
534 """
534 """
535 extra_arguments = []
535 extra_arguments = []
536 if pylab:
536 if pylab:
537 extra_arguments.append('--pylab')
537 extra_arguments.append('--pylab')
538 if isinstance(pylab, basestring):
538 if isinstance(pylab, basestring):
539 extra_arguments.append(pylab)
539 extra_arguments.append(pylab)
540 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
540 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
541 xrep_port, pub_port, req_port, hb_port,
541 xrep_port, pub_port, req_port, hb_port,
542 independent, extra_arguments)
542 independent, extra_arguments)
543
543
544
544
545 def main():
545 def main():
546 """ The IPython kernel main entry point.
546 """ The IPython kernel main entry point.
547 """
547 """
548 parser = make_argument_parser()
548 parser = make_argument_parser()
549 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
549 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
550 const='auto', help = \
550 const='auto', help = \
551 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
551 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
552 given, the GUI backend is matplotlib's, otherwise use one of: \
552 given, the GUI backend is matplotlib's, otherwise use one of: \
553 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
553 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
554 namespace = parser.parse_args()
554 namespace = parser.parse_args()
555
555
556 kernel_class = Kernel
556 kernel_class = Kernel
557
557
558 kernel_classes = {
558 kernel_classes = {
559 'qt' : QtKernel,
559 'qt' : QtKernel,
560 'qt4': QtKernel,
560 'qt4': QtKernel,
561 'payload-svg': Kernel,
561 'inline': Kernel,
562 'wx' : WxKernel,
562 'wx' : WxKernel,
563 'tk' : TkKernel,
563 'tk' : TkKernel,
564 'gtk': GTKKernel,
564 'gtk': GTKKernel,
565 }
565 }
566 if namespace.pylab:
566 if namespace.pylab:
567 if namespace.pylab == 'auto':
567 if namespace.pylab == 'auto':
568 gui, backend = pylabtools.find_gui_and_backend()
568 gui, backend = pylabtools.find_gui_and_backend()
569 else:
569 else:
570 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
570 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
571 kernel_class = kernel_classes.get(gui)
571 kernel_class = kernel_classes.get(gui)
572 if kernel_class is None:
572 if kernel_class is None:
573 raise ValueError('GUI is not supported: %r' % gui)
573 raise ValueError('GUI is not supported: %r' % gui)
574 pylabtools.activate_matplotlib(backend)
574 pylabtools.activate_matplotlib(backend)
575
575
576 kernel = make_kernel(namespace, kernel_class, OutStream)
576 kernel = make_kernel(namespace, kernel_class, OutStream)
577
577
578 if namespace.pylab:
578 if namespace.pylab:
579 pylabtools.import_pylab(kernel.shell.user_ns, backend,
579 pylabtools.import_pylab(kernel.shell.user_ns, backend,
580 shell=kernel.shell)
580 shell=kernel.shell)
581
581
582 start_kernel(namespace, kernel)
582 start_kernel(namespace, kernel)
583
583
584
584
585 if __name__ == '__main__':
585 if __name__ == '__main__':
586 main()
586 main()
1 NO CONTENT: file renamed from IPython/zmq/pylab/backend_payload_svg.py to IPython/zmq/pylab/backend_inline.py
NO CONTENT: file renamed from IPython/zmq/pylab/backend_payload_svg.py to IPython/zmq/pylab/backend_inline.py
General Comments 0
You need to be logged in to leave comments. Login now