##// END OF EJS Templates
%qtconsole implied bind_kernel on engines
MinRK -
Show More
@@ -1,549 +1,559 b''
1 1 """A ZMQ-based subclass of InteractiveShell.
2 2
3 3 This code is meant to ease the refactoring of the base InteractiveShell into
4 4 something with a cleaner architecture for 2-process use, without actually
5 5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 6 we subclass and override what we want to fix. Once this is working well, we
7 7 can go back to the base class and refactor the code for a cleaner inheritance
8 8 implementation that doesn't rely on so much monkeypatching.
9 9
10 10 But this lets us maintain a fully working IPython as we develop the new
11 11 machinery. This should thus be thought of as scaffolding.
12 12 """
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib
19 19 import os
20 20 import sys
21 21 import time
22 22
23 23 # System library imports
24 24 from zmq.eventloop import ioloop
25 25
26 26 # Our own
27 27 from IPython.core.interactiveshell import (
28 28 InteractiveShell, InteractiveShellABC
29 29 )
30 30 from IPython.core import page
31 31 from IPython.core.autocall import ZMQExitAutocall
32 32 from IPython.core.displaypub import DisplayPublisher
33 33 from IPython.core.magics import MacroToEdit, CodeMagics
34 34 from IPython.core.magic import magics_class, line_magic, Magics
35 35 from IPython.core.payloadpage import install_payload_page
36 36 from IPython.lib.kernel import (
37 37 get_connection_file, get_connection_info, connect_qtconsole
38 38 )
39 39 from IPython.testing.skipdoctest import skip_doctest
40 40 from IPython.utils import io
41 41 from IPython.utils.jsonutil import json_clean
42 42 from IPython.utils.process import arg_split
43 43 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
44 44 from IPython.utils.warn import warn, error
45 45 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
46 46 from IPython.zmq.session import extract_header
47 47 from session import Session
48 48
49 49 #-----------------------------------------------------------------------------
50 50 # Functions and classes
51 51 #-----------------------------------------------------------------------------
52 52
53 53 class ZMQDisplayPublisher(DisplayPublisher):
54 54 """A display publisher that publishes data using a ZeroMQ PUB socket."""
55 55
56 56 session = Instance(Session)
57 57 pub_socket = Instance('zmq.Socket')
58 58 parent_header = Dict({})
59 59 topic = CBytes(b'displaypub')
60 60
61 61 def set_parent(self, parent):
62 62 """Set the parent for outbound messages."""
63 63 self.parent_header = extract_header(parent)
64 64
65 65 def _flush_streams(self):
66 66 """flush IO Streams prior to display"""
67 67 sys.stdout.flush()
68 68 sys.stderr.flush()
69 69
70 70 def publish(self, source, data, metadata=None):
71 71 self._flush_streams()
72 72 if metadata is None:
73 73 metadata = {}
74 74 self._validate_data(source, data, metadata)
75 75 content = {}
76 76 content['source'] = source
77 77 content['data'] = _encode_binary(data)
78 78 content['metadata'] = metadata
79 79 self.session.send(
80 80 self.pub_socket, u'display_data', json_clean(content),
81 81 parent=self.parent_header, ident=self.topic,
82 82 )
83 83
84 84 def clear_output(self, stdout=True, stderr=True, other=True):
85 85 content = dict(stdout=stdout, stderr=stderr, other=other)
86 86
87 87 if stdout:
88 88 print('\r', file=sys.stdout, end='')
89 89 if stderr:
90 90 print('\r', file=sys.stderr, end='')
91 91
92 92 self._flush_streams()
93 93
94 94 self.session.send(
95 95 self.pub_socket, u'clear_output', content,
96 96 parent=self.parent_header, ident=self.topic,
97 97 )
98 98
99 99 @magics_class
100 100 class KernelMagics(Magics):
101 101 #------------------------------------------------------------------------
102 102 # Magic overrides
103 103 #------------------------------------------------------------------------
104 104 # Once the base class stops inheriting from magic, this code needs to be
105 105 # moved into a separate machinery as well. For now, at least isolate here
106 106 # the magics which this class needs to implement differently from the base
107 107 # class, or that are unique to it.
108 108
109 109 @line_magic
110 110 def doctest_mode(self, parameter_s=''):
111 111 """Toggle doctest mode on and off.
112 112
113 113 This mode is intended to make IPython behave as much as possible like a
114 114 plain Python shell, from the perspective of how its prompts, exceptions
115 115 and output look. This makes it easy to copy and paste parts of a
116 116 session into doctests. It does so by:
117 117
118 118 - Changing the prompts to the classic ``>>>`` ones.
119 119 - Changing the exception reporting mode to 'Plain'.
120 120 - Disabling pretty-printing of output.
121 121
122 122 Note that IPython also supports the pasting of code snippets that have
123 123 leading '>>>' and '...' prompts in them. This means that you can paste
124 124 doctests from files or docstrings (even if they have leading
125 125 whitespace), and the code will execute correctly. You can then use
126 126 '%history -t' to see the translated history; this will give you the
127 127 input after removal of all the leading prompts and whitespace, which
128 128 can be pasted back into an editor.
129 129
130 130 With these features, you can switch into this mode easily whenever you
131 131 need to do testing and changes to doctests, without having to leave
132 132 your existing IPython session.
133 133 """
134 134
135 135 from IPython.utils.ipstruct import Struct
136 136
137 137 # Shorthands
138 138 shell = self.shell
139 139 disp_formatter = self.shell.display_formatter
140 140 ptformatter = disp_formatter.formatters['text/plain']
141 141 # dstore is a data store kept in the instance metadata bag to track any
142 142 # changes we make, so we can undo them later.
143 143 dstore = shell.meta.setdefault('doctest_mode', Struct())
144 144 save_dstore = dstore.setdefault
145 145
146 146 # save a few values we'll need to recover later
147 147 mode = save_dstore('mode', False)
148 148 save_dstore('rc_pprint', ptformatter.pprint)
149 149 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
150 150 save_dstore('xmode', shell.InteractiveTB.mode)
151 151
152 152 if mode == False:
153 153 # turn on
154 154 ptformatter.pprint = False
155 155 disp_formatter.plain_text_only = True
156 156 shell.magic('xmode Plain')
157 157 else:
158 158 # turn off
159 159 ptformatter.pprint = dstore.rc_pprint
160 160 disp_formatter.plain_text_only = dstore.rc_plain_text_only
161 161 shell.magic("xmode " + dstore.xmode)
162 162
163 163 # Store new mode and inform on console
164 164 dstore.mode = bool(1-int(mode))
165 165 mode_label = ['OFF','ON'][dstore.mode]
166 166 print('Doctest mode is:', mode_label)
167 167
168 168 # Send the payload back so that clients can modify their prompt display
169 169 payload = dict(
170 170 source='IPython.zmq.zmqshell.ZMQInteractiveShell.doctest_mode',
171 171 mode=dstore.mode)
172 172 shell.payload_manager.write_payload(payload)
173 173
174 174
175 175 _find_edit_target = CodeMagics._find_edit_target
176 176
177 177 @skip_doctest
178 178 @line_magic
179 179 def edit(self, parameter_s='', last_call=['','']):
180 180 """Bring up an editor and execute the resulting code.
181 181
182 182 Usage:
183 183 %edit [options] [args]
184 184
185 185 %edit runs an external text editor. You will need to set the command for
186 186 this editor via the ``TerminalInteractiveShell.editor`` option in your
187 187 configuration file before it will work.
188 188
189 189 This command allows you to conveniently edit multi-line code right in
190 190 your IPython session.
191 191
192 192 If called without arguments, %edit opens up an empty editor with a
193 193 temporary file and will execute the contents of this file when you
194 194 close it (don't forget to save it!).
195 195
196 196
197 197 Options:
198 198
199 199 -n <number>: open the editor at a specified line number. By default,
200 200 the IPython editor hook uses the unix syntax 'editor +N filename', but
201 201 you can configure this by providing your own modified hook if your
202 202 favorite editor supports line-number specifications with a different
203 203 syntax.
204 204
205 205 -p: this will call the editor with the same data as the previous time
206 206 it was used, regardless of how long ago (in your current session) it
207 207 was.
208 208
209 209 -r: use 'raw' input. This option only applies to input taken from the
210 210 user's history. By default, the 'processed' history is used, so that
211 211 magics are loaded in their transformed version to valid Python. If
212 212 this option is given, the raw input as typed as the command line is
213 213 used instead. When you exit the editor, it will be executed by
214 214 IPython's own processor.
215 215
216 216 -x: do not execute the edited code immediately upon exit. This is
217 217 mainly useful if you are editing programs which need to be called with
218 218 command line arguments, which you can then do using %run.
219 219
220 220
221 221 Arguments:
222 222
223 223 If arguments are given, the following possibilites exist:
224 224
225 225 - The arguments are numbers or pairs of colon-separated numbers (like
226 226 1 4:8 9). These are interpreted as lines of previous input to be
227 227 loaded into the editor. The syntax is the same of the %macro command.
228 228
229 229 - If the argument doesn't start with a number, it is evaluated as a
230 230 variable and its contents loaded into the editor. You can thus edit
231 231 any string which contains python code (including the result of
232 232 previous edits).
233 233
234 234 - If the argument is the name of an object (other than a string),
235 235 IPython will try to locate the file where it was defined and open the
236 236 editor at the point where it is defined. You can use `%edit function`
237 237 to load an editor exactly at the point where 'function' is defined,
238 238 edit it and have the file be executed automatically.
239 239
240 240 If the object is a macro (see %macro for details), this opens up your
241 241 specified editor with a temporary file containing the macro's data.
242 242 Upon exit, the macro is reloaded with the contents of the file.
243 243
244 244 Note: opening at an exact line is only supported under Unix, and some
245 245 editors (like kedit and gedit up to Gnome 2.8) do not understand the
246 246 '+NUMBER' parameter necessary for this feature. Good editors like
247 247 (X)Emacs, vi, jed, pico and joe all do.
248 248
249 249 - If the argument is not found as a variable, IPython will look for a
250 250 file with that name (adding .py if necessary) and load it into the
251 251 editor. It will execute its contents with execfile() when you exit,
252 252 loading any code in the file into your interactive namespace.
253 253
254 254 After executing your code, %edit will return as output the code you
255 255 typed in the editor (except when it was an existing file). This way
256 256 you can reload the code in further invocations of %edit as a variable,
257 257 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
258 258 the output.
259 259
260 260 Note that %edit is also available through the alias %ed.
261 261
262 262 This is an example of creating a simple function inside the editor and
263 263 then modifying it. First, start up the editor:
264 264
265 265 In [1]: ed
266 266 Editing... done. Executing edited code...
267 267 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
268 268
269 269 We can then call the function foo():
270 270
271 271 In [2]: foo()
272 272 foo() was defined in an editing session
273 273
274 274 Now we edit foo. IPython automatically loads the editor with the
275 275 (temporary) file where foo() was previously defined:
276 276
277 277 In [3]: ed foo
278 278 Editing... done. Executing edited code...
279 279
280 280 And if we call foo() again we get the modified version:
281 281
282 282 In [4]: foo()
283 283 foo() has now been changed!
284 284
285 285 Here is an example of how to edit a code snippet successive
286 286 times. First we call the editor:
287 287
288 288 In [5]: ed
289 289 Editing... done. Executing edited code...
290 290 hello
291 291 Out[5]: "print 'hello'n"
292 292
293 293 Now we call it again with the previous output (stored in _):
294 294
295 295 In [6]: ed _
296 296 Editing... done. Executing edited code...
297 297 hello world
298 298 Out[6]: "print 'hello world'n"
299 299
300 300 Now we call it with the output #8 (stored in _8, also as Out[8]):
301 301
302 302 In [7]: ed _8
303 303 Editing... done. Executing edited code...
304 304 hello again
305 305 Out[7]: "print 'hello again'n"
306 306 """
307 307
308 308 opts,args = self.parse_options(parameter_s,'prn:')
309 309
310 310 try:
311 311 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
312 312 except MacroToEdit as e:
313 313 # TODO: Implement macro editing over 2 processes.
314 314 print("Macro editing not yet implemented in 2-process model.")
315 315 return
316 316
317 317 # Make sure we send to the client an absolute path, in case the working
318 318 # directory of client and kernel don't match
319 319 filename = os.path.abspath(filename)
320 320
321 321 payload = {
322 322 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
323 323 'filename' : filename,
324 324 'line_number' : lineno
325 325 }
326 326 self.shell.payload_manager.write_payload(payload)
327 327
328 328 # A few magics that are adapted to the specifics of using pexpect and a
329 329 # remote terminal
330 330
331 331 @line_magic
332 332 def clear(self, arg_s):
333 333 """Clear the terminal."""
334 334 if os.name == 'posix':
335 335 self.shell.system("clear")
336 336 else:
337 337 self.shell.system("cls")
338 338
339 339 if os.name == 'nt':
340 340 # This is the usual name in windows
341 341 cls = line_magic('cls')(clear)
342 342
343 343 # Terminal pagers won't work over pexpect, but we do have our own pager
344 344
345 345 @line_magic
346 346 def less(self, arg_s):
347 347 """Show a file through the pager.
348 348
349 349 Files ending in .py are syntax-highlighted."""
350 350 cont = open(arg_s).read()
351 351 if arg_s.endswith('.py'):
352 352 cont = self.shell.pycolorize(cont)
353 353 page.page(cont)
354 354
355 355 more = line_magic('more')(less)
356 356
357 357 # Man calls a pager, so we also need to redefine it
358 358 if os.name == 'posix':
359 359 @line_magic
360 360 def man(self, arg_s):
361 361 """Find the man page for the given command and display in pager."""
362 362 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
363 363 split=False))
364 364
365 365 @line_magic
366 366 def connect_info(self, arg_s):
367 367 """Print information for connecting other clients to this kernel
368 368
369 369 It will print the contents of this session's connection file, as well as
370 370 shortcuts for local clients.
371 371
372 372 In the simplest case, when called from the most recently launched kernel,
373 373 secondary clients can be connected, simply with:
374 374
375 375 $> ipython <app> --existing
376 376
377 377 """
378 378
379 379 from IPython.core.application import BaseIPythonApplication as BaseIPApp
380 380
381 381 if BaseIPApp.initialized():
382 382 app = BaseIPApp.instance()
383 383 security_dir = app.profile_dir.security_dir
384 384 profile = app.profile
385 385 else:
386 386 profile = 'default'
387 387 security_dir = ''
388 388
389 389 try:
390 390 connection_file = get_connection_file()
391 391 info = get_connection_info(unpack=False)
392 392 except Exception as e:
393 393 error("Could not get connection info: %r" % e)
394 394 return
395 395
396 396 # add profile flag for non-default profile
397 397 profile_flag = "--profile %s" % profile if profile != 'default' else ""
398 398
399 399 # if it's in the security dir, truncate to basename
400 400 if security_dir == os.path.dirname(connection_file):
401 401 connection_file = os.path.basename(connection_file)
402 402
403 403
404 404 print (info + '\n')
405 405 print ("Paste the above JSON into a file, and connect with:\n"
406 406 " $> ipython <app> --existing <file>\n"
407 407 "or, if you are local, you can connect with just:\n"
408 408 " $> ipython <app> --existing {0} {1}\n"
409 409 "or even just:\n"
410 410 " $> ipython <app> --existing {1}\n"
411 411 "if this is the most recent IPython session you have started.".format(
412 412 connection_file, profile_flag
413 413 )
414 414 )
415 415
416 416 @line_magic
417 417 def qtconsole(self, arg_s):
418 418 """Open a qtconsole connected to this kernel.
419 419
420 420 Useful for connecting a qtconsole to running notebooks, for better
421 421 debugging.
422 422 """
423
424 # %qtconsole should imply bind_kernel for engines:
425 try:
426 from IPython.parallel import bind_kernel
427 except ImportError:
428 # technically possible, because parallel has higher pyzmq min-version
429 pass
430 else:
431 bind_kernel()
432
423 433 try:
424 434 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
425 435 except Exception as e:
426 436 error("Could not start qtconsole: %r" % e)
427 437 return
428 438
429 439
430 440 class ZMQInteractiveShell(InteractiveShell):
431 441 """A subclass of InteractiveShell for ZMQ."""
432 442
433 443 displayhook_class = Type(ZMQShellDisplayHook)
434 444 display_pub_class = Type(ZMQDisplayPublisher)
435 445
436 446 # Override the traitlet in the parent class, because there's no point using
437 447 # readline for the kernel. Can be removed when the readline code is moved
438 448 # to the terminal frontend.
439 449 colors_force = CBool(True)
440 450 readline_use = CBool(False)
441 451 # autoindent has no meaning in a zmqshell, and attempting to enable it
442 452 # will print a warning in the absence of readline.
443 453 autoindent = CBool(False)
444 454
445 455 exiter = Instance(ZMQExitAutocall)
446 456 def _exiter_default(self):
447 457 return ZMQExitAutocall(self)
448 458
449 459 def _exit_now_changed(self, name, old, new):
450 460 """stop eventloop when exit_now fires"""
451 461 if new:
452 462 loop = ioloop.IOLoop.instance()
453 463 loop.add_timeout(time.time()+0.1, loop.stop)
454 464
455 465 keepkernel_on_exit = None
456 466
457 467 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
458 468 # interactive input being read; we provide event loop support in ipkernel
459 469 from .eventloops import enable_gui
460 470 enable_gui = staticmethod(enable_gui)
461 471
462 472 def init_environment(self):
463 473 """Configure the user's environment.
464 474
465 475 """
466 476 env = os.environ
467 477 # These two ensure 'ls' produces nice coloring on BSD-derived systems
468 478 env['TERM'] = 'xterm-color'
469 479 env['CLICOLOR'] = '1'
470 480 # Since normal pagers don't work at all (over pexpect we don't have
471 481 # single-key control of the subprocess), try to disable paging in
472 482 # subprocesses as much as possible.
473 483 env['PAGER'] = 'cat'
474 484 env['GIT_PAGER'] = 'cat'
475 485
476 486 # And install the payload version of page.
477 487 install_payload_page()
478 488
479 489 def auto_rewrite_input(self, cmd):
480 490 """Called to show the auto-rewritten input for autocall and friends.
481 491
482 492 FIXME: this payload is currently not correctly processed by the
483 493 frontend.
484 494 """
485 495 new = self.prompt_manager.render('rewrite') + cmd
486 496 payload = dict(
487 497 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
488 498 transformed_input=new,
489 499 )
490 500 self.payload_manager.write_payload(payload)
491 501
492 502 def ask_exit(self):
493 503 """Engage the exit actions."""
494 504 self.exit_now = True
495 505 payload = dict(
496 506 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
497 507 exit=True,
498 508 keepkernel=self.keepkernel_on_exit,
499 509 )
500 510 self.payload_manager.write_payload(payload)
501 511
502 512 def _showtraceback(self, etype, evalue, stb):
503 513
504 514 exc_content = {
505 515 u'traceback' : stb,
506 516 u'ename' : unicode(etype.__name__),
507 517 u'evalue' : unicode(evalue)
508 518 }
509 519
510 520 dh = self.displayhook
511 521 # Send exception info over pub socket for other clients than the caller
512 522 # to pick up
513 523 topic = None
514 524 if dh.topic:
515 525 topic = dh.topic.replace(b'pyout', b'pyerr')
516 526
517 527 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
518 528
519 529 # FIXME - Hack: store exception info in shell object. Right now, the
520 530 # caller is reading this info after the fact, we need to fix this logic
521 531 # to remove this hack. Even uglier, we need to store the error status
522 532 # here, because in the main loop, the logic that sets it is being
523 533 # skipped because runlines swallows the exceptions.
524 534 exc_content[u'status'] = u'error'
525 535 self._reply_content = exc_content
526 536 # /FIXME
527 537
528 538 return exc_content
529 539
530 540 def set_next_input(self, text):
531 541 """Send the specified text to the frontend to be presented at the next
532 542 input cell."""
533 543 payload = dict(
534 544 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
535 545 text=text
536 546 )
537 547 self.payload_manager.write_payload(payload)
538 548
539 549 #-------------------------------------------------------------------------
540 550 # Things related to magics
541 551 #-------------------------------------------------------------------------
542 552
543 553 def init_magics(self):
544 554 super(ZMQInteractiveShell, self).init_magics()
545 555 self.register_magics(KernelMagics)
546 556
547 557
548 558
549 559 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now