##// END OF EJS Templates
Merge pull request #7261 from otakucode/master...
Thomas Kluyver -
r19631:7ab0a492 merge
parent child Browse files
Show More
@@ -1,487 +1,486 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
12 """
12 """
13
13
14 # Copyright (c) IPython Development Team.
14 # Copyright (c) IPython Development Team.
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16
16
17 from __future__ import print_function
17 from __future__ import print_function
18
18
19 import os
19 import os
20 import sys
20 import sys
21 import time
21 import time
22
22
23 from zmq.eventloop import ioloop
23 from zmq.eventloop import ioloop
24
24
25 from IPython.core.interactiveshell import (
25 from IPython.core.interactiveshell import (
26 InteractiveShell, InteractiveShellABC
26 InteractiveShell, InteractiveShellABC
27 )
27 )
28 from IPython.core import page
28 from IPython.core import page
29 from IPython.core.autocall import ZMQExitAutocall
29 from IPython.core.autocall import ZMQExitAutocall
30 from IPython.core.displaypub import DisplayPublisher
30 from IPython.core.displaypub import DisplayPublisher
31 from IPython.core.error import UsageError
31 from IPython.core.error import UsageError
32 from IPython.core.magics import MacroToEdit, CodeMagics
32 from IPython.core.magics import MacroToEdit, CodeMagics
33 from IPython.core.magic import magics_class, line_magic, Magics
33 from IPython.core.magic import magics_class, line_magic, Magics
34 from IPython.core import payloadpage
34 from IPython.core import payloadpage
35 from IPython.core.usage import default_gui_banner
35 from IPython.core.usage import default_gui_banner
36 from IPython.display import display, Javascript
36 from IPython.display import display, Javascript
37 from IPython.kernel.inprocess.socket import SocketABC
37 from IPython.kernel.inprocess.socket import SocketABC
38 from IPython.kernel import (
38 from IPython.kernel import (
39 get_connection_file, get_connection_info, connect_qtconsole
39 get_connection_file, get_connection_info, connect_qtconsole
40 )
40 )
41 from IPython.testing.skipdoctest import skip_doctest
41 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.utils import openpy
42 from IPython.utils import openpy
43 from IPython.utils.jsonutil import json_clean, encode_images
43 from IPython.utils.jsonutil import json_clean, encode_images
44 from IPython.utils.process import arg_split
44 from IPython.utils.process import arg_split
45 from IPython.utils import py3compat
45 from IPython.utils import py3compat
46 from IPython.utils.py3compat import unicode_type
46 from IPython.utils.py3compat import unicode_type
47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes, Any
47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes, Any
48 from IPython.utils.warn import error
48 from IPython.utils.warn import error
49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
51 from IPython.kernel.zmq.session import extract_header
51 from IPython.kernel.zmq.session import extract_header
52 from .session import Session
52 from .session import Session
53
53
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Functions and classes
55 # Functions and classes
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57
57
58 class ZMQDisplayPublisher(DisplayPublisher):
58 class ZMQDisplayPublisher(DisplayPublisher):
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
60
60
61 session = Instance(Session)
61 session = Instance(Session)
62 pub_socket = Instance(SocketABC)
62 pub_socket = Instance(SocketABC)
63 parent_header = Dict({})
63 parent_header = Dict({})
64 topic = CBytes(b'display_data')
64 topic = CBytes(b'display_data')
65
65
66 def set_parent(self, parent):
66 def set_parent(self, parent):
67 """Set the parent for outbound messages."""
67 """Set the parent for outbound messages."""
68 self.parent_header = extract_header(parent)
68 self.parent_header = extract_header(parent)
69
69
70 def _flush_streams(self):
70 def _flush_streams(self):
71 """flush IO Streams prior to display"""
71 """flush IO Streams prior to display"""
72 sys.stdout.flush()
72 sys.stdout.flush()
73 sys.stderr.flush()
73 sys.stderr.flush()
74
74
75 def publish(self, data, metadata=None, source=None):
75 def publish(self, data, metadata=None, source=None):
76 self._flush_streams()
76 self._flush_streams()
77 if metadata is None:
77 if metadata is None:
78 metadata = {}
78 metadata = {}
79 self._validate_data(data, metadata)
79 self._validate_data(data, metadata)
80 content = {}
80 content = {}
81 content['data'] = encode_images(data)
81 content['data'] = encode_images(data)
82 content['metadata'] = metadata
82 content['metadata'] = metadata
83 self.session.send(
83 self.session.send(
84 self.pub_socket, u'display_data', json_clean(content),
84 self.pub_socket, u'display_data', json_clean(content),
85 parent=self.parent_header, ident=self.topic,
85 parent=self.parent_header, ident=self.topic,
86 )
86 )
87
87
88 def clear_output(self, wait=False):
88 def clear_output(self, wait=False):
89 content = dict(wait=wait)
89 content = dict(wait=wait)
90 self._flush_streams()
90 self._flush_streams()
91 self.session.send(
91 self.session.send(
92 self.pub_socket, u'clear_output', content,
92 self.pub_socket, u'clear_output', content,
93 parent=self.parent_header, ident=self.topic,
93 parent=self.parent_header, ident=self.topic,
94 )
94 )
95
95
96 @magics_class
96 @magics_class
97 class KernelMagics(Magics):
97 class KernelMagics(Magics):
98 #------------------------------------------------------------------------
98 #------------------------------------------------------------------------
99 # Magic overrides
99 # Magic overrides
100 #------------------------------------------------------------------------
100 #------------------------------------------------------------------------
101 # Once the base class stops inheriting from magic, this code needs to be
101 # Once the base class stops inheriting from magic, this code needs to be
102 # moved into a separate machinery as well. For now, at least isolate here
102 # moved into a separate machinery as well. For now, at least isolate here
103 # the magics which this class needs to implement differently from the base
103 # the magics which this class needs to implement differently from the base
104 # class, or that are unique to it.
104 # class, or that are unique to it.
105
105
106 _find_edit_target = CodeMagics._find_edit_target
106 _find_edit_target = CodeMagics._find_edit_target
107
107
108 @skip_doctest
108 @skip_doctest
109 @line_magic
109 @line_magic
110 def edit(self, parameter_s='', last_call=['','']):
110 def edit(self, parameter_s='', last_call=['','']):
111 """Bring up an editor and execute the resulting code.
111 """Bring up an editor and execute the resulting code.
112
112
113 Usage:
113 Usage:
114 %edit [options] [args]
114 %edit [options] [args]
115
115
116 %edit runs an external text editor. You will need to set the command for
116 %edit runs an external text editor. You will need to set the command for
117 this editor via the ``TerminalInteractiveShell.editor`` option in your
117 this editor via the ``TerminalInteractiveShell.editor`` option in your
118 configuration file before it will work.
118 configuration file before it will work.
119
119
120 This command allows you to conveniently edit multi-line code right in
120 This command allows you to conveniently edit multi-line code right in
121 your IPython session.
121 your IPython session.
122
122
123 If called without arguments, %edit opens up an empty editor with a
123 If called without arguments, %edit opens up an empty editor with a
124 temporary file and will execute the contents of this file when you
124 temporary file and will execute the contents of this file when you
125 close it (don't forget to save it!).
125 close it (don't forget to save it!).
126
126
127 Options:
127 Options:
128
128
129 -n <number>
129 -n <number>
130 Open the editor at a specified line number. By default, the IPython
130 Open the editor at a specified line number. By default, the IPython
131 editor hook uses the unix syntax 'editor +N filename', but you can
131 editor hook uses the unix syntax 'editor +N filename', but you can
132 configure this by providing your own modified hook if your favorite
132 configure this by providing your own modified hook if your favorite
133 editor supports line-number specifications with a different syntax.
133 editor supports line-number specifications with a different syntax.
134
134
135 -p
135 -p
136 Call the editor with the same data as the previous time it was used,
136 Call the editor with the same data as the previous time it was used,
137 regardless of how long ago (in your current session) it was.
137 regardless of how long ago (in your current session) it was.
138
138
139 -r
139 -r
140 Use 'raw' input. This option only applies to input taken from the
140 Use 'raw' input. This option only applies to input taken from the
141 user's history. By default, the 'processed' history is used, so that
141 user's history. By default, the 'processed' history is used, so that
142 magics are loaded in their transformed version to valid Python. If
142 magics are loaded in their transformed version to valid Python. If
143 this option is given, the raw input as typed as the command line is
143 this option is given, the raw input as typed as the command line is
144 used instead. When you exit the editor, it will be executed by
144 used instead. When you exit the editor, it will be executed by
145 IPython's own processor.
145 IPython's own processor.
146
146
147 Arguments:
147 Arguments:
148
148
149 If arguments are given, the following possibilites exist:
149 If arguments are given, the following possibilites exist:
150
150
151 - The arguments are numbers or pairs of colon-separated numbers (like
151 - The arguments are numbers or pairs of colon-separated numbers (like
152 1 4:8 9). These are interpreted as lines of previous input to be
152 1 4:8 9). These are interpreted as lines of previous input to be
153 loaded into the editor. The syntax is the same of the %macro command.
153 loaded into the editor. The syntax is the same of the %macro command.
154
154
155 - If the argument doesn't start with a number, it is evaluated as a
155 - If the argument doesn't start with a number, it is evaluated as a
156 variable and its contents loaded into the editor. You can thus edit
156 variable and its contents loaded into the editor. You can thus edit
157 any string which contains python code (including the result of
157 any string which contains python code (including the result of
158 previous edits).
158 previous edits).
159
159
160 - If the argument is the name of an object (other than a string),
160 - If the argument is the name of an object (other than a string),
161 IPython will try to locate the file where it was defined and open the
161 IPython will try to locate the file where it was defined and open the
162 editor at the point where it is defined. You can use ``%edit function``
162 editor at the point where it is defined. You can use ``%edit function``
163 to load an editor exactly at the point where 'function' is defined,
163 to load an editor exactly at the point where 'function' is defined,
164 edit it and have the file be executed automatically.
164 edit it and have the file be executed automatically.
165
165
166 If the object is a macro (see %macro for details), this opens up your
166 If the object is a macro (see %macro for details), this opens up your
167 specified editor with a temporary file containing the macro's data.
167 specified editor with a temporary file containing the macro's data.
168 Upon exit, the macro is reloaded with the contents of the file.
168 Upon exit, the macro is reloaded with the contents of the file.
169
169
170 Note: opening at an exact line is only supported under Unix, and some
170 Note: opening at an exact line is only supported under Unix, and some
171 editors (like kedit and gedit up to Gnome 2.8) do not understand the
171 editors (like kedit and gedit up to Gnome 2.8) do not understand the
172 '+NUMBER' parameter necessary for this feature. Good editors like
172 '+NUMBER' parameter necessary for this feature. Good editors like
173 (X)Emacs, vi, jed, pico and joe all do.
173 (X)Emacs, vi, jed, pico and joe all do.
174
174
175 - If the argument is not found as a variable, IPython will look for a
175 - If the argument is not found as a variable, IPython will look for a
176 file with that name (adding .py if necessary) and load it into the
176 file with that name (adding .py if necessary) and load it into the
177 editor. It will execute its contents with execfile() when you exit,
177 editor. It will execute its contents with execfile() when you exit,
178 loading any code in the file into your interactive namespace.
178 loading any code in the file into your interactive namespace.
179
179
180 Unlike in the terminal, this is designed to use a GUI editor, and we do
180 Unlike in the terminal, this is designed to use a GUI editor, and we do
181 not know when it has closed. So the file you edit will not be
181 not know when it has closed. So the file you edit will not be
182 automatically executed or printed.
182 automatically executed or printed.
183
183
184 Note that %edit is also available through the alias %ed.
184 Note that %edit is also available through the alias %ed.
185 """
185 """
186
186
187 opts,args = self.parse_options(parameter_s,'prn:')
187 opts,args = self.parse_options(parameter_s,'prn:')
188
188
189 try:
189 try:
190 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
190 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
191 except MacroToEdit as e:
191 except MacroToEdit as e:
192 # TODO: Implement macro editing over 2 processes.
192 # TODO: Implement macro editing over 2 processes.
193 print("Macro editing not yet implemented in 2-process model.")
193 print("Macro editing not yet implemented in 2-process model.")
194 return
194 return
195
195
196 # Make sure we send to the client an absolute path, in case the working
196 # Make sure we send to the client an absolute path, in case the working
197 # directory of client and kernel don't match
197 # directory of client and kernel don't match
198 filename = os.path.abspath(filename)
198 filename = os.path.abspath(filename)
199
199
200 payload = {
200 payload = {
201 'source' : 'edit_magic',
201 'source' : 'edit_magic',
202 'filename' : filename,
202 'filename' : filename,
203 'line_number' : lineno
203 'line_number' : lineno
204 }
204 }
205 self.shell.payload_manager.write_payload(payload)
205 self.shell.payload_manager.write_payload(payload)
206
206
207 # A few magics that are adapted to the specifics of using pexpect and a
207 # A few magics that are adapted to the specifics of using pexpect and a
208 # remote terminal
208 # remote terminal
209
209
210 @line_magic
210 @line_magic
211 def clear(self, arg_s):
211 def clear(self, arg_s):
212 """Clear the terminal."""
212 """Clear the terminal."""
213 if os.name == 'posix':
213 if os.name == 'posix':
214 self.shell.system("clear")
214 self.shell.system("clear")
215 else:
215 else:
216 self.shell.system("cls")
216 self.shell.system("cls")
217
217
218 if os.name == 'nt':
218 if os.name == 'nt':
219 # This is the usual name in windows
219 # This is the usual name in windows
220 cls = line_magic('cls')(clear)
220 cls = line_magic('cls')(clear)
221
221
222 # Terminal pagers won't work over pexpect, but we do have our own pager
222 # Terminal pagers won't work over pexpect, but we do have our own pager
223
223
224 @line_magic
224 @line_magic
225 def less(self, arg_s):
225 def less(self, arg_s):
226 """Show a file through the pager.
226 """Show a file through the pager.
227
227
228 Files ending in .py are syntax-highlighted."""
228 Files ending in .py are syntax-highlighted."""
229 if not arg_s:
229 if not arg_s:
230 raise UsageError('Missing filename.')
230 raise UsageError('Missing filename.')
231
231
232 cont = open(arg_s).read()
233 if arg_s.endswith('.py'):
232 if arg_s.endswith('.py'):
234 cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False))
233 cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False))
235 else:
234 else:
236 cont = open(arg_s).read()
235 cont = open(arg_s).read()
237 page.page(cont)
236 page.page(cont)
238
237
239 more = line_magic('more')(less)
238 more = line_magic('more')(less)
240
239
241 # Man calls a pager, so we also need to redefine it
240 # Man calls a pager, so we also need to redefine it
242 if os.name == 'posix':
241 if os.name == 'posix':
243 @line_magic
242 @line_magic
244 def man(self, arg_s):
243 def man(self, arg_s):
245 """Find the man page for the given command and display in pager."""
244 """Find the man page for the given command and display in pager."""
246 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
245 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
247 split=False))
246 split=False))
248
247
249 @line_magic
248 @line_magic
250 def connect_info(self, arg_s):
249 def connect_info(self, arg_s):
251 """Print information for connecting other clients to this kernel
250 """Print information for connecting other clients to this kernel
252
251
253 It will print the contents of this session's connection file, as well as
252 It will print the contents of this session's connection file, as well as
254 shortcuts for local clients.
253 shortcuts for local clients.
255
254
256 In the simplest case, when called from the most recently launched kernel,
255 In the simplest case, when called from the most recently launched kernel,
257 secondary clients can be connected, simply with:
256 secondary clients can be connected, simply with:
258
257
259 $> ipython <app> --existing
258 $> ipython <app> --existing
260
259
261 """
260 """
262
261
263 from IPython.core.application import BaseIPythonApplication as BaseIPApp
262 from IPython.core.application import BaseIPythonApplication as BaseIPApp
264
263
265 if BaseIPApp.initialized():
264 if BaseIPApp.initialized():
266 app = BaseIPApp.instance()
265 app = BaseIPApp.instance()
267 security_dir = app.profile_dir.security_dir
266 security_dir = app.profile_dir.security_dir
268 profile = app.profile
267 profile = app.profile
269 else:
268 else:
270 profile = 'default'
269 profile = 'default'
271 security_dir = ''
270 security_dir = ''
272
271
273 try:
272 try:
274 connection_file = get_connection_file()
273 connection_file = get_connection_file()
275 info = get_connection_info(unpack=False)
274 info = get_connection_info(unpack=False)
276 except Exception as e:
275 except Exception as e:
277 error("Could not get connection info: %r" % e)
276 error("Could not get connection info: %r" % e)
278 return
277 return
279
278
280 # add profile flag for non-default profile
279 # add profile flag for non-default profile
281 profile_flag = "--profile %s" % profile if profile != 'default' else ""
280 profile_flag = "--profile %s" % profile if profile != 'default' else ""
282
281
283 # if it's in the security dir, truncate to basename
282 # if it's in the security dir, truncate to basename
284 if security_dir == os.path.dirname(connection_file):
283 if security_dir == os.path.dirname(connection_file):
285 connection_file = os.path.basename(connection_file)
284 connection_file = os.path.basename(connection_file)
286
285
287
286
288 print (info + '\n')
287 print (info + '\n')
289 print ("Paste the above JSON into a file, and connect with:\n"
288 print ("Paste the above JSON into a file, and connect with:\n"
290 " $> ipython <app> --existing <file>\n"
289 " $> ipython <app> --existing <file>\n"
291 "or, if you are local, you can connect with just:\n"
290 "or, if you are local, you can connect with just:\n"
292 " $> ipython <app> --existing {0} {1}\n"
291 " $> ipython <app> --existing {0} {1}\n"
293 "or even just:\n"
292 "or even just:\n"
294 " $> ipython <app> --existing {1}\n"
293 " $> ipython <app> --existing {1}\n"
295 "if this is the most recent IPython session you have started.".format(
294 "if this is the most recent IPython session you have started.".format(
296 connection_file, profile_flag
295 connection_file, profile_flag
297 )
296 )
298 )
297 )
299
298
300 @line_magic
299 @line_magic
301 def qtconsole(self, arg_s):
300 def qtconsole(self, arg_s):
302 """Open a qtconsole connected to this kernel.
301 """Open a qtconsole connected to this kernel.
303
302
304 Useful for connecting a qtconsole to running notebooks, for better
303 Useful for connecting a qtconsole to running notebooks, for better
305 debugging.
304 debugging.
306 """
305 """
307
306
308 # %qtconsole should imply bind_kernel for engines:
307 # %qtconsole should imply bind_kernel for engines:
309 try:
308 try:
310 from IPython.parallel import bind_kernel
309 from IPython.parallel import bind_kernel
311 except ImportError:
310 except ImportError:
312 # technically possible, because parallel has higher pyzmq min-version
311 # technically possible, because parallel has higher pyzmq min-version
313 pass
312 pass
314 else:
313 else:
315 bind_kernel()
314 bind_kernel()
316
315
317 try:
316 try:
318 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
317 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
319 except Exception as e:
318 except Exception as e:
320 error("Could not start qtconsole: %r" % e)
319 error("Could not start qtconsole: %r" % e)
321 return
320 return
322
321
323 @line_magic
322 @line_magic
324 def autosave(self, arg_s):
323 def autosave(self, arg_s):
325 """Set the autosave interval in the notebook (in seconds).
324 """Set the autosave interval in the notebook (in seconds).
326
325
327 The default value is 120, or two minutes.
326 The default value is 120, or two minutes.
328 ``%autosave 0`` will disable autosave.
327 ``%autosave 0`` will disable autosave.
329
328
330 This magic only has an effect when called from the notebook interface.
329 This magic only has an effect when called from the notebook interface.
331 It has no effect when called in a startup file.
330 It has no effect when called in a startup file.
332 """
331 """
333
332
334 try:
333 try:
335 interval = int(arg_s)
334 interval = int(arg_s)
336 except ValueError:
335 except ValueError:
337 raise UsageError("%%autosave requires an integer, got %r" % arg_s)
336 raise UsageError("%%autosave requires an integer, got %r" % arg_s)
338
337
339 # javascript wants milliseconds
338 # javascript wants milliseconds
340 milliseconds = 1000 * interval
339 milliseconds = 1000 * interval
341 display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds),
340 display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds),
342 include=['application/javascript']
341 include=['application/javascript']
343 )
342 )
344 if interval:
343 if interval:
345 print("Autosaving every %i seconds" % interval)
344 print("Autosaving every %i seconds" % interval)
346 else:
345 else:
347 print("Autosave disabled")
346 print("Autosave disabled")
348
347
349
348
350 class ZMQInteractiveShell(InteractiveShell):
349 class ZMQInteractiveShell(InteractiveShell):
351 """A subclass of InteractiveShell for ZMQ."""
350 """A subclass of InteractiveShell for ZMQ."""
352
351
353 displayhook_class = Type(ZMQShellDisplayHook)
352 displayhook_class = Type(ZMQShellDisplayHook)
354 display_pub_class = Type(ZMQDisplayPublisher)
353 display_pub_class = Type(ZMQDisplayPublisher)
355 data_pub_class = Type(ZMQDataPublisher)
354 data_pub_class = Type(ZMQDataPublisher)
356 kernel = Any()
355 kernel = Any()
357 parent_header = Any()
356 parent_header = Any()
358
357
359 def _banner1_default(self):
358 def _banner1_default(self):
360 return default_gui_banner
359 return default_gui_banner
361
360
362 # Override the traitlet in the parent class, because there's no point using
361 # Override the traitlet in the parent class, because there's no point using
363 # readline for the kernel. Can be removed when the readline code is moved
362 # readline for the kernel. Can be removed when the readline code is moved
364 # to the terminal frontend.
363 # to the terminal frontend.
365 colors_force = CBool(True)
364 colors_force = CBool(True)
366 readline_use = CBool(False)
365 readline_use = CBool(False)
367 # autoindent has no meaning in a zmqshell, and attempting to enable it
366 # autoindent has no meaning in a zmqshell, and attempting to enable it
368 # will print a warning in the absence of readline.
367 # will print a warning in the absence of readline.
369 autoindent = CBool(False)
368 autoindent = CBool(False)
370
369
371 exiter = Instance(ZMQExitAutocall)
370 exiter = Instance(ZMQExitAutocall)
372 def _exiter_default(self):
371 def _exiter_default(self):
373 return ZMQExitAutocall(self)
372 return ZMQExitAutocall(self)
374
373
375 def _exit_now_changed(self, name, old, new):
374 def _exit_now_changed(self, name, old, new):
376 """stop eventloop when exit_now fires"""
375 """stop eventloop when exit_now fires"""
377 if new:
376 if new:
378 loop = ioloop.IOLoop.instance()
377 loop = ioloop.IOLoop.instance()
379 loop.add_timeout(time.time()+0.1, loop.stop)
378 loop.add_timeout(time.time()+0.1, loop.stop)
380
379
381 keepkernel_on_exit = None
380 keepkernel_on_exit = None
382
381
383 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
382 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
384 # interactive input being read; we provide event loop support in ipkernel
383 # interactive input being read; we provide event loop support in ipkernel
385 @staticmethod
384 @staticmethod
386 def enable_gui(gui):
385 def enable_gui(gui):
387 from .eventloops import enable_gui as real_enable_gui
386 from .eventloops import enable_gui as real_enable_gui
388 try:
387 try:
389 real_enable_gui(gui)
388 real_enable_gui(gui)
390 except ValueError as e:
389 except ValueError as e:
391 raise UsageError("%s" % e)
390 raise UsageError("%s" % e)
392
391
393 def init_environment(self):
392 def init_environment(self):
394 """Configure the user's environment."""
393 """Configure the user's environment."""
395 env = os.environ
394 env = os.environ
396 # These two ensure 'ls' produces nice coloring on BSD-derived systems
395 # These two ensure 'ls' produces nice coloring on BSD-derived systems
397 env['TERM'] = 'xterm-color'
396 env['TERM'] = 'xterm-color'
398 env['CLICOLOR'] = '1'
397 env['CLICOLOR'] = '1'
399 # Since normal pagers don't work at all (over pexpect we don't have
398 # Since normal pagers don't work at all (over pexpect we don't have
400 # single-key control of the subprocess), try to disable paging in
399 # single-key control of the subprocess), try to disable paging in
401 # subprocesses as much as possible.
400 # subprocesses as much as possible.
402 env['PAGER'] = 'cat'
401 env['PAGER'] = 'cat'
403 env['GIT_PAGER'] = 'cat'
402 env['GIT_PAGER'] = 'cat'
404
403
405 def init_hooks(self):
404 def init_hooks(self):
406 super(ZMQInteractiveShell, self).init_hooks()
405 super(ZMQInteractiveShell, self).init_hooks()
407 self.set_hook('show_in_pager', page.as_hook(payloadpage.page), 99)
406 self.set_hook('show_in_pager', page.as_hook(payloadpage.page), 99)
408
407
409 def ask_exit(self):
408 def ask_exit(self):
410 """Engage the exit actions."""
409 """Engage the exit actions."""
411 self.exit_now = (not self.keepkernel_on_exit)
410 self.exit_now = (not self.keepkernel_on_exit)
412 payload = dict(
411 payload = dict(
413 source='ask_exit',
412 source='ask_exit',
414 keepkernel=self.keepkernel_on_exit,
413 keepkernel=self.keepkernel_on_exit,
415 )
414 )
416 self.payload_manager.write_payload(payload)
415 self.payload_manager.write_payload(payload)
417
416
418 def _showtraceback(self, etype, evalue, stb):
417 def _showtraceback(self, etype, evalue, stb):
419 # try to preserve ordering of tracebacks and print statements
418 # try to preserve ordering of tracebacks and print statements
420 sys.stdout.flush()
419 sys.stdout.flush()
421 sys.stderr.flush()
420 sys.stderr.flush()
422
421
423 exc_content = {
422 exc_content = {
424 u'traceback' : stb,
423 u'traceback' : stb,
425 u'ename' : unicode_type(etype.__name__),
424 u'ename' : unicode_type(etype.__name__),
426 u'evalue' : py3compat.safe_unicode(evalue),
425 u'evalue' : py3compat.safe_unicode(evalue),
427 }
426 }
428
427
429 dh = self.displayhook
428 dh = self.displayhook
430 # Send exception info over pub socket for other clients than the caller
429 # Send exception info over pub socket for other clients than the caller
431 # to pick up
430 # to pick up
432 topic = None
431 topic = None
433 if dh.topic:
432 if dh.topic:
434 topic = dh.topic.replace(b'execute_result', b'error')
433 topic = dh.topic.replace(b'execute_result', b'error')
435
434
436 exc_msg = dh.session.send(dh.pub_socket, u'error', json_clean(exc_content), dh.parent_header, ident=topic)
435 exc_msg = dh.session.send(dh.pub_socket, u'error', json_clean(exc_content), dh.parent_header, ident=topic)
437
436
438 # FIXME - Hack: store exception info in shell object. Right now, the
437 # FIXME - Hack: store exception info in shell object. Right now, the
439 # caller is reading this info after the fact, we need to fix this logic
438 # caller is reading this info after the fact, we need to fix this logic
440 # to remove this hack. Even uglier, we need to store the error status
439 # to remove this hack. Even uglier, we need to store the error status
441 # here, because in the main loop, the logic that sets it is being
440 # here, because in the main loop, the logic that sets it is being
442 # skipped because runlines swallows the exceptions.
441 # skipped because runlines swallows the exceptions.
443 exc_content[u'status'] = u'error'
442 exc_content[u'status'] = u'error'
444 self._reply_content = exc_content
443 self._reply_content = exc_content
445 # /FIXME
444 # /FIXME
446
445
447 return exc_content
446 return exc_content
448
447
449 def set_next_input(self, text, replace=False):
448 def set_next_input(self, text, replace=False):
450 """Send the specified text to the frontend to be presented at the next
449 """Send the specified text to the frontend to be presented at the next
451 input cell."""
450 input cell."""
452 payload = dict(
451 payload = dict(
453 source='set_next_input',
452 source='set_next_input',
454 text=text,
453 text=text,
455 replace=replace,
454 replace=replace,
456 )
455 )
457 self.payload_manager.write_payload(payload)
456 self.payload_manager.write_payload(payload)
458
457
459 def set_parent(self, parent):
458 def set_parent(self, parent):
460 """Set the parent header for associating output with its triggering input"""
459 """Set the parent header for associating output with its triggering input"""
461 self.parent_header = parent
460 self.parent_header = parent
462 self.displayhook.set_parent(parent)
461 self.displayhook.set_parent(parent)
463 self.display_pub.set_parent(parent)
462 self.display_pub.set_parent(parent)
464 self.data_pub.set_parent(parent)
463 self.data_pub.set_parent(parent)
465 try:
464 try:
466 sys.stdout.set_parent(parent)
465 sys.stdout.set_parent(parent)
467 except AttributeError:
466 except AttributeError:
468 pass
467 pass
469 try:
468 try:
470 sys.stderr.set_parent(parent)
469 sys.stderr.set_parent(parent)
471 except AttributeError:
470 except AttributeError:
472 pass
471 pass
473
472
474 def get_parent(self):
473 def get_parent(self):
475 return self.parent_header
474 return self.parent_header
476
475
477 #-------------------------------------------------------------------------
476 #-------------------------------------------------------------------------
478 # Things related to magics
477 # Things related to magics
479 #-------------------------------------------------------------------------
478 #-------------------------------------------------------------------------
480
479
481 def init_magics(self):
480 def init_magics(self):
482 super(ZMQInteractiveShell, self).init_magics()
481 super(ZMQInteractiveShell, self).init_magics()
483 self.register_magics(KernelMagics)
482 self.register_magics(KernelMagics)
484 self.magics_manager.register_alias('ed', 'edit')
483 self.magics_manager.register_alias('ed', 'edit')
485
484
486
485
487 InteractiveShellABC.register(ZMQInteractiveShell)
486 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now