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