##// END OF EJS Templates
Fixing small bug in display logic....
Brian Granger -
Show More
@@ -1,184 +1,184 b''
1 1 import os
2 2 import uuid
3 3 import pprint
4 4
5 5 import zmq
6 6
7 7 from zmq.utils import jsonapi as json
8 8
9 9 class Message(object):
10 10 """A simple message object that maps dict keys to attributes.
11 11
12 12 A Message can be created from a dict and a dict from a Message instance
13 13 simply by calling dict(msg_obj)."""
14 14
15 15 def __init__(self, msg_dict):
16 16 dct = self.__dict__
17 17 for k, v in msg_dict.iteritems():
18 18 if isinstance(v, dict):
19 19 v = Message(v)
20 20 dct[k] = v
21 21
22 22 # Having this iterator lets dict(msg_obj) work out of the box.
23 23 def __iter__(self):
24 24 return iter(self.__dict__.iteritems())
25 25
26 26 def __repr__(self):
27 27 return repr(self.__dict__)
28 28
29 29 def __str__(self):
30 30 return pprint.pformat(self.__dict__)
31 31
32 32 def __contains__(self, k):
33 33 return k in self.__dict__
34 34
35 35 def __getitem__(self, k):
36 36 return self.__dict__[k]
37 37
38 38
39 39 def msg_header(msg_id, username, session):
40 40 return {
41 41 'msg_id' : msg_id,
42 42 'username' : username,
43 43 'session' : session
44 44 }
45 45
46 46
47 47 def extract_header(msg_or_header):
48 48 """Given a message or header, return the header."""
49 49 if not msg_or_header:
50 50 return {}
51 51 try:
52 52 # See if msg_or_header is the entire message.
53 53 h = msg_or_header['header']
54 54 except KeyError:
55 55 try:
56 56 # See if msg_or_header is just the header
57 57 h = msg_or_header['msg_id']
58 58 except KeyError:
59 59 raise
60 60 else:
61 61 h = msg_or_header
62 62 if not isinstance(h, dict):
63 63 h = dict(h)
64 64 return h
65 65
66 66
67 67 class Session(object):
68 68
69 69 def __init__(self, username=os.environ.get('USER','username'), session=None):
70 70 self.username = username
71 71 if session is None:
72 72 self.session = str(uuid.uuid4())
73 73 else:
74 74 self.session = session
75 75 self.msg_id = 0
76 76
77 77 def msg_header(self):
78 78 h = msg_header(self.msg_id, self.username, self.session)
79 79 self.msg_id += 1
80 80 return h
81 81
82 82 def msg(self, msg_type, content=None, parent=None):
83 83 """Construct a standard-form message, with a given type, content, and parent.
84 84
85 85 NOT to be called directly.
86 86 """
87 87 msg = {}
88 88 msg['header'] = self.msg_header()
89 89 msg['parent_header'] = {} if parent is None else extract_header(parent)
90 90 msg['msg_type'] = msg_type
91 91 msg['content'] = {} if content is None else content
92 92 return msg
93 93
94 94 def send(self, socket, msg_or_type, content=None, parent=None, ident=None):
95 95 """send a message via a socket, using a uniform message pattern.
96 96
97 97 Parameters
98 98 ----------
99 99 socket : zmq.Socket
100 100 The socket on which to send.
101 101 msg_or_type : Message/dict or str
102 102 if str : then a new message will be constructed from content,parent
103 103 if Message/dict : then content and parent are ignored, and the message
104 104 is sent. This is only for use when sending a Message for a second time.
105 105 content : dict, optional
106 106 The contents of the message
107 107 parent : dict, optional
108 108 The parent header, or parent message, of this message
109 109 ident : bytes, optional
110 110 The zmq.IDENTITY prefix of the destination.
111 111 Only for use on certain socket types.
112 112
113 113 Returns
114 114 -------
115 115 msg : dict
116 116 The message, as constructed by self.msg(msg_type,content,parent)
117 117 """
118 118 if isinstance(msg_or_type, (Message, dict)):
119 119 msg = dict(msg_or_type)
120 120 else:
121 121 msg = self.msg(msg_or_type, content, parent)
122 122 if ident is not None:
123 123 socket.send(ident, zmq.SNDMORE)
124 124 socket.send_json(msg)
125 125 return msg
126
126
127 127 def recv(self, socket, mode=zmq.NOBLOCK):
128 128 """recv a message on a socket.
129 129
130 130 Receive an optionally identity-prefixed message, as sent via session.send().
131 131
132 132 Parameters
133 133 ----------
134 134
135 135 socket : zmq.Socket
136 136 The socket on which to recv a message.
137 137 mode : int, optional
138 138 the mode flag passed to socket.recv
139 139 default: zmq.NOBLOCK
140 140
141 141 Returns
142 142 -------
143 143 (ident,msg) : tuple
144 144 always length 2. If no message received, then return is (None,None)
145 145 ident : bytes or None
146 146 the identity prefix is there was one, None otherwise.
147 147 msg : dict or None
148 148 The actual message. If mode==zmq.NOBLOCK and no message was waiting,
149 149 it will be None.
150 150 """
151 151 try:
152 152 msg = socket.recv_multipart(mode)
153 153 except zmq.ZMQError, e:
154 154 if e.errno == zmq.EAGAIN:
155 155 # We can convert EAGAIN to None as we know in this case
156 156 # recv_json won't return None.
157 157 return None,None
158 158 else:
159 159 raise
160 160 if len(msg) == 1:
161 161 ident=None
162 162 msg = msg[0]
163 163 elif len(msg) == 2:
164 164 ident, msg = msg
165 165 else:
166 166 raise ValueError("Got message with length > 2, which is invalid")
167 167
168 168 return ident, json.loads(msg)
169 169
170 170 def test_msg2obj():
171 171 am = dict(x=1)
172 172 ao = Message(am)
173 173 assert ao.x == am['x']
174 174
175 175 am['y'] = dict(z=1)
176 176 ao = Message(am)
177 177 assert ao.y.z == am['y']['z']
178 178
179 179 k1, k2 = 'y', 'z'
180 180 assert ao[k1][k2] == am[k1][k2]
181 181
182 182 am2 = dict(ao)
183 183 assert am['x'] == am2['x']
184 184 assert am['y']['z'] == am2['y']['z']
@@ -1,592 +1,598 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 from base64 import encodestring
20 20 import inspect
21 21 import os
22 22
23 23 # Our own
24 24 from IPython.core.interactiveshell import (
25 25 InteractiveShell, InteractiveShellABC
26 26 )
27 27 from IPython.core import page
28 28 from IPython.core.autocall import ZMQExitAutocall
29 29 from IPython.core.displayhook import DisplayHook
30 30 from IPython.core.displaypub import DisplayPublisher
31 31 from IPython.core.macro import Macro
32 32 from IPython.core.payloadpage import install_payload_page
33 33 from IPython.utils import io
34 34 from IPython.utils.path import get_py_filename
35 35 from IPython.utils.traitlets import Instance, Type, Dict
36 36 from IPython.utils.warn import warn
37 37 from IPython.zmq.session import extract_header
38 38 from session import Session
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Globals and side-effects
42 42 #-----------------------------------------------------------------------------
43 43
44 44 # Install the payload version of page.
45 45 install_payload_page()
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Functions and classes
49 49 #-----------------------------------------------------------------------------
50 50
51 def _encode_png(data):
52 pngdata = data.get('image/png')
53 if pngdata is not None:
54 data['image/png'] = encodestring(pngdata)
55
56
51 57 class ZMQDisplayHook(DisplayHook):
52 58 """A displayhook subclass that publishes data using ZeroMQ."""
53 59
54 60 session = Instance(Session)
55 61 pub_socket = Instance('zmq.Socket')
56 62 parent_header = Dict({})
57 63
58 64 def set_parent(self, parent):
59 65 """Set the parent for outbound messages."""
60 66 self.parent_header = extract_header(parent)
61 67
62 68 def start_displayhook(self):
63 69 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
64 70
65 71 def write_output_prompt(self):
66 72 """Write the output prompt."""
67 73 if self.do_full_cache:
68 74 self.msg['content']['execution_count'] = self.prompt_count
69 75
70 76 def write_format_data(self, format_dict):
71 77 pngdata = format_dict.get('image/png')
72 if pngdata is not None:
73 format_dict['image/png'] = encodestring(pngdata)
78 _encode_png(format_dict)
74 79 self.msg['content']['data'] = format_dict
75 80
76 81 def finish_displayhook(self):
77 82 """Finish up all displayhook activities."""
78 83 self.session.send(self.pub_socket, self.msg)
79 84 self.msg = None
80 85
81 86
82 87 class ZMQDisplayPublisher(DisplayPublisher):
83 88 """A display publisher that publishes data using a ZeroMQ PUB socket."""
84 89
85 90 session = Instance(Session)
86 91 pub_socket = Instance('zmq.Socket')
87 92 parent_header = Dict({})
88 93
89 94 def set_parent(self, parent):
90 95 """Set the parent for outbound messages."""
91 96 self.parent_header = extract_header(parent)
92 97
93 98 def publish(self, source, data, metadata=None):
94 99 if metadata is None:
95 100 metadata = {}
96 101 self._validate_data(source, data, metadata)
97 102 content = {}
98 103 content['source'] = source
104 _encode_png(data)
99 105 content['data'] = data
100 106 content['metadata'] = metadata
101 107 self.session.send(
102 108 self.pub_socket, u'display_data', content,
103 109 parent=self.parent_header
104 110 )
105 111
106 112
107 113 class ZMQInteractiveShell(InteractiveShell):
108 114 """A subclass of InteractiveShell for ZMQ."""
109 115
110 116 displayhook_class = Type(ZMQDisplayHook)
111 117 display_pub_class = Type(ZMQDisplayPublisher)
112 118
113 119 exiter = Instance(ZMQExitAutocall)
114 120 def _exiter_default(self):
115 121 return ZMQExitAutocall(self)
116 122
117 123 keepkernel_on_exit = None
118 124
119 125 def init_environment(self):
120 126 """Configure the user's environment.
121 127
122 128 """
123 129 env = os.environ
124 130 # These two ensure 'ls' produces nice coloring on BSD-derived systems
125 131 env['TERM'] = 'xterm-color'
126 132 env['CLICOLOR'] = '1'
127 133 # Since normal pagers don't work at all (over pexpect we don't have
128 134 # single-key control of the subprocess), try to disable paging in
129 135 # subprocesses as much as possible.
130 136 env['PAGER'] = 'cat'
131 137 env['GIT_PAGER'] = 'cat'
132 138
133 139 def auto_rewrite_input(self, cmd):
134 140 """Called to show the auto-rewritten input for autocall and friends.
135 141
136 142 FIXME: this payload is currently not correctly processed by the
137 143 frontend.
138 144 """
139 145 new = self.displayhook.prompt1.auto_rewrite() + cmd
140 146 payload = dict(
141 147 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
142 148 transformed_input=new,
143 149 )
144 150 self.payload_manager.write_payload(payload)
145 151
146 152 def ask_exit(self):
147 153 """Engage the exit actions."""
148 154 payload = dict(
149 155 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
150 156 exit=True,
151 157 keepkernel=self.keepkernel_on_exit,
152 158 )
153 159 self.payload_manager.write_payload(payload)
154 160
155 161 def _showtraceback(self, etype, evalue, stb):
156 162
157 163 exc_content = {
158 164 u'traceback' : stb,
159 165 u'ename' : unicode(etype.__name__),
160 166 u'evalue' : unicode(evalue)
161 167 }
162 168
163 169 dh = self.displayhook
164 170 # Send exception info over pub socket for other clients than the caller
165 171 # to pick up
166 172 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', exc_content, dh.parent_header)
167 173
168 174 # FIXME - Hack: store exception info in shell object. Right now, the
169 175 # caller is reading this info after the fact, we need to fix this logic
170 176 # to remove this hack. Even uglier, we need to store the error status
171 177 # here, because in the main loop, the logic that sets it is being
172 178 # skipped because runlines swallows the exceptions.
173 179 exc_content[u'status'] = u'error'
174 180 self._reply_content = exc_content
175 181 # /FIXME
176 182
177 183 return exc_content
178 184
179 185 #------------------------------------------------------------------------
180 186 # Magic overrides
181 187 #------------------------------------------------------------------------
182 188 # Once the base class stops inheriting from magic, this code needs to be
183 189 # moved into a separate machinery as well. For now, at least isolate here
184 190 # the magics which this class needs to implement differently from the base
185 191 # class, or that are unique to it.
186 192
187 193 def magic_doctest_mode(self,parameter_s=''):
188 194 """Toggle doctest mode on and off.
189 195
190 196 This mode is intended to make IPython behave as much as possible like a
191 197 plain Python shell, from the perspective of how its prompts, exceptions
192 198 and output look. This makes it easy to copy and paste parts of a
193 199 session into doctests. It does so by:
194 200
195 201 - Changing the prompts to the classic ``>>>`` ones.
196 202 - Changing the exception reporting mode to 'Plain'.
197 203 - Disabling pretty-printing of output.
198 204
199 205 Note that IPython also supports the pasting of code snippets that have
200 206 leading '>>>' and '...' prompts in them. This means that you can paste
201 207 doctests from files or docstrings (even if they have leading
202 208 whitespace), and the code will execute correctly. You can then use
203 209 '%history -t' to see the translated history; this will give you the
204 210 input after removal of all the leading prompts and whitespace, which
205 211 can be pasted back into an editor.
206 212
207 213 With these features, you can switch into this mode easily whenever you
208 214 need to do testing and changes to doctests, without having to leave
209 215 your existing IPython session.
210 216 """
211 217
212 218 from IPython.utils.ipstruct import Struct
213 219
214 220 # Shorthands
215 221 shell = self.shell
216 222 disp_formatter = self.shell.display_formatter
217 223 ptformatter = disp_formatter.formatters['text/plain']
218 224 # dstore is a data store kept in the instance metadata bag to track any
219 225 # changes we make, so we can undo them later.
220 226 dstore = shell.meta.setdefault('doctest_mode', Struct())
221 227 save_dstore = dstore.setdefault
222 228
223 229 # save a few values we'll need to recover later
224 230 mode = save_dstore('mode', False)
225 231 save_dstore('rc_pprint', ptformatter.pprint)
226 232 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
227 233 save_dstore('xmode', shell.InteractiveTB.mode)
228 234
229 235 if mode == False:
230 236 # turn on
231 237 ptformatter.pprint = False
232 238 disp_formatter.plain_text_only = True
233 239 shell.magic_xmode('Plain')
234 240 else:
235 241 # turn off
236 242 ptformatter.pprint = dstore.rc_pprint
237 243 disp_formatter.plain_text_only = dstore.rc_plain_text_only
238 244 shell.magic_xmode(dstore.xmode)
239 245
240 246 # Store new mode and inform on console
241 247 dstore.mode = bool(1-int(mode))
242 248 mode_label = ['OFF','ON'][dstore.mode]
243 249 print('Doctest mode is:', mode_label)
244 250
245 251 # Send the payload back so that clients can modify their prompt display
246 252 payload = dict(
247 253 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
248 254 mode=dstore.mode)
249 255 self.payload_manager.write_payload(payload)
250 256
251 257 def magic_edit(self,parameter_s='',last_call=['','']):
252 258 """Bring up an editor and execute the resulting code.
253 259
254 260 Usage:
255 261 %edit [options] [args]
256 262
257 263 %edit runs IPython's editor hook. The default version of this hook is
258 264 set to call the __IPYTHON__.rc.editor command. This is read from your
259 265 environment variable $EDITOR. If this isn't found, it will default to
260 266 vi under Linux/Unix and to notepad under Windows. See the end of this
261 267 docstring for how to change the editor hook.
262 268
263 269 You can also set the value of this editor via the command line option
264 270 '-editor' or in your ipythonrc file. This is useful if you wish to use
265 271 specifically for IPython an editor different from your typical default
266 272 (and for Windows users who typically don't set environment variables).
267 273
268 274 This command allows you to conveniently edit multi-line code right in
269 275 your IPython session.
270 276
271 277 If called without arguments, %edit opens up an empty editor with a
272 278 temporary file and will execute the contents of this file when you
273 279 close it (don't forget to save it!).
274 280
275 281
276 282 Options:
277 283
278 284 -n <number>: open the editor at a specified line number. By default,
279 285 the IPython editor hook uses the unix syntax 'editor +N filename', but
280 286 you can configure this by providing your own modified hook if your
281 287 favorite editor supports line-number specifications with a different
282 288 syntax.
283 289
284 290 -p: this will call the editor with the same data as the previous time
285 291 it was used, regardless of how long ago (in your current session) it
286 292 was.
287 293
288 294 -r: use 'raw' input. This option only applies to input taken from the
289 295 user's history. By default, the 'processed' history is used, so that
290 296 magics are loaded in their transformed version to valid Python. If
291 297 this option is given, the raw input as typed as the command line is
292 298 used instead. When you exit the editor, it will be executed by
293 299 IPython's own processor.
294 300
295 301 -x: do not execute the edited code immediately upon exit. This is
296 302 mainly useful if you are editing programs which need to be called with
297 303 command line arguments, which you can then do using %run.
298 304
299 305
300 306 Arguments:
301 307
302 308 If arguments are given, the following possibilites exist:
303 309
304 310 - The arguments are numbers or pairs of colon-separated numbers (like
305 311 1 4:8 9). These are interpreted as lines of previous input to be
306 312 loaded into the editor. The syntax is the same of the %macro command.
307 313
308 314 - If the argument doesn't start with a number, it is evaluated as a
309 315 variable and its contents loaded into the editor. You can thus edit
310 316 any string which contains python code (including the result of
311 317 previous edits).
312 318
313 319 - If the argument is the name of an object (other than a string),
314 320 IPython will try to locate the file where it was defined and open the
315 321 editor at the point where it is defined. You can use `%edit function`
316 322 to load an editor exactly at the point where 'function' is defined,
317 323 edit it and have the file be executed automatically.
318 324
319 325 If the object is a macro (see %macro for details), this opens up your
320 326 specified editor with a temporary file containing the macro's data.
321 327 Upon exit, the macro is reloaded with the contents of the file.
322 328
323 329 Note: opening at an exact line is only supported under Unix, and some
324 330 editors (like kedit and gedit up to Gnome 2.8) do not understand the
325 331 '+NUMBER' parameter necessary for this feature. Good editors like
326 332 (X)Emacs, vi, jed, pico and joe all do.
327 333
328 334 - If the argument is not found as a variable, IPython will look for a
329 335 file with that name (adding .py if necessary) and load it into the
330 336 editor. It will execute its contents with execfile() when you exit,
331 337 loading any code in the file into your interactive namespace.
332 338
333 339 After executing your code, %edit will return as output the code you
334 340 typed in the editor (except when it was an existing file). This way
335 341 you can reload the code in further invocations of %edit as a variable,
336 342 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
337 343 the output.
338 344
339 345 Note that %edit is also available through the alias %ed.
340 346
341 347 This is an example of creating a simple function inside the editor and
342 348 then modifying it. First, start up the editor:
343 349
344 350 In [1]: ed
345 351 Editing... done. Executing edited code...
346 352 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
347 353
348 354 We can then call the function foo():
349 355
350 356 In [2]: foo()
351 357 foo() was defined in an editing session
352 358
353 359 Now we edit foo. IPython automatically loads the editor with the
354 360 (temporary) file where foo() was previously defined:
355 361
356 362 In [3]: ed foo
357 363 Editing... done. Executing edited code...
358 364
359 365 And if we call foo() again we get the modified version:
360 366
361 367 In [4]: foo()
362 368 foo() has now been changed!
363 369
364 370 Here is an example of how to edit a code snippet successive
365 371 times. First we call the editor:
366 372
367 373 In [5]: ed
368 374 Editing... done. Executing edited code...
369 375 hello
370 376 Out[5]: "print 'hello'n"
371 377
372 378 Now we call it again with the previous output (stored in _):
373 379
374 380 In [6]: ed _
375 381 Editing... done. Executing edited code...
376 382 hello world
377 383 Out[6]: "print 'hello world'n"
378 384
379 385 Now we call it with the output #8 (stored in _8, also as Out[8]):
380 386
381 387 In [7]: ed _8
382 388 Editing... done. Executing edited code...
383 389 hello again
384 390 Out[7]: "print 'hello again'n"
385 391
386 392
387 393 Changing the default editor hook:
388 394
389 395 If you wish to write your own editor hook, you can put it in a
390 396 configuration file which you load at startup time. The default hook
391 397 is defined in the IPython.core.hooks module, and you can use that as a
392 398 starting example for further modifications. That file also has
393 399 general instructions on how to set a new hook for use once you've
394 400 defined it."""
395 401
396 402 # FIXME: This function has become a convoluted mess. It needs a
397 403 # ground-up rewrite with clean, simple logic.
398 404
399 405 def make_filename(arg):
400 406 "Make a filename from the given args"
401 407 try:
402 408 filename = get_py_filename(arg)
403 409 except IOError:
404 410 if args.endswith('.py'):
405 411 filename = arg
406 412 else:
407 413 filename = None
408 414 return filename
409 415
410 416 # custom exceptions
411 417 class DataIsObject(Exception): pass
412 418
413 419 opts,args = self.parse_options(parameter_s,'prn:')
414 420 # Set a few locals from the options for convenience:
415 421 opts_p = opts.has_key('p')
416 422 opts_r = opts.has_key('r')
417 423
418 424 # Default line number value
419 425 lineno = opts.get('n',None)
420 426 if lineno is not None:
421 427 try:
422 428 lineno = int(lineno)
423 429 except:
424 430 warn("The -n argument must be an integer.")
425 431 return
426 432
427 433 if opts_p:
428 434 args = '_%s' % last_call[0]
429 435 if not self.shell.user_ns.has_key(args):
430 436 args = last_call[1]
431 437
432 438 # use last_call to remember the state of the previous call, but don't
433 439 # let it be clobbered by successive '-p' calls.
434 440 try:
435 441 last_call[0] = self.shell.displayhook.prompt_count
436 442 if not opts_p:
437 443 last_call[1] = parameter_s
438 444 except:
439 445 pass
440 446
441 447 # by default this is done with temp files, except when the given
442 448 # arg is a filename
443 449 use_temp = True
444 450
445 451 data = ''
446 452 if args[0].isdigit():
447 453 # Mode where user specifies ranges of lines, like in %macro.
448 454 # This means that you can't edit files whose names begin with
449 455 # numbers this way. Tough.
450 456 ranges = args.split()
451 457 data = ''.join(self.extract_input_slices(ranges,opts_r))
452 458 elif args.endswith('.py'):
453 459 filename = make_filename(args)
454 460 use_temp = False
455 461 elif args:
456 462 try:
457 463 # Load the parameter given as a variable. If not a string,
458 464 # process it as an object instead (below)
459 465
460 466 #print '*** args',args,'type',type(args) # dbg
461 467 data = eval(args, self.shell.user_ns)
462 468 if not isinstance(data, basestring):
463 469 raise DataIsObject
464 470
465 471 except (NameError,SyntaxError):
466 472 # given argument is not a variable, try as a filename
467 473 filename = make_filename(args)
468 474 if filename is None:
469 475 warn("Argument given (%s) can't be found as a variable "
470 476 "or as a filename." % args)
471 477 return
472 478 use_temp = False
473 479
474 480 except DataIsObject:
475 481 # macros have a special edit function
476 482 if isinstance(data, Macro):
477 483 self._edit_macro(args,data)
478 484 return
479 485
480 486 # For objects, try to edit the file where they are defined
481 487 try:
482 488 filename = inspect.getabsfile(data)
483 489 if 'fakemodule' in filename.lower() and inspect.isclass(data):
484 490 # class created by %edit? Try to find source
485 491 # by looking for method definitions instead, the
486 492 # __module__ in those classes is FakeModule.
487 493 attrs = [getattr(data, aname) for aname in dir(data)]
488 494 for attr in attrs:
489 495 if not inspect.ismethod(attr):
490 496 continue
491 497 filename = inspect.getabsfile(attr)
492 498 if filename and 'fakemodule' not in filename.lower():
493 499 # change the attribute to be the edit target instead
494 500 data = attr
495 501 break
496 502
497 503 datafile = 1
498 504 except TypeError:
499 505 filename = make_filename(args)
500 506 datafile = 1
501 507 warn('Could not find file where `%s` is defined.\n'
502 508 'Opening a file named `%s`' % (args,filename))
503 509 # Now, make sure we can actually read the source (if it was in
504 510 # a temp file it's gone by now).
505 511 if datafile:
506 512 try:
507 513 if lineno is None:
508 514 lineno = inspect.getsourcelines(data)[1]
509 515 except IOError:
510 516 filename = make_filename(args)
511 517 if filename is None:
512 518 warn('The file `%s` where `%s` was defined cannot '
513 519 'be read.' % (filename,data))
514 520 return
515 521 use_temp = False
516 522
517 523 if use_temp:
518 524 filename = self.shell.mktempfile(data)
519 525 print('IPython will make a temporary file named:', filename)
520 526
521 527 # Make sure we send to the client an absolute path, in case the working
522 528 # directory of client and kernel don't match
523 529 filename = os.path.abspath(filename)
524 530
525 531 payload = {
526 532 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
527 533 'filename' : filename,
528 534 'line_number' : lineno
529 535 }
530 536 self.payload_manager.write_payload(payload)
531 537
532 538 def magic_gui(self, *args, **kwargs):
533 539 raise NotImplementedError(
534 540 'GUI support must be enabled in command line options.')
535 541
536 542 def magic_pylab(self, *args, **kwargs):
537 543 raise NotImplementedError(
538 544 'pylab support must be enabled in command line options.')
539 545
540 546 # A few magics that are adapted to the specifics of using pexpect and a
541 547 # remote terminal
542 548
543 549 def magic_clear(self, arg_s):
544 550 """Clear the terminal."""
545 551 if os.name == 'posix':
546 552 self.shell.system("clear")
547 553 else:
548 554 self.shell.system("cls")
549 555
550 556 if os.name == 'nt':
551 557 # This is the usual name in windows
552 558 magic_cls = magic_clear
553 559
554 560 # Terminal pagers won't work over pexpect, but we do have our own pager
555 561
556 562 def magic_less(self, arg_s):
557 563 """Show a file through the pager.
558 564
559 565 Files ending in .py are syntax-highlighted."""
560 566 cont = open(arg_s).read()
561 567 if arg_s.endswith('.py'):
562 568 cont = self.shell.pycolorize(cont)
563 569 page.page(cont)
564 570
565 571 magic_more = magic_less
566 572
567 573 # Man calls a pager, so we also need to redefine it
568 574 if os.name == 'posix':
569 575 def magic_man(self, arg_s):
570 576 """Find the man page for the given command and display in pager."""
571 577 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
572 578 split=False))
573 579
574 580 # FIXME: this is specific to the GUI, so we should let the gui app load
575 581 # magics at startup that are only for the gui. Once the gui app has proper
576 582 # profile and configuration management, we can have it initialize a kernel
577 583 # with a special config file that provides these.
578 584 def magic_guiref(self, arg_s):
579 585 """Show a basic reference about the GUI console."""
580 586 from IPython.core.usage import gui_reference
581 587 page.page(gui_reference, auto_html=True)
582 588
583 589 def set_next_input(self, text):
584 590 """Send the specified text to the frontend to be presented at the next
585 591 input cell."""
586 592 payload = dict(
587 593 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
588 594 text=text
589 595 )
590 596 self.payload_manager.write_payload(payload)
591 597
592 598 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now