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