##// END OF EJS Templates
make parent_header available from the Shell object
MinRK -
Show More
@@ -1,798 +1,789 b''
1 1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
4 Things to do:
5
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.
8 * Implement random port and security key logic.
9 * Implement control messages.
10 * Implement event loop and poll version.
11 """
2 """An interactive kernel that talks to frontends over 0MQ."""
12 3
13 4 #-----------------------------------------------------------------------------
14 5 # Imports
15 6 #-----------------------------------------------------------------------------
16 7 from __future__ import print_function
17 8
18 9 # Standard library imports
19 10 import __builtin__
20 11 import sys
21 12 import time
22 13 import traceback
23 14 import logging
24 15 import uuid
25 16
26 17 from datetime import datetime
27 18 from signal import (
28 19 signal, default_int_handler, SIGINT
29 20 )
30 21
31 22 # System library imports
32 23 import zmq
33 24 from zmq.eventloop import ioloop
34 25 from zmq.eventloop.zmqstream import ZMQStream
35 26
36 27 # Local imports
37 28 from IPython.config.configurable import Configurable
38 29 from IPython.core.error import StdinNotImplementedError
39 30 from IPython.core import release
40 31 from IPython.utils import py3compat
41 32 from IPython.utils.jsonutil import json_clean
42 33 from IPython.utils.traitlets import (
43 34 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
44 35 Type
45 36 )
46 37
47 38 from serialize import serialize_object, unpack_apply_message
48 39 from session import Session
49 40 from zmqshell import ZMQInteractiveShell
50 41
51 42
52 43 #-----------------------------------------------------------------------------
53 44 # Main kernel class
54 45 #-----------------------------------------------------------------------------
55 46
56 47 protocol_version = list(release.kernel_protocol_version_info)
57 48 ipython_version = list(release.version_info)
58 49 language_version = list(sys.version_info[:3])
59 50
60 51
61 52 class Kernel(Configurable):
62 53
63 54 #---------------------------------------------------------------------------
64 55 # Kernel interface
65 56 #---------------------------------------------------------------------------
66 57
67 58 # attribute to override with a GUI
68 59 eventloop = Any(None)
69 60 def _eventloop_changed(self, name, old, new):
70 61 """schedule call to eventloop from IOLoop"""
71 62 loop = ioloop.IOLoop.instance()
72 63 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
73 64
74 65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
75 66 shell_class = Type(ZMQInteractiveShell)
76 67
77 68 session = Instance(Session)
78 69 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
79 70 shell_streams = List()
80 71 control_stream = Instance(ZMQStream)
81 72 iopub_socket = Instance(zmq.Socket)
82 73 stdin_socket = Instance(zmq.Socket)
83 74 log = Instance(logging.Logger)
84 75
85 76 user_module = Any()
86 77 def _user_module_changed(self, name, old, new):
87 78 if self.shell is not None:
88 79 self.shell.user_module = new
89 80
90 81 user_ns = Instance(dict, args=None, allow_none=True)
91 82 def _user_ns_changed(self, name, old, new):
92 83 if self.shell is not None:
93 84 self.shell.user_ns = new
94 85 self.shell.init_user_ns()
95 86
96 87 # identities:
97 88 int_id = Integer(-1)
98 89 ident = Unicode()
99 90
100 91 def _ident_default(self):
101 92 return unicode(uuid.uuid4())
102 93
103 94
104 95 # Private interface
105 96
106 97 # Time to sleep after flushing the stdout/err buffers in each execute
107 98 # cycle. While this introduces a hard limit on the minimal latency of the
108 99 # execute cycle, it helps prevent output synchronization problems for
109 100 # clients.
110 101 # Units are in seconds. The minimum zmq latency on local host is probably
111 102 # ~150 microseconds, set this to 500us for now. We may need to increase it
112 103 # a little if it's not enough after more interactive testing.
113 104 _execute_sleep = Float(0.0005, config=True)
114 105
115 106 # Frequency of the kernel's event loop.
116 107 # Units are in seconds, kernel subclasses for GUI toolkits may need to
117 108 # adapt to milliseconds.
118 109 _poll_interval = Float(0.05, config=True)
119 110
120 111 # If the shutdown was requested over the network, we leave here the
121 112 # necessary reply message so it can be sent by our registered atexit
122 113 # handler. This ensures that the reply is only sent to clients truly at
123 114 # the end of our shutdown process (which happens after the underlying
124 115 # IPython shell's own shutdown).
125 116 _shutdown_message = None
126 117
127 118 # This is a dict of port number that the kernel is listening on. It is set
128 119 # by record_ports and used by connect_request.
129 120 _recorded_ports = Dict()
130 121
131 122 # A reference to the Python builtin 'raw_input' function.
132 123 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
133 124 _sys_raw_input = Any()
134 125 _sys_eval_input = Any()
135 126
136 127 # set of aborted msg_ids
137 128 aborted = Set()
138 129
139 130
140 131 def __init__(self, **kwargs):
141 132 super(Kernel, self).__init__(**kwargs)
142 133
143 134 # Initialize the InteractiveShell subclass
144 135 self.shell = self.shell_class.instance(parent=self,
145 136 profile_dir = self.profile_dir,
146 137 user_module = self.user_module,
147 138 user_ns = self.user_ns,
148 139 kernel = self,
149 140 )
150 141 self.shell.displayhook.session = self.session
151 142 self.shell.displayhook.pub_socket = self.iopub_socket
152 143 self.shell.displayhook.topic = self._topic('pyout')
153 144 self.shell.display_pub.session = self.session
154 145 self.shell.display_pub.pub_socket = self.iopub_socket
155 146 self.shell.data_pub.session = self.session
156 147 self.shell.data_pub.pub_socket = self.iopub_socket
157 148
158 149 # TMP - hack while developing
159 150 self.shell._reply_content = None
160 151
161 152 # Build dict of handlers for message types
162 153 msg_types = [ 'execute_request', 'complete_request',
163 154 'object_info_request', 'history_request',
164 155 'kernel_info_request',
165 156 'connect_request', 'shutdown_request',
166 157 'apply_request',
167 158 ]
168 159 self.shell_handlers = {}
169 160 for msg_type in msg_types:
170 161 self.shell_handlers[msg_type] = getattr(self, msg_type)
171 162
172 163 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
173 164 comm_manager = self.shell.comm_manager
174 165 for msg_type in comm_msg_types:
175 166 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
176 167
177 168 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
178 169 self.control_handlers = {}
179 170 for msg_type in control_msg_types:
180 171 self.control_handlers[msg_type] = getattr(self, msg_type)
181 172
182 173
183 174 def dispatch_control(self, msg):
184 175 """dispatch control requests"""
185 176 idents,msg = self.session.feed_identities(msg, copy=False)
186 177 try:
187 178 msg = self.session.unserialize(msg, content=True, copy=False)
188 179 except:
189 180 self.log.error("Invalid Control Message", exc_info=True)
190 181 return
191 182
192 183 self.log.debug("Control received: %s", msg)
193 184
194 185 header = msg['header']
195 186 msg_id = header['msg_id']
196 187 msg_type = header['msg_type']
197 188
198 189 handler = self.control_handlers.get(msg_type, None)
199 190 if handler is None:
200 191 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
201 192 else:
202 193 try:
203 194 handler(self.control_stream, idents, msg)
204 195 except Exception:
205 196 self.log.error("Exception in control handler:", exc_info=True)
206 197
207 198 def dispatch_shell(self, stream, msg):
208 199 """dispatch shell requests"""
209 200 # flush control requests first
210 201 if self.control_stream:
211 202 self.control_stream.flush()
212 203
213 204 idents,msg = self.session.feed_identities(msg, copy=False)
214 205 try:
215 206 msg = self.session.unserialize(msg, content=True, copy=False)
216 207 except:
217 208 self.log.error("Invalid Message", exc_info=True)
218 209 return
219 210
220 211 header = msg['header']
221 212 msg_id = header['msg_id']
222 213 msg_type = msg['header']['msg_type']
223 214
224 215 # Print some info about this message and leave a '--->' marker, so it's
225 216 # easier to trace visually the message chain when debugging. Each
226 217 # handler prints its message at the end.
227 218 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
228 219 self.log.debug(' Content: %s\n --->\n ', msg['content'])
229 220
230 221 if msg_id in self.aborted:
231 222 self.aborted.remove(msg_id)
232 223 # is it safe to assume a msg_id will not be resubmitted?
233 224 reply_type = msg_type.split('_')[0] + '_reply'
234 225 status = {'status' : 'aborted'}
235 226 md = {'engine' : self.ident}
236 227 md.update(status)
237 228 reply_msg = self.session.send(stream, reply_type, metadata=md,
238 229 content=status, parent=msg, ident=idents)
239 230 return
240 231
241 232 handler = self.shell_handlers.get(msg_type, None)
242 233 if handler is None:
243 234 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
244 235 else:
245 236 # ensure default_int_handler during handler call
246 237 sig = signal(SIGINT, default_int_handler)
247 238 try:
248 239 handler(stream, idents, msg)
249 240 except Exception:
250 241 self.log.error("Exception in message handler:", exc_info=True)
251 242 finally:
252 243 signal(SIGINT, sig)
253 244
254 245 def enter_eventloop(self):
255 246 """enter eventloop"""
256 247 self.log.info("entering eventloop")
257 248 # restore default_int_handler
258 249 signal(SIGINT, default_int_handler)
259 250 while self.eventloop is not None:
260 251 try:
261 252 self.eventloop(self)
262 253 except KeyboardInterrupt:
263 254 # Ctrl-C shouldn't crash the kernel
264 255 self.log.error("KeyboardInterrupt caught in kernel")
265 256 continue
266 257 else:
267 258 # eventloop exited cleanly, this means we should stop (right?)
268 259 self.eventloop = None
269 260 break
270 261 self.log.info("exiting eventloop")
271 262
272 263 def start(self):
273 264 """register dispatchers for streams"""
274 265 self.shell.exit_now = False
275 266 if self.control_stream:
276 267 self.control_stream.on_recv(self.dispatch_control, copy=False)
277 268
278 269 def make_dispatcher(stream):
279 270 def dispatcher(msg):
280 271 return self.dispatch_shell(stream, msg)
281 272 return dispatcher
282 273
283 274 for s in self.shell_streams:
284 275 s.on_recv(make_dispatcher(s), copy=False)
285 276
286 277 # publish idle status
287 278 self._publish_status('starting')
288 279
289 280 def do_one_iteration(self):
290 281 """step eventloop just once"""
291 282 if self.control_stream:
292 283 self.control_stream.flush()
293 284 for stream in self.shell_streams:
294 285 # handle at most one request per iteration
295 286 stream.flush(zmq.POLLIN, 1)
296 287 stream.flush(zmq.POLLOUT)
297 288
298 289
299 290 def record_ports(self, ports):
300 291 """Record the ports that this kernel is using.
301 292
302 293 The creator of the Kernel instance must call this methods if they
303 294 want the :meth:`connect_request` method to return the port numbers.
304 295 """
305 296 self._recorded_ports = ports
306 297
307 298 #---------------------------------------------------------------------------
308 299 # Kernel request handlers
309 300 #---------------------------------------------------------------------------
310 301
311 302 def _make_metadata(self, other=None):
312 303 """init metadata dict, for execute/apply_reply"""
313 304 new_md = {
314 305 'dependencies_met' : True,
315 306 'engine' : self.ident,
316 307 'started': datetime.now(),
317 308 }
318 309 if other:
319 310 new_md.update(other)
320 311 return new_md
321 312
322 313 def _publish_pyin(self, code, parent, execution_count):
323 314 """Publish the code request on the pyin stream."""
324 315
325 316 self.session.send(self.iopub_socket, u'pyin',
326 317 {u'code':code, u'execution_count': execution_count},
327 318 parent=parent, ident=self._topic('pyin')
328 319 )
329 320
330 321 def _publish_status(self, status, parent=None):
331 322 """send status (busy/idle) on IOPub"""
332 323 self.session.send(self.iopub_socket,
333 324 u'status',
334 325 {u'execution_state': status},
335 326 parent=parent,
336 327 ident=self._topic('status'),
337 328 )
338 329
339 330
340 331 def execute_request(self, stream, ident, parent):
341 332 """handle an execute_request"""
342 333
343 334 self._publish_status(u'busy', parent)
344 335
345 336 try:
346 337 content = parent[u'content']
347 338 code = content[u'code']
348 339 silent = content[u'silent']
349 340 store_history = content.get(u'store_history', not silent)
350 341 except:
351 342 self.log.error("Got bad msg: ")
352 343 self.log.error("%s", parent)
353 344 return
354 345
355 346 md = self._make_metadata(parent['metadata'])
356 347
357 348 shell = self.shell # we'll need this a lot here
358 349
359 350 # Replace raw_input. Note that is not sufficient to replace
360 351 # raw_input in the user namespace.
361 352 if content.get('allow_stdin', False):
362 353 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
363 354 input = lambda prompt='': eval(raw_input(prompt))
364 355 else:
365 356 raw_input = input = lambda prompt='' : self._no_raw_input()
366 357
367 358 if py3compat.PY3:
368 359 self._sys_raw_input = __builtin__.input
369 360 __builtin__.input = raw_input
370 361 else:
371 362 self._sys_raw_input = __builtin__.raw_input
372 363 self._sys_eval_input = __builtin__.input
373 364 __builtin__.raw_input = raw_input
374 365 __builtin__.input = input
375 366
376 367 # Set the parent message of the display hook and out streams.
377 368 shell.set_parent(parent)
378 369
379 370 # Re-broadcast our input for the benefit of listening clients, and
380 371 # start computing output
381 372 if not silent:
382 373 self._publish_pyin(code, parent, shell.execution_count)
383 374
384 375 reply_content = {}
385 376 try:
386 377 # FIXME: the shell calls the exception handler itself.
387 378 shell.run_cell(code, store_history=store_history, silent=silent)
388 379 except:
389 380 status = u'error'
390 381 # FIXME: this code right now isn't being used yet by default,
391 382 # because the run_cell() call above directly fires off exception
392 383 # reporting. This code, therefore, is only active in the scenario
393 384 # where runlines itself has an unhandled exception. We need to
394 385 # uniformize this, for all exception construction to come from a
395 386 # single location in the codbase.
396 387 etype, evalue, tb = sys.exc_info()
397 388 tb_list = traceback.format_exception(etype, evalue, tb)
398 389 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
399 390 else:
400 391 status = u'ok'
401 392 finally:
402 393 # Restore raw_input.
403 394 if py3compat.PY3:
404 395 __builtin__.input = self._sys_raw_input
405 396 else:
406 397 __builtin__.raw_input = self._sys_raw_input
407 398 __builtin__.input = self._sys_eval_input
408 399
409 400 reply_content[u'status'] = status
410 401
411 402 # Return the execution counter so clients can display prompts
412 403 reply_content['execution_count'] = shell.execution_count - 1
413 404
414 405 # FIXME - fish exception info out of shell, possibly left there by
415 406 # runlines. We'll need to clean up this logic later.
416 407 if shell._reply_content is not None:
417 408 reply_content.update(shell._reply_content)
418 409 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
419 410 reply_content['engine_info'] = e_info
420 411 # reset after use
421 412 shell._reply_content = None
422 413
423 414 if 'traceback' in reply_content:
424 415 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
425 416
426 417
427 418 # At this point, we can tell whether the main code execution succeeded
428 419 # or not. If it did, we proceed to evaluate user_variables/expressions
429 420 if reply_content['status'] == 'ok':
430 421 reply_content[u'user_variables'] = \
431 422 shell.user_variables(content.get(u'user_variables', []))
432 423 reply_content[u'user_expressions'] = \
433 424 shell.user_expressions(content.get(u'user_expressions', {}))
434 425 else:
435 426 # If there was an error, don't even try to compute variables or
436 427 # expressions
437 428 reply_content[u'user_variables'] = {}
438 429 reply_content[u'user_expressions'] = {}
439 430
440 431 # Payloads should be retrieved regardless of outcome, so we can both
441 432 # recover partial output (that could have been generated early in a
442 433 # block, before an error) and clear the payload system always.
443 434 reply_content[u'payload'] = shell.payload_manager.read_payload()
444 435 # Be agressive about clearing the payload because we don't want
445 436 # it to sit in memory until the next execute_request comes in.
446 437 shell.payload_manager.clear_payload()
447 438
448 439 # Flush output before sending the reply.
449 440 sys.stdout.flush()
450 441 sys.stderr.flush()
451 442 # FIXME: on rare occasions, the flush doesn't seem to make it to the
452 443 # clients... This seems to mitigate the problem, but we definitely need
453 444 # to better understand what's going on.
454 445 if self._execute_sleep:
455 446 time.sleep(self._execute_sleep)
456 447
457 448 # Send the reply.
458 449 reply_content = json_clean(reply_content)
459 450
460 451 md['status'] = reply_content['status']
461 452 if reply_content['status'] == 'error' and \
462 453 reply_content['ename'] == 'UnmetDependency':
463 454 md['dependencies_met'] = False
464 455
465 456 reply_msg = self.session.send(stream, u'execute_reply',
466 457 reply_content, parent, metadata=md,
467 458 ident=ident)
468 459
469 460 self.log.debug("%s", reply_msg)
470 461
471 462 if not silent and reply_msg['content']['status'] == u'error':
472 463 self._abort_queues()
473 464
474 465 self._publish_status(u'idle', parent)
475 466
476 467 def complete_request(self, stream, ident, parent):
477 468 txt, matches = self._complete(parent)
478 469 matches = {'matches' : matches,
479 470 'matched_text' : txt,
480 471 'status' : 'ok'}
481 472 matches = json_clean(matches)
482 473 completion_msg = self.session.send(stream, 'complete_reply',
483 474 matches, parent, ident)
484 475 self.log.debug("%s", completion_msg)
485 476
486 477 def object_info_request(self, stream, ident, parent):
487 478 content = parent['content']
488 479 object_info = self.shell.object_inspect(content['oname'],
489 480 detail_level = content.get('detail_level', 0)
490 481 )
491 482 # Before we send this object over, we scrub it for JSON usage
492 483 oinfo = json_clean(object_info)
493 484 msg = self.session.send(stream, 'object_info_reply',
494 485 oinfo, parent, ident)
495 486 self.log.debug("%s", msg)
496 487
497 488 def history_request(self, stream, ident, parent):
498 489 # We need to pull these out, as passing **kwargs doesn't work with
499 490 # unicode keys before Python 2.6.5.
500 491 hist_access_type = parent['content']['hist_access_type']
501 492 raw = parent['content']['raw']
502 493 output = parent['content']['output']
503 494 if hist_access_type == 'tail':
504 495 n = parent['content']['n']
505 496 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
506 497 include_latest=True)
507 498
508 499 elif hist_access_type == 'range':
509 500 session = parent['content']['session']
510 501 start = parent['content']['start']
511 502 stop = parent['content']['stop']
512 503 hist = self.shell.history_manager.get_range(session, start, stop,
513 504 raw=raw, output=output)
514 505
515 506 elif hist_access_type == 'search':
516 507 n = parent['content'].get('n')
517 508 unique = parent['content'].get('unique', False)
518 509 pattern = parent['content']['pattern']
519 510 hist = self.shell.history_manager.search(
520 511 pattern, raw=raw, output=output, n=n, unique=unique)
521 512
522 513 else:
523 514 hist = []
524 515 hist = list(hist)
525 516 content = {'history' : hist}
526 517 content = json_clean(content)
527 518 msg = self.session.send(stream, 'history_reply',
528 519 content, parent, ident)
529 520 self.log.debug("Sending history reply with %i entries", len(hist))
530 521
531 522 def connect_request(self, stream, ident, parent):
532 523 if self._recorded_ports is not None:
533 524 content = self._recorded_ports.copy()
534 525 else:
535 526 content = {}
536 527 msg = self.session.send(stream, 'connect_reply',
537 528 content, parent, ident)
538 529 self.log.debug("%s", msg)
539 530
540 531 def kernel_info_request(self, stream, ident, parent):
541 532 vinfo = {
542 533 'protocol_version': protocol_version,
543 534 'ipython_version': ipython_version,
544 535 'language_version': language_version,
545 536 'language': 'python',
546 537 }
547 538 msg = self.session.send(stream, 'kernel_info_reply',
548 539 vinfo, parent, ident)
549 540 self.log.debug("%s", msg)
550 541
551 542 def shutdown_request(self, stream, ident, parent):
552 543 self.shell.exit_now = True
553 544 content = dict(status='ok')
554 545 content.update(parent['content'])
555 546 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
556 547 # same content, but different msg_id for broadcasting on IOPub
557 548 self._shutdown_message = self.session.msg(u'shutdown_reply',
558 549 content, parent
559 550 )
560 551
561 552 self._at_shutdown()
562 553 # call sys.exit after a short delay
563 554 loop = ioloop.IOLoop.instance()
564 555 loop.add_timeout(time.time()+0.1, loop.stop)
565 556
566 557 #---------------------------------------------------------------------------
567 558 # Engine methods
568 559 #---------------------------------------------------------------------------
569 560
570 561 def apply_request(self, stream, ident, parent):
571 562 try:
572 563 content = parent[u'content']
573 564 bufs = parent[u'buffers']
574 565 msg_id = parent['header']['msg_id']
575 566 except:
576 567 self.log.error("Got bad msg: %s", parent, exc_info=True)
577 568 return
578 569
579 570 self._publish_status(u'busy', parent)
580 571
581 572 # Set the parent message of the display hook and out streams.
582 573 shell = self.shell
583 574 shell.set_parent(parent)
584 575
585 576 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
586 577 # self.iopub_socket.send(pyin_msg)
587 578 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
588 579 md = self._make_metadata(parent['metadata'])
589 580 try:
590 581 working = shell.user_ns
591 582
592 583 prefix = "_"+str(msg_id).replace("-","")+"_"
593 584
594 585 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
595 586
596 587 fname = getattr(f, '__name__', 'f')
597 588
598 589 fname = prefix+"f"
599 590 argname = prefix+"args"
600 591 kwargname = prefix+"kwargs"
601 592 resultname = prefix+"result"
602 593
603 594 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
604 595 # print ns
605 596 working.update(ns)
606 597 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
607 598 try:
608 599 exec code in shell.user_global_ns, shell.user_ns
609 600 result = working.get(resultname)
610 601 finally:
611 602 for key in ns.iterkeys():
612 603 working.pop(key)
613 604
614 605 result_buf = serialize_object(result,
615 606 buffer_threshold=self.session.buffer_threshold,
616 607 item_threshold=self.session.item_threshold,
617 608 )
618 609
619 610 except:
620 611 # invoke IPython traceback formatting
621 612 shell.showtraceback()
622 613 # FIXME - fish exception info out of shell, possibly left there by
623 614 # run_code. We'll need to clean up this logic later.
624 615 reply_content = {}
625 616 if shell._reply_content is not None:
626 617 reply_content.update(shell._reply_content)
627 618 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
628 619 reply_content['engine_info'] = e_info
629 620 # reset after use
630 621 shell._reply_content = None
631 622
632 623 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
633 624 ident=self._topic('pyerr'))
634 625 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
635 626 result_buf = []
636 627
637 628 if reply_content['ename'] == 'UnmetDependency':
638 629 md['dependencies_met'] = False
639 630 else:
640 631 reply_content = {'status' : 'ok'}
641 632
642 633 # put 'ok'/'error' status in header, for scheduler introspection:
643 634 md['status'] = reply_content['status']
644 635
645 636 # flush i/o
646 637 sys.stdout.flush()
647 638 sys.stderr.flush()
648 639
649 640 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
650 641 parent=parent, ident=ident,buffers=result_buf, metadata=md)
651 642
652 643 self._publish_status(u'idle', parent)
653 644
654 645 #---------------------------------------------------------------------------
655 646 # Control messages
656 647 #---------------------------------------------------------------------------
657 648
658 649 def abort_request(self, stream, ident, parent):
659 650 """abort a specifig msg by id"""
660 651 msg_ids = parent['content'].get('msg_ids', None)
661 652 if isinstance(msg_ids, basestring):
662 653 msg_ids = [msg_ids]
663 654 if not msg_ids:
664 655 self.abort_queues()
665 656 for mid in msg_ids:
666 657 self.aborted.add(str(mid))
667 658
668 659 content = dict(status='ok')
669 660 reply_msg = self.session.send(stream, 'abort_reply', content=content,
670 661 parent=parent, ident=ident)
671 662 self.log.debug("%s", reply_msg)
672 663
673 664 def clear_request(self, stream, idents, parent):
674 665 """Clear our namespace."""
675 666 self.shell.reset(False)
676 667 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
677 668 content = dict(status='ok'))
678 669
679 670
680 671 #---------------------------------------------------------------------------
681 672 # Protected interface
682 673 #---------------------------------------------------------------------------
683 674
684 675 def _wrap_exception(self, method=None):
685 676 # import here, because _wrap_exception is only used in parallel,
686 677 # and parallel has higher min pyzmq version
687 678 from IPython.parallel.error import wrap_exception
688 679 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
689 680 content = wrap_exception(e_info)
690 681 return content
691 682
692 683 def _topic(self, topic):
693 684 """prefixed topic for IOPub messages"""
694 685 if self.int_id >= 0:
695 686 base = "engine.%i" % self.int_id
696 687 else:
697 688 base = "kernel.%s" % self.ident
698 689
699 690 return py3compat.cast_bytes("%s.%s" % (base, topic))
700 691
701 692 def _abort_queues(self):
702 693 for stream in self.shell_streams:
703 694 if stream:
704 695 self._abort_queue(stream)
705 696
706 697 def _abort_queue(self, stream):
707 698 poller = zmq.Poller()
708 699 poller.register(stream.socket, zmq.POLLIN)
709 700 while True:
710 701 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
711 702 if msg is None:
712 703 return
713 704
714 705 self.log.info("Aborting:")
715 706 self.log.info("%s", msg)
716 707 msg_type = msg['header']['msg_type']
717 708 reply_type = msg_type.split('_')[0] + '_reply'
718 709
719 710 status = {'status' : 'aborted'}
720 711 md = {'engine' : self.ident}
721 712 md.update(status)
722 713 reply_msg = self.session.send(stream, reply_type, metadata=md,
723 714 content=status, parent=msg, ident=idents)
724 715 self.log.debug("%s", reply_msg)
725 716 # We need to wait a bit for requests to come in. This can probably
726 717 # be set shorter for true asynchronous clients.
727 718 poller.poll(50)
728 719
729 720
730 721 def _no_raw_input(self):
731 722 """Raise StdinNotImplentedError if active frontend doesn't support
732 723 stdin."""
733 724 raise StdinNotImplementedError("raw_input was called, but this "
734 725 "frontend does not support stdin.")
735 726
736 727 def _raw_input(self, prompt, ident, parent):
737 728 # Flush output before making the request.
738 729 sys.stderr.flush()
739 730 sys.stdout.flush()
740 731 # flush the stdin socket, to purge stale replies
741 732 while True:
742 733 try:
743 734 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
744 735 except zmq.ZMQError as e:
745 736 if e.errno == zmq.EAGAIN:
746 737 break
747 738 else:
748 739 raise
749 740
750 741 # Send the input request.
751 742 content = json_clean(dict(prompt=prompt))
752 743 self.session.send(self.stdin_socket, u'input_request', content, parent,
753 744 ident=ident)
754 745
755 746 # Await a response.
756 747 while True:
757 748 try:
758 749 ident, reply = self.session.recv(self.stdin_socket, 0)
759 750 except Exception:
760 751 self.log.warn("Invalid Message:", exc_info=True)
761 752 except KeyboardInterrupt:
762 753 # re-raise KeyboardInterrupt, to truncate traceback
763 754 raise KeyboardInterrupt
764 755 else:
765 756 break
766 757 try:
767 758 value = py3compat.unicode_to_str(reply['content']['value'])
768 759 except:
769 760 self.log.error("Got bad raw_input reply: ")
770 761 self.log.error("%s", parent)
771 762 value = ''
772 763 if value == '\x04':
773 764 # EOF
774 765 raise EOFError
775 766 return value
776 767
777 768 def _complete(self, msg):
778 769 c = msg['content']
779 770 try:
780 771 cpos = int(c['cursor_pos'])
781 772 except:
782 773 # If we don't get something that we can convert to an integer, at
783 774 # least attempt the completion guessing the cursor is at the end of
784 775 # the text, if there's any, and otherwise of the line
785 776 cpos = len(c['text'])
786 777 if cpos==0:
787 778 cpos = len(c['line'])
788 779 return self.shell.complete(c['text'], c['line'], cpos)
789 780
790 781 def _at_shutdown(self):
791 782 """Actions taken at shutdown by the kernel, called by python's atexit.
792 783 """
793 784 # io.rprint("Kernel at_shutdown") # dbg
794 785 if self._shutdown_message is not None:
795 786 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
796 787 self.log.debug("%s", self._shutdown_message)
797 788 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
798 789
@@ -1,618 +1,623 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.error import UsageError
34 34 from IPython.core.magics import MacroToEdit, CodeMagics
35 35 from IPython.core.magic import magics_class, line_magic, Magics
36 36 from IPython.core.payloadpage import install_payload_page
37 37 from IPython.display import display, Javascript
38 38 from IPython.kernel.inprocess.socket import SocketABC
39 39 from IPython.kernel import (
40 40 get_connection_file, get_connection_info, connect_qtconsole
41 41 )
42 42 from IPython.testing.skipdoctest import skip_doctest
43 43 from IPython.utils import openpy
44 44 from IPython.utils.jsonutil import json_clean, encode_images
45 45 from IPython.utils.process import arg_split
46 46 from IPython.utils import py3compat
47 47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes, Any
48 48 from IPython.utils.warn import error
49 49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
50 50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
51 51 from IPython.kernel.zmq.session import extract_header
52 52 from IPython.kernel.comm import CommManager
53 53 from session import Session
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Functions and classes
57 57 #-----------------------------------------------------------------------------
58 58
59 59 class ZMQDisplayPublisher(DisplayPublisher):
60 60 """A display publisher that publishes data using a ZeroMQ PUB socket."""
61 61
62 62 session = Instance(Session)
63 63 pub_socket = Instance(SocketABC)
64 64 parent_header = Dict({})
65 65 topic = CBytes(b'display_data')
66 66
67 67 def set_parent(self, parent):
68 68 """Set the parent for outbound messages."""
69 69 self.parent_header = extract_header(parent)
70 70
71 71 def _flush_streams(self):
72 72 """flush IO Streams prior to display"""
73 73 sys.stdout.flush()
74 74 sys.stderr.flush()
75 75
76 76 def publish(self, source, data, metadata=None):
77 77 self._flush_streams()
78 78 if metadata is None:
79 79 metadata = {}
80 80 self._validate_data(source, data, metadata)
81 81 content = {}
82 82 content['source'] = source
83 83 content['data'] = encode_images(data)
84 84 content['metadata'] = metadata
85 85 self.session.send(
86 86 self.pub_socket, u'display_data', json_clean(content),
87 87 parent=self.parent_header, ident=self.topic,
88 88 )
89 89
90 90 def clear_output(self, wait=False):
91 91 content = dict(wait=wait)
92 92
93 93 print('\r', file=sys.stdout, end='')
94 94 print('\r', file=sys.stderr, end='')
95 95 self._flush_streams()
96 96
97 97 self.session.send(
98 98 self.pub_socket, u'clear_output', content,
99 99 parent=self.parent_header, ident=self.topic,
100 100 )
101 101
102 102 @magics_class
103 103 class KernelMagics(Magics):
104 104 #------------------------------------------------------------------------
105 105 # Magic overrides
106 106 #------------------------------------------------------------------------
107 107 # Once the base class stops inheriting from magic, this code needs to be
108 108 # moved into a separate machinery as well. For now, at least isolate here
109 109 # the magics which this class needs to implement differently from the base
110 110 # class, or that are unique to it.
111 111
112 112 @line_magic
113 113 def doctest_mode(self, parameter_s=''):
114 114 """Toggle doctest mode on and off.
115 115
116 116 This mode is intended to make IPython behave as much as possible like a
117 117 plain Python shell, from the perspective of how its prompts, exceptions
118 118 and output look. This makes it easy to copy and paste parts of a
119 119 session into doctests. It does so by:
120 120
121 121 - Changing the prompts to the classic ``>>>`` ones.
122 122 - Changing the exception reporting mode to 'Plain'.
123 123 - Disabling pretty-printing of output.
124 124
125 125 Note that IPython also supports the pasting of code snippets that have
126 126 leading '>>>' and '...' prompts in them. This means that you can paste
127 127 doctests from files or docstrings (even if they have leading
128 128 whitespace), and the code will execute correctly. You can then use
129 129 '%history -t' to see the translated history; this will give you the
130 130 input after removal of all the leading prompts and whitespace, which
131 131 can be pasted back into an editor.
132 132
133 133 With these features, you can switch into this mode easily whenever you
134 134 need to do testing and changes to doctests, without having to leave
135 135 your existing IPython session.
136 136 """
137 137
138 138 from IPython.utils.ipstruct import Struct
139 139
140 140 # Shorthands
141 141 shell = self.shell
142 142 disp_formatter = self.shell.display_formatter
143 143 ptformatter = disp_formatter.formatters['text/plain']
144 144 # dstore is a data store kept in the instance metadata bag to track any
145 145 # changes we make, so we can undo them later.
146 146 dstore = shell.meta.setdefault('doctest_mode', Struct())
147 147 save_dstore = dstore.setdefault
148 148
149 149 # save a few values we'll need to recover later
150 150 mode = save_dstore('mode', False)
151 151 save_dstore('rc_pprint', ptformatter.pprint)
152 152 save_dstore('rc_active_types',disp_formatter.active_types)
153 153 save_dstore('xmode', shell.InteractiveTB.mode)
154 154
155 155 if mode == False:
156 156 # turn on
157 157 ptformatter.pprint = False
158 158 disp_formatter.active_types = ['text/plain']
159 159 shell.magic('xmode Plain')
160 160 else:
161 161 # turn off
162 162 ptformatter.pprint = dstore.rc_pprint
163 163 disp_formatter.active_types = dstore.rc_active_types
164 164 shell.magic("xmode " + dstore.xmode)
165 165
166 166 # Store new mode and inform on console
167 167 dstore.mode = bool(1-int(mode))
168 168 mode_label = ['OFF','ON'][dstore.mode]
169 169 print('Doctest mode is:', mode_label)
170 170
171 171 # Send the payload back so that clients can modify their prompt display
172 172 payload = dict(
173 173 source='doctest_mode',
174 174 mode=dstore.mode)
175 175 shell.payload_manager.write_payload(payload)
176 176
177 177
178 178 _find_edit_target = CodeMagics._find_edit_target
179 179
180 180 @skip_doctest
181 181 @line_magic
182 182 def edit(self, parameter_s='', last_call=['','']):
183 183 """Bring up an editor and execute the resulting code.
184 184
185 185 Usage:
186 186 %edit [options] [args]
187 187
188 188 %edit runs an external text editor. You will need to set the command for
189 189 this editor via the ``TerminalInteractiveShell.editor`` option in your
190 190 configuration file before it will work.
191 191
192 192 This command allows you to conveniently edit multi-line code right in
193 193 your IPython session.
194 194
195 195 If called without arguments, %edit opens up an empty editor with a
196 196 temporary file and will execute the contents of this file when you
197 197 close it (don't forget to save it!).
198 198
199 199
200 200 Options:
201 201
202 202 -n <number>: open the editor at a specified line number. By default,
203 203 the IPython editor hook uses the unix syntax 'editor +N filename', but
204 204 you can configure this by providing your own modified hook if your
205 205 favorite editor supports line-number specifications with a different
206 206 syntax.
207 207
208 208 -p: this will call the editor with the same data as the previous time
209 209 it was used, regardless of how long ago (in your current session) it
210 210 was.
211 211
212 212 -r: use 'raw' input. This option only applies to input taken from the
213 213 user's history. By default, the 'processed' history is used, so that
214 214 magics are loaded in their transformed version to valid Python. If
215 215 this option is given, the raw input as typed as the command line is
216 216 used instead. When you exit the editor, it will be executed by
217 217 IPython's own processor.
218 218
219 219 -x: do not execute the edited code immediately upon exit. This is
220 220 mainly useful if you are editing programs which need to be called with
221 221 command line arguments, which you can then do using %run.
222 222
223 223
224 224 Arguments:
225 225
226 226 If arguments are given, the following possibilites exist:
227 227
228 228 - The arguments are numbers or pairs of colon-separated numbers (like
229 229 1 4:8 9). These are interpreted as lines of previous input to be
230 230 loaded into the editor. The syntax is the same of the %macro command.
231 231
232 232 - If the argument doesn't start with a number, it is evaluated as a
233 233 variable and its contents loaded into the editor. You can thus edit
234 234 any string which contains python code (including the result of
235 235 previous edits).
236 236
237 237 - If the argument is the name of an object (other than a string),
238 238 IPython will try to locate the file where it was defined and open the
239 239 editor at the point where it is defined. You can use `%edit function`
240 240 to load an editor exactly at the point where 'function' is defined,
241 241 edit it and have the file be executed automatically.
242 242
243 243 If the object is a macro (see %macro for details), this opens up your
244 244 specified editor with a temporary file containing the macro's data.
245 245 Upon exit, the macro is reloaded with the contents of the file.
246 246
247 247 Note: opening at an exact line is only supported under Unix, and some
248 248 editors (like kedit and gedit up to Gnome 2.8) do not understand the
249 249 '+NUMBER' parameter necessary for this feature. Good editors like
250 250 (X)Emacs, vi, jed, pico and joe all do.
251 251
252 252 - If the argument is not found as a variable, IPython will look for a
253 253 file with that name (adding .py if necessary) and load it into the
254 254 editor. It will execute its contents with execfile() when you exit,
255 255 loading any code in the file into your interactive namespace.
256 256
257 257 After executing your code, %edit will return as output the code you
258 258 typed in the editor (except when it was an existing file). This way
259 259 you can reload the code in further invocations of %edit as a variable,
260 260 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
261 261 the output.
262 262
263 263 Note that %edit is also available through the alias %ed.
264 264
265 265 This is an example of creating a simple function inside the editor and
266 266 then modifying it. First, start up the editor:
267 267
268 268 In [1]: ed
269 269 Editing... done. Executing edited code...
270 270 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
271 271
272 272 We can then call the function foo():
273 273
274 274 In [2]: foo()
275 275 foo() was defined in an editing session
276 276
277 277 Now we edit foo. IPython automatically loads the editor with the
278 278 (temporary) file where foo() was previously defined:
279 279
280 280 In [3]: ed foo
281 281 Editing... done. Executing edited code...
282 282
283 283 And if we call foo() again we get the modified version:
284 284
285 285 In [4]: foo()
286 286 foo() has now been changed!
287 287
288 288 Here is an example of how to edit a code snippet successive
289 289 times. First we call the editor:
290 290
291 291 In [5]: ed
292 292 Editing... done. Executing edited code...
293 293 hello
294 294 Out[5]: "print 'hello'n"
295 295
296 296 Now we call it again with the previous output (stored in _):
297 297
298 298 In [6]: ed _
299 299 Editing... done. Executing edited code...
300 300 hello world
301 301 Out[6]: "print 'hello world'n"
302 302
303 303 Now we call it with the output #8 (stored in _8, also as Out[8]):
304 304
305 305 In [7]: ed _8
306 306 Editing... done. Executing edited code...
307 307 hello again
308 308 Out[7]: "print 'hello again'n"
309 309 """
310 310
311 311 opts,args = self.parse_options(parameter_s,'prn:')
312 312
313 313 try:
314 314 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
315 315 except MacroToEdit as e:
316 316 # TODO: Implement macro editing over 2 processes.
317 317 print("Macro editing not yet implemented in 2-process model.")
318 318 return
319 319
320 320 # Make sure we send to the client an absolute path, in case the working
321 321 # directory of client and kernel don't match
322 322 filename = os.path.abspath(filename)
323 323
324 324 payload = {
325 325 'source' : 'edit_magic',
326 326 'filename' : filename,
327 327 'line_number' : lineno
328 328 }
329 329 self.shell.payload_manager.write_payload(payload)
330 330
331 331 # A few magics that are adapted to the specifics of using pexpect and a
332 332 # remote terminal
333 333
334 334 @line_magic
335 335 def clear(self, arg_s):
336 336 """Clear the terminal."""
337 337 if os.name == 'posix':
338 338 self.shell.system("clear")
339 339 else:
340 340 self.shell.system("cls")
341 341
342 342 if os.name == 'nt':
343 343 # This is the usual name in windows
344 344 cls = line_magic('cls')(clear)
345 345
346 346 # Terminal pagers won't work over pexpect, but we do have our own pager
347 347
348 348 @line_magic
349 349 def less(self, arg_s):
350 350 """Show a file through the pager.
351 351
352 352 Files ending in .py are syntax-highlighted."""
353 353 if not arg_s:
354 354 raise UsageError('Missing filename.')
355 355
356 356 cont = open(arg_s).read()
357 357 if arg_s.endswith('.py'):
358 358 cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False))
359 359 else:
360 360 cont = open(arg_s).read()
361 361 page.page(cont)
362 362
363 363 more = line_magic('more')(less)
364 364
365 365 # Man calls a pager, so we also need to redefine it
366 366 if os.name == 'posix':
367 367 @line_magic
368 368 def man(self, arg_s):
369 369 """Find the man page for the given command and display in pager."""
370 370 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
371 371 split=False))
372 372
373 373 @line_magic
374 374 def connect_info(self, arg_s):
375 375 """Print information for connecting other clients to this kernel
376 376
377 377 It will print the contents of this session's connection file, as well as
378 378 shortcuts for local clients.
379 379
380 380 In the simplest case, when called from the most recently launched kernel,
381 381 secondary clients can be connected, simply with:
382 382
383 383 $> ipython <app> --existing
384 384
385 385 """
386 386
387 387 from IPython.core.application import BaseIPythonApplication as BaseIPApp
388 388
389 389 if BaseIPApp.initialized():
390 390 app = BaseIPApp.instance()
391 391 security_dir = app.profile_dir.security_dir
392 392 profile = app.profile
393 393 else:
394 394 profile = 'default'
395 395 security_dir = ''
396 396
397 397 try:
398 398 connection_file = get_connection_file()
399 399 info = get_connection_info(unpack=False)
400 400 except Exception as e:
401 401 error("Could not get connection info: %r" % e)
402 402 return
403 403
404 404 # add profile flag for non-default profile
405 405 profile_flag = "--profile %s" % profile if profile != 'default' else ""
406 406
407 407 # if it's in the security dir, truncate to basename
408 408 if security_dir == os.path.dirname(connection_file):
409 409 connection_file = os.path.basename(connection_file)
410 410
411 411
412 412 print (info + '\n')
413 413 print ("Paste the above JSON into a file, and connect with:\n"
414 414 " $> ipython <app> --existing <file>\n"
415 415 "or, if you are local, you can connect with just:\n"
416 416 " $> ipython <app> --existing {0} {1}\n"
417 417 "or even just:\n"
418 418 " $> ipython <app> --existing {1}\n"
419 419 "if this is the most recent IPython session you have started.".format(
420 420 connection_file, profile_flag
421 421 )
422 422 )
423 423
424 424 @line_magic
425 425 def qtconsole(self, arg_s):
426 426 """Open a qtconsole connected to this kernel.
427 427
428 428 Useful for connecting a qtconsole to running notebooks, for better
429 429 debugging.
430 430 """
431 431
432 432 # %qtconsole should imply bind_kernel for engines:
433 433 try:
434 434 from IPython.parallel import bind_kernel
435 435 except ImportError:
436 436 # technically possible, because parallel has higher pyzmq min-version
437 437 pass
438 438 else:
439 439 bind_kernel()
440 440
441 441 try:
442 442 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
443 443 except Exception as e:
444 444 error("Could not start qtconsole: %r" % e)
445 445 return
446 446
447 447 @line_magic
448 448 def autosave(self, arg_s):
449 449 """Set the autosave interval in the notebook (in seconds).
450 450
451 451 The default value is 120, or two minutes.
452 452 ``%autosave 0`` will disable autosave.
453 453
454 454 This magic only has an effect when called from the notebook interface.
455 455 It has no effect when called in a startup file.
456 456 """
457 457
458 458 try:
459 459 interval = int(arg_s)
460 460 except ValueError:
461 461 raise UsageError("%%autosave requires an integer, got %r" % arg_s)
462 462
463 463 # javascript wants milliseconds
464 464 milliseconds = 1000 * interval
465 465 display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds),
466 466 include=['application/javascript']
467 467 )
468 468 if interval:
469 469 print("Autosaving every %i seconds" % interval)
470 470 else:
471 471 print("Autosave disabled")
472 472
473 473
474 474 class ZMQInteractiveShell(InteractiveShell):
475 475 """A subclass of InteractiveShell for ZMQ."""
476 476
477 477 displayhook_class = Type(ZMQShellDisplayHook)
478 478 display_pub_class = Type(ZMQDisplayPublisher)
479 479 data_pub_class = Type(ZMQDataPublisher)
480 480 kernel = Any()
481 parent_header = Any()
481 482
482 483 # Override the traitlet in the parent class, because there's no point using
483 484 # readline for the kernel. Can be removed when the readline code is moved
484 485 # to the terminal frontend.
485 486 colors_force = CBool(True)
486 487 readline_use = CBool(False)
487 488 # autoindent has no meaning in a zmqshell, and attempting to enable it
488 489 # will print a warning in the absence of readline.
489 490 autoindent = CBool(False)
490 491
491 492 exiter = Instance(ZMQExitAutocall)
492 493 def _exiter_default(self):
493 494 return ZMQExitAutocall(self)
494 495
495 496 def _exit_now_changed(self, name, old, new):
496 497 """stop eventloop when exit_now fires"""
497 498 if new:
498 499 loop = ioloop.IOLoop.instance()
499 500 loop.add_timeout(time.time()+0.1, loop.stop)
500 501
501 502 keepkernel_on_exit = None
502 503
503 504 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
504 505 # interactive input being read; we provide event loop support in ipkernel
505 506 @staticmethod
506 507 def enable_gui(gui):
507 508 from .eventloops import enable_gui as real_enable_gui
508 509 try:
509 510 real_enable_gui(gui)
510 511 except ValueError as e:
511 512 raise UsageError("%s" % e)
512 513
513 514 def init_environment(self):
514 515 """Configure the user's environment.
515 516
516 517 """
517 518 env = os.environ
518 519 # These two ensure 'ls' produces nice coloring on BSD-derived systems
519 520 env['TERM'] = 'xterm-color'
520 521 env['CLICOLOR'] = '1'
521 522 # Since normal pagers don't work at all (over pexpect we don't have
522 523 # single-key control of the subprocess), try to disable paging in
523 524 # subprocesses as much as possible.
524 525 env['PAGER'] = 'cat'
525 526 env['GIT_PAGER'] = 'cat'
526 527
527 528 # And install the payload version of page.
528 529 install_payload_page()
529 530
530 531 def auto_rewrite_input(self, cmd):
531 532 """Called to show the auto-rewritten input for autocall and friends.
532 533
533 534 FIXME: this payload is currently not correctly processed by the
534 535 frontend.
535 536 """
536 537 new = self.prompt_manager.render('rewrite') + cmd
537 538 payload = dict(
538 539 source='auto_rewrite_input',
539 540 transformed_input=new,
540 541 )
541 542 self.payload_manager.write_payload(payload)
542 543
543 544 def ask_exit(self):
544 545 """Engage the exit actions."""
545 546 self.exit_now = True
546 547 payload = dict(
547 548 source='ask_exit',
548 549 exit=True,
549 550 keepkernel=self.keepkernel_on_exit,
550 551 )
551 552 self.payload_manager.write_payload(payload)
552 553
553 554 def _showtraceback(self, etype, evalue, stb):
554 555
555 556 exc_content = {
556 557 u'traceback' : stb,
557 558 u'ename' : unicode(etype.__name__),
558 559 u'evalue' : py3compat.safe_unicode(evalue),
559 560 }
560 561
561 562 dh = self.displayhook
562 563 # Send exception info over pub socket for other clients than the caller
563 564 # to pick up
564 565 topic = None
565 566 if dh.topic:
566 567 topic = dh.topic.replace(b'pyout', b'pyerr')
567 568
568 569 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
569 570
570 571 # FIXME - Hack: store exception info in shell object. Right now, the
571 572 # caller is reading this info after the fact, we need to fix this logic
572 573 # to remove this hack. Even uglier, we need to store the error status
573 574 # here, because in the main loop, the logic that sets it is being
574 575 # skipped because runlines swallows the exceptions.
575 576 exc_content[u'status'] = u'error'
576 577 self._reply_content = exc_content
577 578 # /FIXME
578 579
579 580 return exc_content
580 581
581 582 def set_next_input(self, text):
582 583 """Send the specified text to the frontend to be presented at the next
583 584 input cell."""
584 585 payload = dict(
585 586 source='set_next_input',
586 587 text=text
587 588 )
588 589 self.payload_manager.write_payload(payload)
589 590
590 591 def set_parent(self, parent):
591 592 """Set the parent header for associating output with its triggering input"""
593 self.parent_header = parent
592 594 self.displayhook.set_parent(parent)
593 595 self.display_pub.set_parent(parent)
594 596 self.data_pub.set_parent(parent)
595 597 try:
596 598 sys.stdout.set_parent(parent)
597 599 except AttributeError:
598 600 pass
599 601 try:
600 602 sys.stderr.set_parent(parent)
601 603 except AttributeError:
602 604 pass
603 605
606 def get_parent(self):
607 return self.parent_header
608
604 609 #-------------------------------------------------------------------------
605 610 # Things related to magics
606 611 #-------------------------------------------------------------------------
607 612
608 613 def init_magics(self):
609 614 super(ZMQInteractiveShell, self).init_magics()
610 615 self.register_magics(KernelMagics)
611 616 self.magics_manager.register_alias('ed', 'edit')
612 617
613 618 def init_comms(self):
614 619 self.comm_manager = CommManager(shell=self, parent=self)
615 620 self.configurables.append(self.comm_manager)
616 621
617 622
618 623 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now