##// END OF EJS Templates
Finishing display system work....
Brian E. Granger -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,274 +1,373 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats.
2 """Top-level display functions for displaying object in different formats.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2010 The IPython Development Team
10 # Copyright (C) 2008-2010 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from .displaypub import (
20 from .displaypub import (
21 publish_pretty, publish_html,
21 publish_pretty, publish_html,
22 publish_latex, publish_svg,
22 publish_latex, publish_svg,
23 publish_png, publish_json,
23 publish_png, publish_json,
24 publish_javascript
24 publish_javascript, publish_jpeg
25 )
25 )
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Main functions
28 # Main functions
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 def display(*objs, **kwargs):
31 def display(*objs, **kwargs):
32 """Display a Python object in all frontends.
32 """Display a Python object in all frontends.
33
33
34 By default all representations will be computed and sent to the frontends.
34 By default all representations will be computed and sent to the frontends.
35 Frontends can decide which representation is used and how.
35 Frontends can decide which representation is used and how.
36
36
37 Parameters
37 Parameters
38 ----------
38 ----------
39 objs : tuple of objects
39 objs : tuple of objects
40 The Python objects to display.
40 The Python objects to display.
41 include : list or tuple, optional
41 include : list or tuple, optional
42 A list of format type strings (MIME types) to include in the
42 A list of format type strings (MIME types) to include in the
43 format data dict. If this is set *only* the format types included
43 format data dict. If this is set *only* the format types included
44 in this list will be computed.
44 in this list will be computed.
45 exclude : list or tuple, optional
45 exclude : list or tuple, optional
46 A list of format type string (MIME types) to exclue in the format
46 A list of format type string (MIME types) to exclue in the format
47 data dict. If this is set all format types will be computed,
47 data dict. If this is set all format types will be computed,
48 except for those included in this argument.
48 except for those included in this argument.
49 """
49 """
50 include = kwargs.get('include')
50 include = kwargs.get('include')
51 exclude = kwargs.get('exclude')
51 exclude = kwargs.get('exclude')
52
52
53 from IPython.core.interactiveshell import InteractiveShell
53 from IPython.core.interactiveshell import InteractiveShell
54 inst = InteractiveShell.instance()
54 inst = InteractiveShell.instance()
55 format = inst.display_formatter.format
55 format = inst.display_formatter.format
56 publish = inst.display_pub.publish
56 publish = inst.display_pub.publish
57
57
58 for obj in objs:
58 for obj in objs:
59 format_dict = format(obj, include=include, exclude=exclude)
59 format_dict = format(obj, include=include, exclude=exclude)
60 publish('IPython.core.display.display', format_dict)
60 publish('IPython.core.display.display', format_dict)
61
61
62
62
63 def display_pretty(*objs, **kwargs):
63 def display_pretty(*objs, **kwargs):
64 """Display the pretty (default) representation of an object.
64 """Display the pretty (default) representation of an object.
65
65
66 Parameters
66 Parameters
67 ----------
67 ----------
68 objs : tuple of objects
68 objs : tuple of objects
69 The Python objects to display, or if raw=True raw text data to
69 The Python objects to display, or if raw=True raw text data to
70 display.
70 display.
71 raw : bool
71 raw : bool
72 Are the data objects raw data or Python objects that need to be
72 Are the data objects raw data or Python objects that need to be
73 formatted before display? [default: False]
73 formatted before display? [default: False]
74 """
74 """
75 raw = kwargs.pop('raw',False)
75 raw = kwargs.pop('raw',False)
76 if raw:
76 if raw:
77 for obj in objs:
77 for obj in objs:
78 publish_pretty(obj)
78 publish_pretty(obj)
79 else:
79 else:
80 display(*objs, include=['text/plain'])
80 display(*objs, include=['text/plain'])
81
81
82
82
83 def display_html(*objs, **kwargs):
83 def display_html(*objs, **kwargs):
84 """Display the HTML representation of an object.
84 """Display the HTML representation of an object.
85
85
86 Parameters
86 Parameters
87 ----------
87 ----------
88 objs : tuple of objects
88 objs : tuple of objects
89 The Python objects to display, or if raw=True raw html data to
89 The Python objects to display, or if raw=True raw HTML data to
90 display.
90 display.
91 raw : bool
91 raw : bool
92 Are the data objects raw data or Python objects that need to be
92 Are the data objects raw data or Python objects that need to be
93 formatted before display? [default: False]
93 formatted before display? [default: False]
94 """
94 """
95 raw = kwargs.pop('raw',False)
95 raw = kwargs.pop('raw',False)
96 if raw:
96 if raw:
97 for obj in objs:
97 for obj in objs:
98 publish_html(obj)
98 publish_html(obj)
99 else:
99 else:
100 display(*objs, include=['text/plain','text/html'])
100 display(*objs, include=['text/plain','text/html'])
101
101
102
102
103 def display_svg(*objs, **kwargs):
103 def display_svg(*objs, **kwargs):
104 """Display the SVG representation of an object.
104 """Display the SVG representation of an object.
105
105
106 Parameters
106 Parameters
107 ----------
107 ----------
108 objs : tuple of objects
108 objs : tuple of objects
109 The Python objects to display, or if raw=True raw svg data to
109 The Python objects to display, or if raw=True raw svg data to
110 display.
110 display.
111 raw : bool
111 raw : bool
112 Are the data objects raw data or Python objects that need to be
112 Are the data objects raw data or Python objects that need to be
113 formatted before display? [default: False]
113 formatted before display? [default: False]
114 """
114 """
115 raw = kwargs.pop('raw',False)
115 raw = kwargs.pop('raw',False)
116 if raw:
116 if raw:
117 for obj in objs:
117 for obj in objs:
118 publish_svg(obj)
118 publish_svg(obj)
119 else:
119 else:
120 display(*objs, include=['text/plain','image/svg+xml'])
120 display(*objs, include=['text/plain','image/svg+xml'])
121
121
122
122
123 def display_png(*objs, **kwargs):
123 def display_png(*objs, **kwargs):
124 """Display the PNG representation of an object.
124 """Display the PNG representation of an object.
125
125
126 Parameters
126 Parameters
127 ----------
127 ----------
128 objs : tuple of objects
128 objs : tuple of objects
129 The Python objects to display, or if raw=True raw png data to
129 The Python objects to display, or if raw=True raw png data to
130 display.
130 display.
131 raw : bool
131 raw : bool
132 Are the data objects raw data or Python objects that need to be
132 Are the data objects raw data or Python objects that need to be
133 formatted before display? [default: False]
133 formatted before display? [default: False]
134 """
134 """
135 raw = kwargs.pop('raw',False)
135 raw = kwargs.pop('raw',False)
136 if raw:
136 if raw:
137 for obj in objs:
137 for obj in objs:
138 publish_png(obj)
138 publish_png(obj)
139 else:
139 else:
140 display(*objs, include=['text/plain','image/png'])
140 display(*objs, include=['text/plain','image/png'])
141
141
142
142
143 def display_jpeg(*objs, **kwargs):
144 """Display the JPEG representation of an object.
145
146 Parameters
147 ----------
148 objs : tuple of objects
149 The Python objects to display, or if raw=True raw JPEG data to
150 display.
151 raw : bool
152 Are the data objects raw data or Python objects that need to be
153 formatted before display? [default: False]
154 """
155 raw = kwargs.pop('raw',False)
156 if raw:
157 for obj in objs:
158 publish_png(obj)
159 else:
160 display(*objs, include=['text/plain','image/jpeg'])
161
162
143 def display_latex(*objs, **kwargs):
163 def display_latex(*objs, **kwargs):
144 """Display the LaTeX representation of an object.
164 """Display the LaTeX representation of an object.
145
165
146 Parameters
166 Parameters
147 ----------
167 ----------
148 objs : tuple of objects
168 objs : tuple of objects
149 The Python objects to display, or if raw=True raw latex data to
169 The Python objects to display, or if raw=True raw latex data to
150 display.
170 display.
151 raw : bool
171 raw : bool
152 Are the data objects raw data or Python objects that need to be
172 Are the data objects raw data or Python objects that need to be
153 formatted before display? [default: False]
173 formatted before display? [default: False]
154 """
174 """
155 raw = kwargs.pop('raw',False)
175 raw = kwargs.pop('raw',False)
156 if raw:
176 if raw:
157 for obj in objs:
177 for obj in objs:
158 publish_latex(obj)
178 publish_latex(obj)
159 else:
179 else:
160 display(*objs, include=['text/plain','text/latex'])
180 display(*objs, include=['text/plain','text/latex'])
161
181
162
182
163 def display_json(*objs, **kwargs):
183 def display_json(*objs, **kwargs):
164 """Display the JSON representation of an object.
184 """Display the JSON representation of an object.
165
185
166 Parameters
186 Parameters
167 ----------
187 ----------
168 objs : tuple of objects
188 objs : tuple of objects
169 The Python objects to display, or if raw=True raw json data to
189 The Python objects to display, or if raw=True raw json data to
170 display.
190 display.
171 raw : bool
191 raw : bool
172 Are the data objects raw data or Python objects that need to be
192 Are the data objects raw data or Python objects that need to be
173 formatted before display? [default: False]
193 formatted before display? [default: False]
174 """
194 """
175 raw = kwargs.pop('raw',False)
195 raw = kwargs.pop('raw',False)
176 if raw:
196 if raw:
177 for obj in objs:
197 for obj in objs:
178 publish_json(obj)
198 publish_json(obj)
179 else:
199 else:
180 display(*objs, include=['text/plain','application/json'])
200 display(*objs, include=['text/plain','application/json'])
181
201
182
202
183 def display_javascript(*objs, **kwargs):
203 def display_javascript(*objs, **kwargs):
184 """Display the Javascript representation of an object.
204 """Display the Javascript representation of an object.
185
205
186 Parameters
206 Parameters
187 ----------
207 ----------
188 objs : tuple of objects
208 objs : tuple of objects
189 The Python objects to display, or if raw=True raw javascript data to
209 The Python objects to display, or if raw=True raw javascript data to
190 display.
210 display.
191 raw : bool
211 raw : bool
192 Are the data objects raw data or Python objects that need to be
212 Are the data objects raw data or Python objects that need to be
193 formatted before display? [default: False]
213 formatted before display? [default: False]
194 """
214 """
195 raw = kwargs.pop('raw',False)
215 raw = kwargs.pop('raw',False)
196 if raw:
216 if raw:
197 for obj in objs:
217 for obj in objs:
198 publish_javascript(obj)
218 publish_javascript(obj)
199 else:
219 else:
200 display(*objs, include=['text/plain','application/javascript'])
220 display(*objs, include=['text/plain','application/javascript'])
201
221
202 #-----------------------------------------------------------------------------
222 #-----------------------------------------------------------------------------
203 # Smart classes
223 # Smart classes
204 #-----------------------------------------------------------------------------
224 #-----------------------------------------------------------------------------
205
225
206
226
207 class DisplayObject(object):
227 class DisplayObject(object):
208 """An object that wraps data to be displayed."""
228 """An object that wraps data to be displayed."""
209
229
210 def __init__(self, data):
230 _read_flags = 'r'
211 """Create a display object given raw data of a MIME type or a URL.
231
232 def __init__(self, data=None, url=None, filename=None):
233 """Create a display object given raw data.
212
234
213 When this object is returned by an expression or passed to the
235 When this object is returned by an expression or passed to the
214 display function, it will result in the data being displayed
236 display function, it will result in the data being displayed
215 in the frontend. The MIME type of the data should match the
237 in the frontend. The MIME type of the data should match the
216 subclasses used, so the Png subclass should be used for 'image/png'
238 subclasses used, so the Png subclass should be used for 'image/png'
217 data. If the data is a URL, the data will first be downloaded
239 data. If the data is a URL, the data will first be downloaded
218 and then displayed.
240 and then displayed. If
219
241
220 Parameters
242 Parameters
221 ----------
243 ----------
222 data : unicode, str or bytes
244 data : unicode, str or bytes
223 The raw data or a URL to download the data from.
245 The raw data or a URL to download the data from.
246 url : unicode
247 A URL to download the data from.
248 filename : unicode
249 Path to a local file to load the data from.
224 """
250 """
225 if data.startswith('http'):
251 if data is not None and data.startswith('http'):
226 import urllib2
252 self.url = data
227 response = urllib2.urlopen(data)
253 self.filename = None
228 self.data = response.read()
254 self.data = None
229 else:
255 else:
230 self.data = data
256 self.data = data
231
257 self.url = url
258 self.filename = None if filename is None else unicode(filename)
259 self.reload()
260
261 def reload(self):
262 """Reload the raw data from file or URL."""
263 if self.filename is not None:
264 with open(self.filename, self._read_flags) as f:
265 self.data = f.read()
266 elif self.url is not None:
267 try:
268 import urllib2
269 response = urllib2.urlopen(self.url)
270 self.data = response.read()
271 except:
272 self.data = None
232
273
233 class Pretty(DisplayObject):
274 class Pretty(DisplayObject):
234
275
235 def _repr_pretty_(self):
276 def _repr_pretty_(self):
236 return self.data
277 return self.data
237
278
238
279
239 class Html(DisplayObject):
280 class HTML(DisplayObject):
240
281
241 def _repr_html_(self):
282 def _repr_html_(self):
242 return self.data
283 return self.data
243
284
244
285
245 class Latex(DisplayObject):
286 class Math(DisplayObject):
246
287
247 def _repr_latex_(self):
288 def _repr_latex_(self):
248 return self.data
289 return self.data
249
290
250
291
251 class Png(DisplayObject):
292 class SVG(DisplayObject):
252
253 def _repr_png_(self):
254 return self.data
255
256
257 class Svg(DisplayObject):
258
293
259 def _repr_svg_(self):
294 def _repr_svg_(self):
260 return self.data
295 return self.data
261
296
262
297
263 class Json(DisplayObject):
298 class JSON(DisplayObject):
264
299
265 def _repr_json_(self):
300 def _repr_json_(self):
266 return self.data
301 return self.data
267
302
268
303
269 class Javscript(DisplayObject):
304 class Javascript(DisplayObject):
270
305
271 def _repr_javascript_(self):
306 def _repr_javascript_(self):
272 return self.data
307 return self.data
273
308
274
309
310 class Image(DisplayObject):
311
312 _read_flags = 'rb'
313
314 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False):
315 """Create a display an PNG/JPEG image given raw data.
316
317 When this object is returned by an expression or passed to the
318 display function, it will result in the image being displayed
319 in the frontend.
320
321 Parameters
322 ----------
323 data : unicode, str or bytes
324 The raw data or a URL to download the data from.
325 url : unicode
326 A URL to download the data from.
327 filename : unicode
328 Path to a local file to load the data from.
329 format : unicode
330 The format of the image data (png/jpeg/jpg). If a filename or URL is given
331 for format will be inferred from the filename extension.
332 embed : bool
333 Should the image data be embedded in the notebook using a data URI (True)
334 or be loaded using an <img> tag. Set this to True if you want the image
335 to be viewable later with no internet connection. If a filename is given
336 embed is always set to True.
337 """
338 if filename is not None:
339 ext = self._find_ext(filename)
340 elif url is not None:
341 ext = self._find_ext(url)
342 elif data.startswith('http'):
343 ext = self._find_ext(data)
344 else:
345 ext = None
346 if ext is not None:
347 if ext == u'jpg' or ext == u'jpeg':
348 format = u'jpeg'
349 if ext == u'png':
350 format = u'png'
351 self.format = unicode(format).lower()
352 self.embed = True if filename is not None else embed
353 super(Image, self).__init__(data=data, url=url, filename=filename)
354
355 def reload(self):
356 """Reload the raw data from file or URL."""
357 if self.embed:
358 super(Image,self).reload()
359
360 def _repr_html_(self):
361 if not self.embed:
362 return u'<img src="%s" />' % self.url
363
364 def _repr_png_(self):
365 if self.embed and self.format == u'png':
366 return self.data
367
368 def _repr_jpeg_(self):
369 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
370 return self.data
371
372 def _find_ext(self, s):
373 return unicode(s.split('.')[-1].lower())
@@ -1,276 +1,298 b''
1 """An interface for publishing rich data to frontends.
1 """An interface for publishing rich data to frontends.
2
2
3 There are two components of the display system:
3 There are two components of the display system:
4
4
5 * Display formatters, which take a Python object and compute the
5 * Display formatters, which take a Python object and compute the
6 representation of the object in various formats (text, HTML, SVg, etc.).
6 representation of the object in various formats (text, HTML, SVg, etc.).
7 * The display publisher that is used to send the representation data to the
7 * The display publisher that is used to send the representation data to the
8 various frontends.
8 various frontends.
9
9
10 This module defines the logic display publishing. The display publisher uses
10 This module defines the logic display publishing. The display publisher uses
11 the ``display_data`` message type that is defined in the IPython messaging
11 the ``display_data`` message type that is defined in the IPython messaging
12 spec.
12 spec.
13
13
14 Authors:
14 Authors:
15
15
16 * Brian Granger
16 * Brian Granger
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008-2010 The IPython Development Team
20 # Copyright (C) 2008-2010 The IPython Development Team
21 #
21 #
22 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Imports
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 from __future__ import print_function
30 from __future__ import print_function
31
31
32 from IPython.config.configurable import Configurable
32 from IPython.config.configurable import Configurable
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Main payload class
35 # Main payload class
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class DisplayPublisher(Configurable):
38 class DisplayPublisher(Configurable):
39 """A traited class that publishes display data to frontends.
39 """A traited class that publishes display data to frontends.
40
40
41 Instances of this class are created by the main IPython object and should
41 Instances of this class are created by the main IPython object and should
42 be accessed there.
42 be accessed there.
43 """
43 """
44
44
45 def _validate_data(self, source, data, metadata=None):
45 def _validate_data(self, source, data, metadata=None):
46 """Validate the display data.
46 """Validate the display data.
47
47
48 Parameters
48 Parameters
49 ----------
49 ----------
50 source : str
50 source : str
51 The fully dotted name of the callable that created the data, like
51 The fully dotted name of the callable that created the data, like
52 :func:`foo.bar.my_formatter`.
52 :func:`foo.bar.my_formatter`.
53 data : dict
53 data : dict
54 The formata data dictionary.
54 The formata data dictionary.
55 metadata : dict
55 metadata : dict
56 Any metadata for the data.
56 Any metadata for the data.
57 """
57 """
58
58
59 if not isinstance(source, (str,unicode)):
59 if not isinstance(source, (str,unicode)):
60 raise TypeError('source must be a str, got: %r' % source)
60 raise TypeError('source must be a str, got: %r' % source)
61 if not isinstance(data, dict):
61 if not isinstance(data, dict):
62 raise TypeError('data must be a dict, got: %r' % data)
62 raise TypeError('data must be a dict, got: %r' % data)
63 if metadata is not None:
63 if metadata is not None:
64 if not isinstance(metadata, dict):
64 if not isinstance(metadata, dict):
65 raise TypeError('metadata must be a dict, got: %r' % data)
65 raise TypeError('metadata must be a dict, got: %r' % data)
66
66
67 def publish(self, source, data, metadata=None):
67 def publish(self, source, data, metadata=None):
68 """Publish data and metadata to all frontends.
68 """Publish data and metadata to all frontends.
69
69
70 See the ``display_data`` message in the messaging documentation for
70 See the ``display_data`` message in the messaging documentation for
71 more details about this message type.
71 more details about this message type.
72
72
73 The following MIME types are currently implemented:
73 The following MIME types are currently implemented:
74
74
75 * text/plain
75 * text/plain
76 * text/html
76 * text/html
77 * text/latex
77 * text/latex
78 * application/json
78 * application/json
79 * application/javascript
79 * application/javascript
80 * image/png
80 * image/png
81 * image/jpeg
81 * image/svg+xml
82 * image/svg+xml
82
83
83 Parameters
84 Parameters
84 ----------
85 ----------
85 source : str
86 source : str
86 A string that give the function or method that created the data,
87 A string that give the function or method that created the data,
87 such as 'IPython.core.page'.
88 such as 'IPython.core.page'.
88 data : dict
89 data : dict
89 A dictionary having keys that are valid MIME types (like
90 A dictionary having keys that are valid MIME types (like
90 'text/plain' or 'image/svg+xml') and values that are the data for
91 'text/plain' or 'image/svg+xml') and values that are the data for
91 that MIME type. The data itself must be a JSON'able data
92 that MIME type. The data itself must be a JSON'able data
92 structure. Minimally all data should have the 'text/plain' data,
93 structure. Minimally all data should have the 'text/plain' data,
93 which can be displayed by all frontends. If more than the plain
94 which can be displayed by all frontends. If more than the plain
94 text is given, it is up to the frontend to decide which
95 text is given, it is up to the frontend to decide which
95 representation to use.
96 representation to use.
96 metadata : dict
97 metadata : dict
97 A dictionary for metadata related to the data. This can contain
98 A dictionary for metadata related to the data. This can contain
98 arbitrary key, value pairs that frontends can use to interpret
99 arbitrary key, value pairs that frontends can use to interpret
99 the data.
100 the data.
100 """
101 """
101 from IPython.utils import io
102 from IPython.utils import io
102 # The default is to simply write the plain text data using io.stdout.
103 # The default is to simply write the plain text data using io.stdout.
103 if data.has_key('text/plain'):
104 if data.has_key('text/plain'):
104 print(data['text/plain'], file=io.stdout)
105 print(data['text/plain'], file=io.stdout)
105
106
106
107
107 def publish_display_data(source, data, metadata=None):
108 def publish_display_data(source, data, metadata=None):
108 """Publish data and metadata to all frontends.
109 """Publish data and metadata to all frontends.
109
110
110 See the ``display_data`` message in the messaging documentation for
111 See the ``display_data`` message in the messaging documentation for
111 more details about this message type.
112 more details about this message type.
112
113
113 The following MIME types are currently implemented:
114 The following MIME types are currently implemented:
114
115
115 * text/plain
116 * text/plain
116 * text/html
117 * text/html
117 * text/latex
118 * text/latex
118 * application/json
119 * application/json
119 * application/javascript
120 * application/javascript
120 * image/png
121 * image/png
122 * image/jpeg
121 * image/svg+xml
123 * image/svg+xml
122
124
123 Parameters
125 Parameters
124 ----------
126 ----------
125 source : str
127 source : str
126 A string that give the function or method that created the data,
128 A string that give the function or method that created the data,
127 such as 'IPython.core.page'.
129 such as 'IPython.core.page'.
128 data : dict
130 data : dict
129 A dictionary having keys that are valid MIME types (like
131 A dictionary having keys that are valid MIME types (like
130 'text/plain' or 'image/svg+xml') and values that are the data for
132 'text/plain' or 'image/svg+xml') and values that are the data for
131 that MIME type. The data itself must be a JSON'able data
133 that MIME type. The data itself must be a JSON'able data
132 structure. Minimally all data should have the 'text/plain' data,
134 structure. Minimally all data should have the 'text/plain' data,
133 which can be displayed by all frontends. If more than the plain
135 which can be displayed by all frontends. If more than the plain
134 text is given, it is up to the frontend to decide which
136 text is given, it is up to the frontend to decide which
135 representation to use.
137 representation to use.
136 metadata : dict
138 metadata : dict
137 A dictionary for metadata related to the data. This can contain
139 A dictionary for metadata related to the data. This can contain
138 arbitrary key, value pairs that frontends can use to interpret
140 arbitrary key, value pairs that frontends can use to interpret
139 the data.
141 the data.
140 """
142 """
141 from IPython.core.interactiveshell import InteractiveShell
143 from IPython.core.interactiveshell import InteractiveShell
142 InteractiveShell.instance().display_pub.publish(
144 InteractiveShell.instance().display_pub.publish(
143 source,
145 source,
144 data,
146 data,
145 metadata
147 metadata
146 )
148 )
147
149
148
150
149 def publish_pretty(data, metadata=None):
151 def publish_pretty(data, metadata=None):
150 """Publish raw text data to all frontends.
152 """Publish raw text data to all frontends.
151
153
152 Parameters
154 Parameters
153 ----------
155 ----------
154 data : unicode
156 data : unicode
155 The raw text data to publish.
157 The raw text data to publish.
156 metadata : dict
158 metadata : dict
157 A dictionary for metadata related to the data. This can contain
159 A dictionary for metadata related to the data. This can contain
158 arbitrary key, value pairs that frontends can use to interpret
160 arbitrary key, value pairs that frontends can use to interpret
159 the data.
161 the data.
160 """
162 """
161 publish_display_data(
163 publish_display_data(
162 u'IPython.core.displaypub.publish_pretty',
164 u'IPython.core.displaypub.publish_pretty',
163 {'text/plain':data},
165 {'text/plain':data},
164 metadata=metadata
166 metadata=metadata
165 )
167 )
166
168
167
169
168 def publish_html(data, metadata=None):
170 def publish_html(data, metadata=None):
169 """Publish raw html data to all frontends.
171 """Publish raw HTML data to all frontends.
170
172
171 Parameters
173 Parameters
172 ----------
174 ----------
173 data : unicode
175 data : unicode
174 The raw html data to publish.
176 The raw HTML data to publish.
175 metadata : dict
177 metadata : dict
176 A dictionary for metadata related to the data. This can contain
178 A dictionary for metadata related to the data. This can contain
177 arbitrary key, value pairs that frontends can use to interpret
179 arbitrary key, value pairs that frontends can use to interpret
178 the data.
180 the data.
179 """
181 """
180 publish_display_data(
182 publish_display_data(
181 u'IPython.core.displaypub.publish_html',
183 u'IPython.core.displaypub.publish_html',
182 {'text/html':data},
184 {'text/html':data},
183 metadata=metadata
185 metadata=metadata
184 )
186 )
185
187
186
188
187 def publish_latex(data, metadata=None):
189 def publish_latex(data, metadata=None):
188 """Publish raw latex data to all frontends.
190 """Publish raw LaTeX data to all frontends.
189
191
190 Parameters
192 Parameters
191 ----------
193 ----------
192 data : unicode
194 data : unicode
193 The raw latex data to publish.
195 The raw LaTeX data to publish.
194 metadata : dict
196 metadata : dict
195 A dictionary for metadata related to the data. This can contain
197 A dictionary for metadata related to the data. This can contain
196 arbitrary key, value pairs that frontends can use to interpret
198 arbitrary key, value pairs that frontends can use to interpret
197 the data.
199 the data.
198 """
200 """
199 publish_display_data(
201 publish_display_data(
200 u'IPython.core.displaypub.publish_latex',
202 u'IPython.core.displaypub.publish_latex',
201 {'text/latex':data},
203 {'text/latex':data},
202 metadata=metadata
204 metadata=metadata
203 )
205 )
204
206
205 def publish_png(data, metadata=None):
207 def publish_png(data, metadata=None):
206 """Publish raw binary png data to all frontends.
208 """Publish raw binary PNG data to all frontends.
207
209
208 Parameters
210 Parameters
209 ----------
211 ----------
210 data : str/bytes
212 data : str/bytes
211 The raw binary png data to publish.
213 The raw binary PNG data to publish.
212 metadata : dict
214 metadata : dict
213 A dictionary for metadata related to the data. This can contain
215 A dictionary for metadata related to the data. This can contain
214 arbitrary key, value pairs that frontends can use to interpret
216 arbitrary key, value pairs that frontends can use to interpret
215 the data.
217 the data.
216 """
218 """
217 publish_display_data(
219 publish_display_data(
218 u'IPython.core.displaypub.publish_png',
220 u'IPython.core.displaypub.publish_png',
219 {'image/png':data},
221 {'image/png':data},
220 metadata=metadata
222 metadata=metadata
221 )
223 )
222
224
225
226 def publish_jpeg(data, metadata=None):
227 """Publish raw binary JPEG data to all frontends.
228
229 Parameters
230 ----------
231 data : str/bytes
232 The raw binary JPEG data to publish.
233 metadata : dict
234 A dictionary for metadata related to the data. This can contain
235 arbitrary key, value pairs that frontends can use to interpret
236 the data.
237 """
238 publish_display_data(
239 u'IPython.core.displaypub.publish_jpeg',
240 {'image/jpeg':data},
241 metadata=metadata
242 )
243
244
223 def publish_svg(data, metadata=None):
245 def publish_svg(data, metadata=None):
224 """Publish raw svg data to all frontends.
246 """Publish raw SVG data to all frontends.
225
247
226 Parameters
248 Parameters
227 ----------
249 ----------
228 data : unicode
250 data : unicode
229 The raw svg data to publish.
251 The raw SVG data to publish.
230 metadata : dict
252 metadata : dict
231 A dictionary for metadata related to the data. This can contain
253 A dictionary for metadata related to the data. This can contain
232 arbitrary key, value pairs that frontends can use to interpret
254 arbitrary key, value pairs that frontends can use to interpret
233 the data.
255 the data.
234 """
256 """
235 publish_display_data(
257 publish_display_data(
236 u'IPython.core.displaypub.publish_svg',
258 u'IPython.core.displaypub.publish_svg',
237 {'image/svg+xml':data},
259 {'image/svg+xml':data},
238 metadata=metadata
260 metadata=metadata
239 )
261 )
240
262
241 def publish_json(data, metadata=None):
263 def publish_json(data, metadata=None):
242 """Publish raw json data to all frontends.
264 """Publish raw JSON data to all frontends.
243
265
244 Parameters
266 Parameters
245 ----------
267 ----------
246 data : unicode
268 data : unicode
247 The raw json data to publish.
269 The raw JSON data to publish.
248 metadata : dict
270 metadata : dict
249 A dictionary for metadata related to the data. This can contain
271 A dictionary for metadata related to the data. This can contain
250 arbitrary key, value pairs that frontends can use to interpret
272 arbitrary key, value pairs that frontends can use to interpret
251 the data.
273 the data.
252 """
274 """
253 publish_display_data(
275 publish_display_data(
254 u'IPython.core.displaypub.publish_json',
276 u'IPython.core.displaypub.publish_json',
255 {'application/json':data},
277 {'application/json':data},
256 metadata=metadata
278 metadata=metadata
257 )
279 )
258
280
259 def publish_javascript(data, metadata=None):
281 def publish_javascript(data, metadata=None):
260 """Publish raw javascript data to all frontends.
282 """Publish raw Javascript data to all frontends.
261
283
262 Parameters
284 Parameters
263 ----------
285 ----------
264 data : unicode
286 data : unicode
265 The raw javascript data to publish.
287 The raw Javascript data to publish.
266 metadata : dict
288 metadata : dict
267 A dictionary for metadata related to the data. This can contain
289 A dictionary for metadata related to the data. This can contain
268 arbitrary key, value pairs that frontends can use to interpret
290 arbitrary key, value pairs that frontends can use to interpret
269 the data.
291 the data.
270 """
292 """
271 publish_display_data(
293 publish_display_data(
272 u'IPython.core.displaypub.publish_javascript',
294 u'IPython.core.displaypub.publish_javascript',
273 {'application/javascript':data},
295 {'application/javascript':data},
274 metadata=metadata
296 metadata=metadata
275 )
297 )
276
298
@@ -1,598 +1,619 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Display formatters.
2 """Display formatters.
3
3
4
4
5 Authors:
5 Authors:
6
6
7 * Robert Kern
7 * Robert Kern
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (c) 2010, IPython Development Team.
11 # Copyright (c) 2010, IPython Development Team.
12 #
12 #
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14 #
14 #
15 # The full license is in the file COPYING.txt, distributed with this software.
15 # The full license is in the file COPYING.txt, distributed with this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # Stdlib imports
22 # Stdlib imports
23 import abc
23 import abc
24 import sys
24 import sys
25 # We must use StringIO, as cStringIO doesn't handle unicode properly.
25 # We must use StringIO, as cStringIO doesn't handle unicode properly.
26 from StringIO import StringIO
26 from StringIO import StringIO
27
27
28 # Our own imports
28 # Our own imports
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.lib import pretty
30 from IPython.lib import pretty
31 from IPython.utils.traitlets import Bool, Dict, Int, Unicode, CUnicode, ObjectName
31 from IPython.utils.traitlets import Bool, Dict, Int, Unicode, CUnicode, ObjectName
32
32
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # The main DisplayFormatter class
35 # The main DisplayFormatter class
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38
38
39 class DisplayFormatter(Configurable):
39 class DisplayFormatter(Configurable):
40
40
41 # When set to true only the default plain text formatter will be used.
41 # When set to true only the default plain text formatter will be used.
42 plain_text_only = Bool(False, config=True)
42 plain_text_only = Bool(False, config=True)
43
43
44 # A dict of formatter whose keys are format types (MIME types) and whose
44 # A dict of formatter whose keys are format types (MIME types) and whose
45 # values are subclasses of BaseFormatter.
45 # values are subclasses of BaseFormatter.
46 formatters = Dict(config=True)
46 formatters = Dict(config=True)
47 def _formatters_default(self):
47 def _formatters_default(self):
48 """Activate the default formatters."""
48 """Activate the default formatters."""
49 formatter_classes = [
49 formatter_classes = [
50 PlainTextFormatter,
50 PlainTextFormatter,
51 HTMLFormatter,
51 HTMLFormatter,
52 SVGFormatter,
52 SVGFormatter,
53 PNGFormatter,
53 PNGFormatter,
54 JPEGFormatter,
54 LatexFormatter,
55 LatexFormatter,
55 JSONFormatter,
56 JSONFormatter,
56 JavascriptFormatter
57 JavascriptFormatter
57 ]
58 ]
58 d = {}
59 d = {}
59 for cls in formatter_classes:
60 for cls in formatter_classes:
60 f = cls(config=self.config)
61 f = cls(config=self.config)
61 d[f.format_type] = f
62 d[f.format_type] = f
62 return d
63 return d
63
64
64 def format(self, obj, include=None, exclude=None):
65 def format(self, obj, include=None, exclude=None):
65 """Return a format data dict for an object.
66 """Return a format data dict for an object.
66
67
67 By default all format types will be computed.
68 By default all format types will be computed.
68
69
69 The following MIME types are currently implemented:
70 The following MIME types are currently implemented:
70
71
71 * text/plain
72 * text/plain
72 * text/html
73 * text/html
73 * text/latex
74 * text/latex
74 * application/json
75 * application/json
75 * application/javascript
76 * application/javascript
76 * image/png
77 * image/png
78 * image/jpeg
77 * image/svg+xml
79 * image/svg+xml
78
80
79 Parameters
81 Parameters
80 ----------
82 ----------
81 obj : object
83 obj : object
82 The Python object whose format data will be computed.
84 The Python object whose format data will be computed.
83 include : list or tuple, optional
85 include : list or tuple, optional
84 A list of format type strings (MIME types) to include in the
86 A list of format type strings (MIME types) to include in the
85 format data dict. If this is set *only* the format types included
87 format data dict. If this is set *only* the format types included
86 in this list will be computed.
88 in this list will be computed.
87 exclude : list or tuple, optional
89 exclude : list or tuple, optional
88 A list of format type string (MIME types) to exclue in the format
90 A list of format type string (MIME types) to exclue in the format
89 data dict. If this is set all format types will be computed,
91 data dict. If this is set all format types will be computed,
90 except for those included in this argument.
92 except for those included in this argument.
91
93
92 Returns
94 Returns
93 -------
95 -------
94 format_dict : dict
96 format_dict : dict
95 A dictionary of key/value pairs, one or each format that was
97 A dictionary of key/value pairs, one or each format that was
96 generated for the object. The keys are the format types, which
98 generated for the object. The keys are the format types, which
97 will usually be MIME type strings and the values and JSON'able
99 will usually be MIME type strings and the values and JSON'able
98 data structure containing the raw data for the representation in
100 data structure containing the raw data for the representation in
99 that format.
101 that format.
100 """
102 """
101 format_dict = {}
103 format_dict = {}
102
104
103 # If plain text only is active
105 # If plain text only is active
104 if self.plain_text_only:
106 if self.plain_text_only:
105 formatter = self.formatters['text/plain']
107 formatter = self.formatters['text/plain']
106 try:
108 try:
107 data = formatter(obj)
109 data = formatter(obj)
108 except:
110 except:
109 # FIXME: log the exception
111 # FIXME: log the exception
110 raise
112 raise
111 if data is not None:
113 if data is not None:
112 format_dict['text/plain'] = data
114 format_dict['text/plain'] = data
113 return format_dict
115 return format_dict
114
116
115 for format_type, formatter in self.formatters.items():
117 for format_type, formatter in self.formatters.items():
116 if include is not None:
118 if include is not None:
117 if format_type not in include:
119 if format_type not in include:
118 continue
120 continue
119 if exclude is not None:
121 if exclude is not None:
120 if format_type in exclude:
122 if format_type in exclude:
121 continue
123 continue
122 try:
124 try:
123 data = formatter(obj)
125 data = formatter(obj)
124 except:
126 except:
125 # FIXME: log the exception
127 # FIXME: log the exception
126 raise
128 raise
127 if data is not None:
129 if data is not None:
128 format_dict[format_type] = data
130 format_dict[format_type] = data
129 return format_dict
131 return format_dict
130
132
131 @property
133 @property
132 def format_types(self):
134 def format_types(self):
133 """Return the format types (MIME types) of the active formatters."""
135 """Return the format types (MIME types) of the active formatters."""
134 return self.formatters.keys()
136 return self.formatters.keys()
135
137
136
138
137 #-----------------------------------------------------------------------------
139 #-----------------------------------------------------------------------------
138 # Formatters for specific format types (text, html, svg, etc.)
140 # Formatters for specific format types (text, html, svg, etc.)
139 #-----------------------------------------------------------------------------
141 #-----------------------------------------------------------------------------
140
142
141
143
142 class FormatterABC(object):
144 class FormatterABC(object):
143 """ Abstract base class for Formatters.
145 """ Abstract base class for Formatters.
144
146
145 A formatter is a callable class that is responsible for computing the
147 A formatter is a callable class that is responsible for computing the
146 raw format data for a particular format type (MIME type). For example,
148 raw format data for a particular format type (MIME type). For example,
147 an HTML formatter would have a format type of `text/html` and would return
149 an HTML formatter would have a format type of `text/html` and would return
148 the HTML representation of the object when called.
150 the HTML representation of the object when called.
149 """
151 """
150 __metaclass__ = abc.ABCMeta
152 __metaclass__ = abc.ABCMeta
151
153
152 # The format type of the data returned, usually a MIME type.
154 # The format type of the data returned, usually a MIME type.
153 format_type = 'text/plain'
155 format_type = 'text/plain'
154
156
155 # Is the formatter enabled...
157 # Is the formatter enabled...
156 enabled = True
158 enabled = True
157
159
158 @abc.abstractmethod
160 @abc.abstractmethod
159 def __call__(self, obj):
161 def __call__(self, obj):
160 """Return a JSON'able representation of the object.
162 """Return a JSON'able representation of the object.
161
163
162 If the object cannot be formatted by this formatter, then return None
164 If the object cannot be formatted by this formatter, then return None
163 """
165 """
164 try:
166 try:
165 return repr(obj)
167 return repr(obj)
166 except TypeError:
168 except TypeError:
167 return None
169 return None
168
170
169
171
170 class BaseFormatter(Configurable):
172 class BaseFormatter(Configurable):
171 """A base formatter class that is configurable.
173 """A base formatter class that is configurable.
172
174
173 This formatter should usually be used as the base class of all formatters.
175 This formatter should usually be used as the base class of all formatters.
174 It is a traited :class:`Configurable` class and includes an extensible
176 It is a traited :class:`Configurable` class and includes an extensible
175 API for users to determine how their objects are formatted. The following
177 API for users to determine how their objects are formatted. The following
176 logic is used to find a function to format an given object.
178 logic is used to find a function to format an given object.
177
179
178 1. The object is introspected to see if it has a method with the name
180 1. The object is introspected to see if it has a method with the name
179 :attr:`print_method`. If is does, that object is passed to that method
181 :attr:`print_method`. If is does, that object is passed to that method
180 for formatting.
182 for formatting.
181 2. If no print method is found, three internal dictionaries are consulted
183 2. If no print method is found, three internal dictionaries are consulted
182 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
184 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
183 and :attr:`deferred_printers`.
185 and :attr:`deferred_printers`.
184
186
185 Users should use these dictionaries to register functions that will be
187 Users should use these dictionaries to register functions that will be
186 used to compute the format data for their objects (if those objects don't
188 used to compute the format data for their objects (if those objects don't
187 have the special print methods). The easiest way of using these
189 have the special print methods). The easiest way of using these
188 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
190 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
189 methods.
191 methods.
190
192
191 If no function/callable is found to compute the format data, ``None`` is
193 If no function/callable is found to compute the format data, ``None`` is
192 returned and this format type is not used.
194 returned and this format type is not used.
193 """
195 """
194
196
195 format_type = Unicode('text/plain')
197 format_type = Unicode('text/plain')
196
198
197 enabled = Bool(True, config=True)
199 enabled = Bool(True, config=True)
198
200
199 print_method = ObjectName('__repr__')
201 print_method = ObjectName('__repr__')
200
202
201 # The singleton printers.
203 # The singleton printers.
202 # Maps the IDs of the builtin singleton objects to the format functions.
204 # Maps the IDs of the builtin singleton objects to the format functions.
203 singleton_printers = Dict(config=True)
205 singleton_printers = Dict(config=True)
204 def _singleton_printers_default(self):
206 def _singleton_printers_default(self):
205 return {}
207 return {}
206
208
207 # The type-specific printers.
209 # The type-specific printers.
208 # Map type objects to the format functions.
210 # Map type objects to the format functions.
209 type_printers = Dict(config=True)
211 type_printers = Dict(config=True)
210 def _type_printers_default(self):
212 def _type_printers_default(self):
211 return {}
213 return {}
212
214
213 # The deferred-import type-specific printers.
215 # The deferred-import type-specific printers.
214 # Map (modulename, classname) pairs to the format functions.
216 # Map (modulename, classname) pairs to the format functions.
215 deferred_printers = Dict(config=True)
217 deferred_printers = Dict(config=True)
216 def _deferred_printers_default(self):
218 def _deferred_printers_default(self):
217 return {}
219 return {}
218
220
219 def __call__(self, obj):
221 def __call__(self, obj):
220 """Compute the format for an object."""
222 """Compute the format for an object."""
221 if self.enabled:
223 if self.enabled:
222 obj_id = id(obj)
224 obj_id = id(obj)
223 try:
225 try:
224 obj_class = getattr(obj, '__class__', None) or type(obj)
226 obj_class = getattr(obj, '__class__', None) or type(obj)
225 # First try to find registered singleton printers for the type.
227 # First try to find registered singleton printers for the type.
226 try:
228 try:
227 printer = self.singleton_printers[obj_id]
229 printer = self.singleton_printers[obj_id]
228 except (TypeError, KeyError):
230 except (TypeError, KeyError):
229 pass
231 pass
230 else:
232 else:
231 return printer(obj)
233 return printer(obj)
232 # Next look for type_printers.
234 # Next look for type_printers.
233 for cls in pretty._get_mro(obj_class):
235 for cls in pretty._get_mro(obj_class):
234 if cls in self.type_printers:
236 if cls in self.type_printers:
235 return self.type_printers[cls](obj)
237 return self.type_printers[cls](obj)
236 else:
238 else:
237 printer = self._in_deferred_types(cls)
239 printer = self._in_deferred_types(cls)
238 if printer is not None:
240 if printer is not None:
239 return printer(obj)
241 return printer(obj)
240 # Finally look for special method names.
242 # Finally look for special method names.
241 if hasattr(obj_class, self.print_method):
243 if hasattr(obj_class, self.print_method):
242 printer = getattr(obj_class, self.print_method)
244 printer = getattr(obj_class, self.print_method)
243 return printer(obj)
245 return printer(obj)
244 return None
246 return None
245 except Exception:
247 except Exception:
246 pass
248 pass
247 else:
249 else:
248 return None
250 return None
249
251
250 def for_type(self, typ, func):
252 def for_type(self, typ, func):
251 """Add a format function for a given type.
253 """Add a format function for a given type.
252
254
253 Parameters
255 Parameters
254 -----------
256 -----------
255 typ : class
257 typ : class
256 The class of the object that will be formatted using `func`.
258 The class of the object that will be formatted using `func`.
257 func : callable
259 func : callable
258 The callable that will be called to compute the format data. The
260 The callable that will be called to compute the format data. The
259 call signature of this function is simple, it must take the
261 call signature of this function is simple, it must take the
260 object to be formatted and return the raw data for the given
262 object to be formatted and return the raw data for the given
261 format. Subclasses may use a different call signature for the
263 format. Subclasses may use a different call signature for the
262 `func` argument.
264 `func` argument.
263 """
265 """
264 oldfunc = self.type_printers.get(typ, None)
266 oldfunc = self.type_printers.get(typ, None)
265 if func is not None:
267 if func is not None:
266 # To support easy restoration of old printers, we need to ignore
268 # To support easy restoration of old printers, we need to ignore
267 # Nones.
269 # Nones.
268 self.type_printers[typ] = func
270 self.type_printers[typ] = func
269 return oldfunc
271 return oldfunc
270
272
271 def for_type_by_name(self, type_module, type_name, func):
273 def for_type_by_name(self, type_module, type_name, func):
272 """Add a format function for a type specified by the full dotted
274 """Add a format function for a type specified by the full dotted
273 module and name of the type, rather than the type of the object.
275 module and name of the type, rather than the type of the object.
274
276
275 Parameters
277 Parameters
276 ----------
278 ----------
277 type_module : str
279 type_module : str
278 The full dotted name of the module the type is defined in, like
280 The full dotted name of the module the type is defined in, like
279 ``numpy``.
281 ``numpy``.
280 type_name : str
282 type_name : str
281 The name of the type (the class name), like ``dtype``
283 The name of the type (the class name), like ``dtype``
282 func : callable
284 func : callable
283 The callable that will be called to compute the format data. The
285 The callable that will be called to compute the format data. The
284 call signature of this function is simple, it must take the
286 call signature of this function is simple, it must take the
285 object to be formatted and return the raw data for the given
287 object to be formatted and return the raw data for the given
286 format. Subclasses may use a different call signature for the
288 format. Subclasses may use a different call signature for the
287 `func` argument.
289 `func` argument.
288 """
290 """
289 key = (type_module, type_name)
291 key = (type_module, type_name)
290 oldfunc = self.deferred_printers.get(key, None)
292 oldfunc = self.deferred_printers.get(key, None)
291 if func is not None:
293 if func is not None:
292 # To support easy restoration of old printers, we need to ignore
294 # To support easy restoration of old printers, we need to ignore
293 # Nones.
295 # Nones.
294 self.deferred_printers[key] = func
296 self.deferred_printers[key] = func
295 return oldfunc
297 return oldfunc
296
298
297 def _in_deferred_types(self, cls):
299 def _in_deferred_types(self, cls):
298 """
300 """
299 Check if the given class is specified in the deferred type registry.
301 Check if the given class is specified in the deferred type registry.
300
302
301 Returns the printer from the registry if it exists, and None if the
303 Returns the printer from the registry if it exists, and None if the
302 class is not in the registry. Successful matches will be moved to the
304 class is not in the registry. Successful matches will be moved to the
303 regular type registry for future use.
305 regular type registry for future use.
304 """
306 """
305 mod = getattr(cls, '__module__', None)
307 mod = getattr(cls, '__module__', None)
306 name = getattr(cls, '__name__', None)
308 name = getattr(cls, '__name__', None)
307 key = (mod, name)
309 key = (mod, name)
308 printer = None
310 printer = None
309 if key in self.deferred_printers:
311 if key in self.deferred_printers:
310 # Move the printer over to the regular registry.
312 # Move the printer over to the regular registry.
311 printer = self.deferred_printers.pop(key)
313 printer = self.deferred_printers.pop(key)
312 self.type_printers[cls] = printer
314 self.type_printers[cls] = printer
313 return printer
315 return printer
314
316
315
317
316 class PlainTextFormatter(BaseFormatter):
318 class PlainTextFormatter(BaseFormatter):
317 """The default pretty-printer.
319 """The default pretty-printer.
318
320
319 This uses :mod:`IPython.external.pretty` to compute the format data of
321 This uses :mod:`IPython.external.pretty` to compute the format data of
320 the object. If the object cannot be pretty printed, :func:`repr` is used.
322 the object. If the object cannot be pretty printed, :func:`repr` is used.
321 See the documentation of :mod:`IPython.external.pretty` for details on
323 See the documentation of :mod:`IPython.external.pretty` for details on
322 how to write pretty printers. Here is a simple example::
324 how to write pretty printers. Here is a simple example::
323
325
324 def dtype_pprinter(obj, p, cycle):
326 def dtype_pprinter(obj, p, cycle):
325 if cycle:
327 if cycle:
326 return p.text('dtype(...)')
328 return p.text('dtype(...)')
327 if hasattr(obj, 'fields'):
329 if hasattr(obj, 'fields'):
328 if obj.fields is None:
330 if obj.fields is None:
329 p.text(repr(obj))
331 p.text(repr(obj))
330 else:
332 else:
331 p.begin_group(7, 'dtype([')
333 p.begin_group(7, 'dtype([')
332 for i, field in enumerate(obj.descr):
334 for i, field in enumerate(obj.descr):
333 if i > 0:
335 if i > 0:
334 p.text(',')
336 p.text(',')
335 p.breakable()
337 p.breakable()
336 p.pretty(field)
338 p.pretty(field)
337 p.end_group(7, '])')
339 p.end_group(7, '])')
338 """
340 """
339
341
340 # The format type of data returned.
342 # The format type of data returned.
341 format_type = Unicode('text/plain')
343 format_type = Unicode('text/plain')
342
344
343 # This subclass ignores this attribute as it always need to return
345 # This subclass ignores this attribute as it always need to return
344 # something.
346 # something.
345 enabled = Bool(True, config=False)
347 enabled = Bool(True, config=False)
346
348
347 # Look for a _repr_pretty_ methods to use for pretty printing.
349 # Look for a _repr_pretty_ methods to use for pretty printing.
348 print_method = ObjectName('_repr_pretty_')
350 print_method = ObjectName('_repr_pretty_')
349
351
350 # Whether to pretty-print or not.
352 # Whether to pretty-print or not.
351 pprint = Bool(True, config=True)
353 pprint = Bool(True, config=True)
352
354
353 # Whether to be verbose or not.
355 # Whether to be verbose or not.
354 verbose = Bool(False, config=True)
356 verbose = Bool(False, config=True)
355
357
356 # The maximum width.
358 # The maximum width.
357 max_width = Int(79, config=True)
359 max_width = Int(79, config=True)
358
360
359 # The newline character.
361 # The newline character.
360 newline = Unicode('\n', config=True)
362 newline = Unicode('\n', config=True)
361
363
362 # format-string for pprinting floats
364 # format-string for pprinting floats
363 float_format = Unicode('%r')
365 float_format = Unicode('%r')
364 # setter for float precision, either int or direct format-string
366 # setter for float precision, either int or direct format-string
365 float_precision = CUnicode('', config=True)
367 float_precision = CUnicode('', config=True)
366
368
367 def _float_precision_changed(self, name, old, new):
369 def _float_precision_changed(self, name, old, new):
368 """float_precision changed, set float_format accordingly.
370 """float_precision changed, set float_format accordingly.
369
371
370 float_precision can be set by int or str.
372 float_precision can be set by int or str.
371 This will set float_format, after interpreting input.
373 This will set float_format, after interpreting input.
372 If numpy has been imported, numpy print precision will also be set.
374 If numpy has been imported, numpy print precision will also be set.
373
375
374 integer `n` sets format to '%.nf', otherwise, format set directly.
376 integer `n` sets format to '%.nf', otherwise, format set directly.
375
377
376 An empty string returns to defaults (repr for float, 8 for numpy).
378 An empty string returns to defaults (repr for float, 8 for numpy).
377
379
378 This parameter can be set via the '%precision' magic.
380 This parameter can be set via the '%precision' magic.
379 """
381 """
380
382
381 if '%' in new:
383 if '%' in new:
382 # got explicit format string
384 # got explicit format string
383 fmt = new
385 fmt = new
384 try:
386 try:
385 fmt%3.14159
387 fmt%3.14159
386 except Exception:
388 except Exception:
387 raise ValueError("Precision must be int or format string, not %r"%new)
389 raise ValueError("Precision must be int or format string, not %r"%new)
388 elif new:
390 elif new:
389 # otherwise, should be an int
391 # otherwise, should be an int
390 try:
392 try:
391 i = int(new)
393 i = int(new)
392 assert i >= 0
394 assert i >= 0
393 except ValueError:
395 except ValueError:
394 raise ValueError("Precision must be int or format string, not %r"%new)
396 raise ValueError("Precision must be int or format string, not %r"%new)
395 except AssertionError:
397 except AssertionError:
396 raise ValueError("int precision must be non-negative, not %r"%i)
398 raise ValueError("int precision must be non-negative, not %r"%i)
397
399
398 fmt = '%%.%if'%i
400 fmt = '%%.%if'%i
399 if 'numpy' in sys.modules:
401 if 'numpy' in sys.modules:
400 # set numpy precision if it has been imported
402 # set numpy precision if it has been imported
401 import numpy
403 import numpy
402 numpy.set_printoptions(precision=i)
404 numpy.set_printoptions(precision=i)
403 else:
405 else:
404 # default back to repr
406 # default back to repr
405 fmt = '%r'
407 fmt = '%r'
406 if 'numpy' in sys.modules:
408 if 'numpy' in sys.modules:
407 import numpy
409 import numpy
408 # numpy default is 8
410 # numpy default is 8
409 numpy.set_printoptions(precision=8)
411 numpy.set_printoptions(precision=8)
410 self.float_format = fmt
412 self.float_format = fmt
411
413
412 # Use the default pretty printers from IPython.external.pretty.
414 # Use the default pretty printers from IPython.external.pretty.
413 def _singleton_printers_default(self):
415 def _singleton_printers_default(self):
414 return pretty._singleton_pprinters.copy()
416 return pretty._singleton_pprinters.copy()
415
417
416 def _type_printers_default(self):
418 def _type_printers_default(self):
417 d = pretty._type_pprinters.copy()
419 d = pretty._type_pprinters.copy()
418 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
420 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
419 return d
421 return d
420
422
421 def _deferred_printers_default(self):
423 def _deferred_printers_default(self):
422 return pretty._deferred_type_pprinters.copy()
424 return pretty._deferred_type_pprinters.copy()
423
425
424 #### FormatterABC interface ####
426 #### FormatterABC interface ####
425
427
426 def __call__(self, obj):
428 def __call__(self, obj):
427 """Compute the pretty representation of the object."""
429 """Compute the pretty representation of the object."""
428 if not self.pprint:
430 if not self.pprint:
429 try:
431 try:
430 return repr(obj)
432 return repr(obj)
431 except TypeError:
433 except TypeError:
432 return ''
434 return ''
433 else:
435 else:
434 # This uses use StringIO, as cStringIO doesn't handle unicode.
436 # This uses use StringIO, as cStringIO doesn't handle unicode.
435 stream = StringIO()
437 stream = StringIO()
436 # self.newline.encode() is a quick fix for issue gh-597. We need to
438 # self.newline.encode() is a quick fix for issue gh-597. We need to
437 # ensure that stream does not get a mix of unicode and bytestrings,
439 # ensure that stream does not get a mix of unicode and bytestrings,
438 # or it will cause trouble.
440 # or it will cause trouble.
439 printer = pretty.RepresentationPrinter(stream, self.verbose,
441 printer = pretty.RepresentationPrinter(stream, self.verbose,
440 self.max_width, self.newline.encode(),
442 self.max_width, self.newline.encode(),
441 singleton_pprinters=self.singleton_printers,
443 singleton_pprinters=self.singleton_printers,
442 type_pprinters=self.type_printers,
444 type_pprinters=self.type_printers,
443 deferred_pprinters=self.deferred_printers)
445 deferred_pprinters=self.deferred_printers)
444 printer.pretty(obj)
446 printer.pretty(obj)
445 printer.flush()
447 printer.flush()
446 return stream.getvalue()
448 return stream.getvalue()
447
449
448
450
449 class HTMLFormatter(BaseFormatter):
451 class HTMLFormatter(BaseFormatter):
450 """An HTML formatter.
452 """An HTML formatter.
451
453
452 To define the callables that compute the HTML representation of your
454 To define the callables that compute the HTML representation of your
453 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
455 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
454 or :meth:`for_type_by_name` methods to register functions that handle
456 or :meth:`for_type_by_name` methods to register functions that handle
455 this.
457 this.
456
458
457 The return value of this formatter should be a valid HTML snippet that
459 The return value of this formatter should be a valid HTML snippet that
458 could be injected into an existing DOM. It should *not* include the
460 could be injected into an existing DOM. It should *not* include the
459 ```<html>`` or ```<body>`` tags.
461 ```<html>`` or ```<body>`` tags.
460 """
462 """
461 format_type = Unicode('text/html')
463 format_type = Unicode('text/html')
462
464
463 print_method = ObjectName('_repr_html_')
465 print_method = ObjectName('_repr_html_')
464
466
465
467
466 class SVGFormatter(BaseFormatter):
468 class SVGFormatter(BaseFormatter):
467 """An SVG formatter.
469 """An SVG formatter.
468
470
469 To define the callables that compute the SVG representation of your
471 To define the callables that compute the SVG representation of your
470 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
472 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
471 or :meth:`for_type_by_name` methods to register functions that handle
473 or :meth:`for_type_by_name` methods to register functions that handle
472 this.
474 this.
473
475
474 The return value of this formatter should be valid SVG enclosed in
476 The return value of this formatter should be valid SVG enclosed in
475 ```<svg>``` tags, that could be injected into an existing DOM. It should
477 ```<svg>``` tags, that could be injected into an existing DOM. It should
476 *not* include the ```<html>`` or ```<body>`` tags.
478 *not* include the ```<html>`` or ```<body>`` tags.
477 """
479 """
478 format_type = Unicode('image/svg+xml')
480 format_type = Unicode('image/svg+xml')
479
481
480 print_method = ObjectName('_repr_svg_')
482 print_method = ObjectName('_repr_svg_')
481
483
482
484
483 class PNGFormatter(BaseFormatter):
485 class PNGFormatter(BaseFormatter):
484 """A PNG formatter.
486 """A PNG formatter.
485
487
486 To define the callables that compute the PNG representation of your
488 To define the callables that compute the PNG representation of your
487 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
489 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
488 or :meth:`for_type_by_name` methods to register functions that handle
490 or :meth:`for_type_by_name` methods to register functions that handle
489 this.
491 this.
490
492
491 The return value of this formatter should be raw PNG data, *not*
493 The return value of this formatter should be raw PNG data, *not*
492 base64 encoded.
494 base64 encoded.
493 """
495 """
494 format_type = Unicode('image/png')
496 format_type = Unicode('image/png')
495
497
496 print_method = ObjectName('_repr_png_')
498 print_method = ObjectName('_repr_png_')
497
499
498
500
501 class JPEGFormatter(BaseFormatter):
502 """A JPEG formatter.
503
504 To define the callables that compute the JPEG representation of your
505 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
506 or :meth:`for_type_by_name` methods to register functions that handle
507 this.
508
509 The return value of this formatter should be raw JPEG data, *not*
510 base64 encoded.
511 """
512 format_type = Unicode('image/jpeg')
513
514 print_method = ObjectName('_repr_jpeg_')
515
516
499 class LatexFormatter(BaseFormatter):
517 class LatexFormatter(BaseFormatter):
500 """A LaTeX formatter.
518 """A LaTeX formatter.
501
519
502 To define the callables that compute the LaTeX representation of your
520 To define the callables that compute the LaTeX representation of your
503 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
521 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
504 or :meth:`for_type_by_name` methods to register functions that handle
522 or :meth:`for_type_by_name` methods to register functions that handle
505 this.
523 this.
506
524
507 The return value of this formatter should be a valid LaTeX equation,
525 The return value of this formatter should be a valid LaTeX equation,
508 enclosed in either ```$``` or ```$$```.
526 enclosed in either ```$``` or ```$$```.
509 """
527 """
510 format_type = Unicode('text/latex')
528 format_type = Unicode('text/latex')
511
529
512 print_method = ObjectName('_repr_latex_')
530 print_method = ObjectName('_repr_latex_')
513
531
514
532
515 class JSONFormatter(BaseFormatter):
533 class JSONFormatter(BaseFormatter):
516 """A JSON string formatter.
534 """A JSON string formatter.
517
535
518 To define the callables that compute the JSON string representation of
536 To define the callables that compute the JSON string representation of
519 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
537 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
520 or :meth:`for_type_by_name` methods to register functions that handle
538 or :meth:`for_type_by_name` methods to register functions that handle
521 this.
539 this.
522
540
523 The return value of this formatter should be a valid JSON string.
541 The return value of this formatter should be a valid JSON string.
524 """
542 """
525 format_type = Unicode('application/json')
543 format_type = Unicode('application/json')
526
544
527 print_method = ObjectName('_repr_json_')
545 print_method = ObjectName('_repr_json_')
528
546
529
547
530 class JavascriptFormatter(BaseFormatter):
548 class JavascriptFormatter(BaseFormatter):
531 """A Javascript formatter.
549 """A Javascript formatter.
532
550
533 To define the callables that compute the Javascript representation of
551 To define the callables that compute the Javascript representation of
534 your objects, define a :meth:`_repr_javascript_` method or use the
552 your objects, define a :meth:`_repr_javascript_` method or use the
535 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
553 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
536 that handle this.
554 that handle this.
537
555
538 The return value of this formatter should be valid Javascript code and
556 The return value of this formatter should be valid Javascript code and
539 should *not* be enclosed in ```<script>``` tags.
557 should *not* be enclosed in ```<script>``` tags.
540 """
558 """
541 format_type = Unicode('application/javascript')
559 format_type = Unicode('application/javascript')
542
560
543 print_method = ObjectName('_repr_javascript_')
561 print_method = ObjectName('_repr_javascript_')
544
562
545 FormatterABC.register(BaseFormatter)
563 FormatterABC.register(BaseFormatter)
546 FormatterABC.register(PlainTextFormatter)
564 FormatterABC.register(PlainTextFormatter)
547 FormatterABC.register(HTMLFormatter)
565 FormatterABC.register(HTMLFormatter)
548 FormatterABC.register(SVGFormatter)
566 FormatterABC.register(SVGFormatter)
549 FormatterABC.register(PNGFormatter)
567 FormatterABC.register(PNGFormatter)
568 FormatterABC.register(JPEGFormatter)
550 FormatterABC.register(LatexFormatter)
569 FormatterABC.register(LatexFormatter)
551 FormatterABC.register(JSONFormatter)
570 FormatterABC.register(JSONFormatter)
552 FormatterABC.register(JavascriptFormatter)
571 FormatterABC.register(JavascriptFormatter)
553
572
554
573
555 def format_display_data(obj, include=None, exclude=None):
574 def format_display_data(obj, include=None, exclude=None):
556 """Return a format data dict for an object.
575 """Return a format data dict for an object.
557
576
558 By default all format types will be computed.
577 By default all format types will be computed.
559
578
560 The following MIME types are currently implemented:
579 The following MIME types are currently implemented:
561
580
562 * text/plain
581 * text/plain
563 * text/html
582 * text/html
564 * text/latex
583 * text/latex
565 * application/json
584 * application/json
566 * application/javascript
585 * application/javascript
567 * image/png
586 * image/png
587 * image/jpeg
568 * image/svg+xml
588 * image/svg+xml
569
589
570 Parameters
590 Parameters
571 ----------
591 ----------
572 obj : object
592 obj : object
573 The Python object whose format data will be computed.
593 The Python object whose format data will be computed.
574
594
575 Returns
595 Returns
576 -------
596 -------
577 format_dict : dict
597 format_dict : dict
578 A dictionary of key/value pairs, one or each format that was
598 A dictionary of key/value pairs, one or each format that was
579 generated for the object. The keys are the format types, which
599 generated for the object. The keys are the format types, which
580 will usually be MIME type strings and the values and JSON'able
600 will usually be MIME type strings and the values and JSON'able
581 data structure containing the raw data for the representation in
601 data structure containing the raw data for the representation in
582 that format.
602 that format.
583 include : list or tuple, optional
603 include : list or tuple, optional
584 A list of format type strings (MIME types) to include in the
604 A list of format type strings (MIME types) to include in the
585 format data dict. If this is set *only* the format types included
605 format data dict. If this is set *only* the format types included
586 in this list will be computed.
606 in this list will be computed.
587 exclude : list or tuple, optional
607 exclude : list or tuple, optional
588 A list of format type string (MIME types) to exclue in the format
608 A list of format type string (MIME types) to exclue in the format
589 data dict. If this is set all format types will be computed,
609 data dict. If this is set all format types will be computed,
590 except for those included in this argument.
610 except for those included in this argument.
591 """
611 """
592 from IPython.core.interactiveshell import InteractiveShell
612 from IPython.core.interactiveshell import InteractiveShell
593
613
594 InteractiveShell.instance().display_formatter.format(
614 InteractiveShell.instance().display_formatter.format(
595 obj,
615 obj,
596 include,
616 include,
597 exclude
617 exclude
598 )
618 )
619
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,397 +1,410 b''
1
1
2 //============================================================================
2 //============================================================================
3 // CodeCell
3 // CodeCell
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var CodeCell = function (notebook) {
10 var CodeCell = function (notebook) {
11 this.code_mirror = null;
11 this.code_mirror = null;
12 this.input_prompt_number = ' ';
12 this.input_prompt_number = ' ';
13 this.is_completing = false;
13 this.is_completing = false;
14 this.completion_cursor = null;
14 this.completion_cursor = null;
15 this.outputs = [];
15 this.outputs = [];
16 IPython.Cell.apply(this, arguments);
16 IPython.Cell.apply(this, arguments);
17 };
17 };
18
18
19
19
20 CodeCell.prototype = new IPython.Cell();
20 CodeCell.prototype = new IPython.Cell();
21
21
22
22
23 CodeCell.prototype.create_element = function () {
23 CodeCell.prototype.create_element = function () {
24 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
24 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
25 var input = $('<div></div>').addClass('input hbox');
25 var input = $('<div></div>').addClass('input hbox');
26 input.append($('<div/>').addClass('prompt input_prompt'));
26 input.append($('<div/>').addClass('prompt input_prompt'));
27 var input_area = $('<div/>').addClass('input_area box-flex1');
27 var input_area = $('<div/>').addClass('input_area box-flex1');
28 this.code_mirror = CodeMirror(input_area.get(0), {
28 this.code_mirror = CodeMirror(input_area.get(0), {
29 indentUnit : 4,
29 indentUnit : 4,
30 enterMode : 'flat',
30 enterMode : 'flat',
31 tabMode: 'shift',
31 tabMode: 'shift',
32 mode: 'python',
32 mode: 'python',
33 theme: 'ipython',
33 theme: 'ipython',
34 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
34 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
35 });
35 });
36 input.append(input_area);
36 input.append(input_area);
37 var output = $('<div></div>').addClass('output vbox');
37 var output = $('<div></div>').addClass('output vbox');
38 cell.append(input).append(output);
38 cell.append(input).append(output);
39 this.element = cell;
39 this.element = cell;
40 this.collapse()
40 this.collapse()
41 };
41 };
42
42
43
43
44 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
44 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
45 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
45 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
46 // is used to provide custom key handling. Its return value is used to determine
46 // is used to provide custom key handling. Its return value is used to determine
47 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
47 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
48 if (event.keyCode === 13 && event.shiftKey) {
48 if (event.keyCode === 13 && event.shiftKey) {
49 // Always ignore shift-enter in CodeMirror as we handle it.
49 // Always ignore shift-enter in CodeMirror as we handle it.
50 return true;
50 return true;
51 } else if (event.keyCode === 9) {
51 } else if (event.keyCode === 9) {
52 var cur = editor.getCursor();
52 var cur = editor.getCursor();
53 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
53 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
54 if (pre_cursor === "") {
54 if (pre_cursor === "") {
55 // Don't autocomplete if the part of the line before the cursor is empty.
55 // Don't autocomplete if the part of the line before the cursor is empty.
56 // In this case, let CodeMirror handle indentation.
56 // In this case, let CodeMirror handle indentation.
57 return false;
57 return false;
58 } else {
58 } else {
59 // Autocomplete the current line.
59 // Autocomplete the current line.
60 event.stop();
60 event.stop();
61 var line = editor.getLine(cur.line);
61 var line = editor.getLine(cur.line);
62 this.is_completing = true;
62 this.is_completing = true;
63 this.completion_cursor = cur;
63 this.completion_cursor = cur;
64 IPython.notebook.complete_cell(this, line, cur.ch);
64 IPython.notebook.complete_cell(this, line, cur.ch);
65 return true;
65 return true;
66 }
66 }
67 } else if (event.keyCode === 8) {
67 } else if (event.keyCode === 8) {
68 // If backspace and the line ends with 4 spaces, remove them.
68 // If backspace and the line ends with 4 spaces, remove them.
69 var cur = editor.getCursor();
69 var cur = editor.getCursor();
70 var line = editor.getLine(cur.line);
70 var line = editor.getLine(cur.line);
71 var ending = line.slice(-4);
71 var ending = line.slice(-4);
72 if (ending === ' ') {
72 if (ending === ' ') {
73 editor.replaceRange('',
73 editor.replaceRange('',
74 {line: cur.line, ch: cur.ch-4},
74 {line: cur.line, ch: cur.ch-4},
75 {line: cur.line, ch: cur.ch}
75 {line: cur.line, ch: cur.ch}
76 );
76 );
77 event.stop();
77 event.stop();
78 return true;
78 return true;
79 } else {
79 } else {
80 return false;
80 return false;
81 };
81 };
82 } else {
82 } else {
83 if (this.is_completing && this.completion_cursor !== editor.getCursor()) {
83 if (this.is_completing && this.completion_cursor !== editor.getCursor()) {
84 this.is_completing = false;
84 this.is_completing = false;
85 this.completion_cursor = null;
85 this.completion_cursor = null;
86 }
86 }
87 return false;
87 return false;
88 };
88 };
89 };
89 };
90
90
91
91
92 CodeCell.prototype.finish_completing = function (matched_text, matches) {
92 CodeCell.prototype.finish_completing = function (matched_text, matches) {
93 if (!this.is_completing || matches.length === 0) {return;}
93 if (!this.is_completing || matches.length === 0) {return;}
94 // console.log("Got matches", matched_text, matches);
94 // console.log("Got matches", matched_text, matches);
95
95
96 var that = this;
96 var that = this;
97 var cur = this.completion_cursor;
97 var cur = this.completion_cursor;
98 var complete = $('<div/>').addClass('completions');
98 var complete = $('<div/>').addClass('completions');
99 var select = $('<select/>').attr('multiple','true');
99 var select = $('<select/>').attr('multiple','true');
100 for (var i=0; i<matches.length; ++i) {
100 for (var i=0; i<matches.length; ++i) {
101 select.append($('<option/>').text(matches[i]));
101 select.append($('<option/>').text(matches[i]));
102 }
102 }
103 select.children().first().attr('selected','true');
103 select.children().first().attr('selected','true');
104 select.attr('size',Math.min(10,matches.length));
104 select.attr('size',Math.min(10,matches.length));
105 var pos = this.code_mirror.cursorCoords();
105 var pos = this.code_mirror.cursorCoords();
106 complete.css('left',pos.x+'px');
106 complete.css('left',pos.x+'px');
107 complete.css('top',pos.yBot+'px');
107 complete.css('top',pos.yBot+'px');
108 complete.append(select);
108 complete.append(select);
109
109
110 $('body').append(complete);
110 $('body').append(complete);
111 var done = false;
111 var done = false;
112
112
113 var insert = function (selected_text) {
113 var insert = function (selected_text) {
114 that.code_mirror.replaceRange(
114 that.code_mirror.replaceRange(
115 selected_text,
115 selected_text,
116 {line: cur.line, ch: (cur.ch-matched_text.length)},
116 {line: cur.line, ch: (cur.ch-matched_text.length)},
117 {line: cur.line, ch: cur.ch}
117 {line: cur.line, ch: cur.ch}
118 );
118 );
119 };
119 };
120
120
121 var close = function () {
121 var close = function () {
122 if (done) return;
122 if (done) return;
123 done = true;
123 done = true;
124 complete.remove();
124 complete.remove();
125 that.is_completing = false;
125 that.is_completing = false;
126 that.completion_cursor = null;
126 that.completion_cursor = null;
127 };
127 };
128
128
129 var pick = function () {
129 var pick = function () {
130 insert(select.val()[0]);
130 insert(select.val()[0]);
131 close();
131 close();
132 setTimeout(function(){that.code_mirror.focus();}, 50);
132 setTimeout(function(){that.code_mirror.focus();}, 50);
133 };
133 };
134
134
135 select.blur(close);
135 select.blur(close);
136 select.keydown(function (event) {
136 select.keydown(function (event) {
137 var code = event.which;
137 var code = event.which;
138 if (code === 13 || code === 32) {
138 if (code === 13 || code === 32) {
139 // Pressing SPACE or ENTER will cause a pick
139 // Pressing SPACE or ENTER will cause a pick
140 event.stopPropagation();
140 event.stopPropagation();
141 event.preventDefault();
141 event.preventDefault();
142 pick();
142 pick();
143 } else if (code === 38 || code === 40) {
143 } else if (code === 38 || code === 40) {
144 // We don't want the document keydown handler to handle UP/DOWN,
144 // We don't want the document keydown handler to handle UP/DOWN,
145 // but we want the default action.
145 // but we want the default action.
146 event.stopPropagation();
146 event.stopPropagation();
147 } else {
147 } else {
148 // All other key presses simple exit completion.
148 // All other key presses simple exit completion.
149 event.stopPropagation();
149 event.stopPropagation();
150 event.preventDefault();
150 event.preventDefault();
151 close();
151 close();
152 that.code_mirror.focus();
152 that.code_mirror.focus();
153 }
153 }
154 });
154 });
155 // Double click also causes a pick.
155 // Double click also causes a pick.
156 select.dblclick(pick);
156 select.dblclick(pick);
157 select.focus();
157 select.focus();
158 };
158 };
159
159
160
160
161 CodeCell.prototype.select = function () {
161 CodeCell.prototype.select = function () {
162 IPython.Cell.prototype.select.apply(this);
162 IPython.Cell.prototype.select.apply(this);
163 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
163 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
164 // not causing the cursor to blink if the editor is empty initially.
164 // not causing the cursor to blink if the editor is empty initially.
165 // While this seems to fix the issue, this should be fixed
165 // While this seems to fix the issue, this should be fixed
166 // in CodeMirror proper.
166 // in CodeMirror proper.
167 var s = this.code_mirror.getValue();
167 var s = this.code_mirror.getValue();
168 if (s === '') this.code_mirror.setValue('.');
168 if (s === '') this.code_mirror.setValue('.');
169 this.code_mirror.focus();
169 this.code_mirror.focus();
170 if (s === '') this.code_mirror.setValue('');
170 if (s === '') this.code_mirror.setValue('');
171 };
171 };
172
172
173
173
174 CodeCell.prototype.append_output = function (json) {
174 CodeCell.prototype.append_output = function (json) {
175 this.expand();
175 this.expand();
176 if (json.output_type === 'pyout') {
176 if (json.output_type === 'pyout') {
177 this.append_pyout(json);
177 this.append_pyout(json);
178 } else if (json.output_type === 'pyerr') {
178 } else if (json.output_type === 'pyerr') {
179 this.append_pyerr(json);
179 this.append_pyerr(json);
180 } else if (json.output_type === 'display_data') {
180 } else if (json.output_type === 'display_data') {
181 this.append_display_data(json);
181 this.append_display_data(json);
182 } else if (json.output_type === 'stream') {
182 } else if (json.output_type === 'stream') {
183 this.append_stream(json);
183 this.append_stream(json);
184 };
184 };
185 this.outputs.push(json);
185 this.outputs.push(json);
186 };
186 };
187
187
188
188
189 CodeCell.prototype.append_pyout = function (json) {
189 CodeCell.prototype.append_pyout = function (json) {
190 n = json.prompt_number || ' ';
190 n = json.prompt_number || ' ';
191 var toinsert = $("<div/>").addClass("output_area output_pyout hbox");
191 var toinsert = $("<div/>").addClass("output_area output_pyout hbox");
192 toinsert.append($('<div/>').
192 toinsert.append($('<div/>').
193 addClass('prompt output_prompt').
193 addClass('prompt output_prompt').
194 html('Out[' + n + ']:')
194 html('Out[' + n + ']:')
195 );
195 );
196 this.append_mime_type(json, toinsert);
196 this.append_mime_type(json, toinsert);
197 toinsert.children().last().addClass("box_flex1");
197 toinsert.children().last().addClass("box_flex1");
198 this.element.find("div.output").append(toinsert);
198 this.element.find("div.output").append(toinsert);
199 // If we just output latex, typeset it.
199 // If we just output latex, typeset it.
200 if (json.latex !== undefined) {
200 if (json.latex !== undefined) {
201 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
201 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
202 };
202 };
203 };
203 };
204
204
205
205
206 CodeCell.prototype.append_pyerr = function (json) {
206 CodeCell.prototype.append_pyerr = function (json) {
207 var tb = json.traceback;
207 var tb = json.traceback;
208 var s = '';
208 if (tb !== undefined) {
209 var len = tb.length;
209 var s = '';
210 for (var i=0; i<len; i++) {
210 var len = tb.length;
211 s = s + tb[i] + '\n';
211 for (var i=0; i<len; i++) {
212 }
212 s = s + tb[i] + '\n';
213 s = s + '\n';
213 }
214 this.append_text(s);
214 s = s + '\n';
215 this.append_text(s);
216 };
215 };
217 };
216
218
217
219
218 CodeCell.prototype.append_stream = function (json) {
220 CodeCell.prototype.append_stream = function (json) {
219 this.append_text(json.text);
221 this.append_text(json.text);
220 };
222 };
221
223
222
224
223 CodeCell.prototype.append_display_data = function (json) {
225 CodeCell.prototype.append_display_data = function (json) {
224 this.append_mime_type(json);
226 this.append_mime_type(json);
225 };
227 };
226
228
227
229
228 CodeCell.prototype.append_mime_type = function (json, element) {
230 CodeCell.prototype.append_mime_type = function (json, element) {
229 if (json.html !== undefined) {
231 if (json.html !== undefined) {
230 this.append_html(json.html, element);
232 this.append_html(json.html, element);
231 } else if (json.latex !== undefined) {
233 } else if (json.latex !== undefined) {
232 this.append_latex(json.latex, element);
234 this.append_latex(json.latex, element);
233 // If it is undefined, then we just appended to div.output, which
235 // If it is undefined, then we just appended to div.output, which
234 // makes the latex visible and we can typeset it. The typesetting
236 // makes the latex visible and we can typeset it. The typesetting
235 // has to be done after the latex is on the page.
237 // has to be done after the latex is on the page.
236 if (element === undefined) {
238 if (element === undefined) {
237 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
239 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
238 };
240 };
239 } else if (json.svg !== undefined) {
241 } else if (json.svg !== undefined) {
240 this.append_svg(json.svg, element);
242 this.append_svg(json.svg, element);
241 } else if (json.png !== undefined) {
243 } else if (json.png !== undefined) {
242 this.append_png(json.png, element);
244 this.append_png(json.png, element);
245 } else if (json.jpeg !== undefined) {
246 this.append_jpeg(json.jpeg, element);
243 } else if (json.text !== undefined) {
247 } else if (json.text !== undefined) {
244 this.append_text(json.text, element);
248 this.append_text(json.text, element);
245 };
249 };
246 return element;
250 return element;
247 };
251 };
248
252
249
253
250 CodeCell.prototype.append_html = function (html, element) {
254 CodeCell.prototype.append_html = function (html, element) {
251 element = element || this.element.find("div.output");
255 element = element || this.element.find("div.output");
252 var toinsert = $("<div/>").addClass("output_area output_html");
256 var toinsert = $("<div/>").addClass("output_area output_html");
253 toinsert.append(html);
257 toinsert.append(html);
254 element.append(toinsert);
258 element.append(toinsert);
255 return element;
259 return element;
256 }
260 }
257
261
258
262
259 CodeCell.prototype.append_text = function (data, element) {
263 CodeCell.prototype.append_text = function (data, element) {
260 element = element || this.element.find("div.output");
264 element = element || this.element.find("div.output");
261 var toinsert = $("<div/>").addClass("output_area output_stream");
265 var toinsert = $("<div/>").addClass("output_area output_stream");
262 toinsert.append($("<pre/>").html(utils.fixConsole(data)));
266 toinsert.append($("<pre/>").html(utils.fixConsole(data)));
263 element.append(toinsert);
267 element.append(toinsert);
264 return element;
268 return element;
265 };
269 };
266
270
267
271
268 CodeCell.prototype.append_svg = function (svg, element) {
272 CodeCell.prototype.append_svg = function (svg, element) {
269 element = element || this.element.find("div.output");
273 element = element || this.element.find("div.output");
270 var toinsert = $("<div/>").addClass("output_area output_svg");
274 var toinsert = $("<div/>").addClass("output_area output_svg");
271 toinsert.append(svg);
275 toinsert.append(svg);
272 element.append(toinsert);
276 element.append(toinsert);
273 return element;
277 return element;
274 };
278 };
275
279
276
280
277 CodeCell.prototype.append_png = function (png, element) {
281 CodeCell.prototype.append_png = function (png, element) {
278 element = element || this.element.find("div.output");
282 element = element || this.element.find("div.output");
279 var toinsert = $("<div/>").addClass("output_area output_png");
283 var toinsert = $("<div/>").addClass("output_area output_png");
280 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
284 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
281 element.append(toinsert);
285 element.append(toinsert);
282 return element;
286 return element;
283 };
287 };
284
288
285
289
290 CodeCell.prototype.append_jpeg = function (jpeg, element) {
291 element = element || this.element.find("div.output");
292 var toinsert = $("<div/>").addClass("output_area output_jpeg");
293 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
294 element.append(toinsert);
295 return element;
296 };
297
298
286 CodeCell.prototype.append_latex = function (latex, element) {
299 CodeCell.prototype.append_latex = function (latex, element) {
287 // This method cannot do the typesetting because the latex first has to
300 // This method cannot do the typesetting because the latex first has to
288 // be on the page.
301 // be on the page.
289 element = element || this.element.find("div.output");
302 element = element || this.element.find("div.output");
290 var toinsert = $("<div/>").addClass("output_area output_latex");
303 var toinsert = $("<div/>").addClass("output_area output_latex");
291 toinsert.append(latex);
304 toinsert.append(latex);
292 element.append(toinsert);
305 element.append(toinsert);
293 return element;
306 return element;
294 }
307 }
295
308
296
309
297 CodeCell.prototype.clear_output = function () {
310 CodeCell.prototype.clear_output = function () {
298 this.element.find("div.output").html("");
311 this.element.find("div.output").html("");
299 this.outputs = [];
312 this.outputs = [];
300 };
313 };
301
314
302
315
303 CodeCell.prototype.clear_input = function () {
316 CodeCell.prototype.clear_input = function () {
304 this.code_mirror.setValue('');
317 this.code_mirror.setValue('');
305 };
318 };
306
319
307
320
308 CodeCell.prototype.collapse = function () {
321 CodeCell.prototype.collapse = function () {
309 this.element.find('div.output').hide();
322 this.element.find('div.output').hide();
310 };
323 };
311
324
312
325
313 CodeCell.prototype.expand = function () {
326 CodeCell.prototype.expand = function () {
314 this.element.find('div.output').show();
327 this.element.find('div.output').show();
315 };
328 };
316
329
317
330
318 CodeCell.prototype.set_input_prompt = function (number) {
331 CodeCell.prototype.set_input_prompt = function (number) {
319 var n = number || ' ';
332 var n = number || ' ';
320 this.input_prompt_number = n
333 this.input_prompt_number = n
321 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
334 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
322 };
335 };
323
336
324
337
325 CodeCell.prototype.get_code = function () {
338 CodeCell.prototype.get_code = function () {
326 return this.code_mirror.getValue();
339 return this.code_mirror.getValue();
327 };
340 };
328
341
329
342
330 CodeCell.prototype.set_code = function (code) {
343 CodeCell.prototype.set_code = function (code) {
331 return this.code_mirror.setValue(code);
344 return this.code_mirror.setValue(code);
332 };
345 };
333
346
334
347
335 CodeCell.prototype.at_top = function () {
348 CodeCell.prototype.at_top = function () {
336 var cursor = this.code_mirror.getCursor();
349 var cursor = this.code_mirror.getCursor();
337 if (cursor.line === 0) {
350 if (cursor.line === 0) {
338 return true;
351 return true;
339 } else {
352 } else {
340 return false;
353 return false;
341 }
354 }
342 };
355 };
343
356
344
357
345 CodeCell.prototype.at_bottom = function () {
358 CodeCell.prototype.at_bottom = function () {
346 var cursor = this.code_mirror.getCursor();
359 var cursor = this.code_mirror.getCursor();
347 if (cursor.line === (this.code_mirror.lineCount()-1)) {
360 if (cursor.line === (this.code_mirror.lineCount()-1)) {
348 return true;
361 return true;
349 } else {
362 } else {
350 return false;
363 return false;
351 }
364 }
352 };
365 };
353
366
354
367
355 CodeCell.prototype.fromJSON = function (data) {
368 CodeCell.prototype.fromJSON = function (data) {
356 // console.log('Import from JSON:', data);
369 // console.log('Import from JSON:', data);
357 if (data.cell_type === 'code') {
370 if (data.cell_type === 'code') {
358 if (data.input !== undefined) {
371 if (data.input !== undefined) {
359 this.set_code(data.input);
372 this.set_code(data.input);
360 }
373 }
361 if (data.prompt_number !== undefined) {
374 if (data.prompt_number !== undefined) {
362 this.set_input_prompt(data.prompt_number);
375 this.set_input_prompt(data.prompt_number);
363 } else {
376 } else {
364 this.set_input_prompt();
377 this.set_input_prompt();
365 };
378 };
366 var len = data.outputs.length;
379 var len = data.outputs.length;
367 for (var i=0; i<len; i++) {
380 for (var i=0; i<len; i++) {
368 this.append_output(data.outputs[i]);
381 this.append_output(data.outputs[i]);
369 };
382 };
370 };
383 };
371 };
384 };
372
385
373
386
374 CodeCell.prototype.toJSON = function () {
387 CodeCell.prototype.toJSON = function () {
375 var data = {};
388 var data = {};
376 data.input = this.get_code();
389 data.input = this.get_code();
377 data.cell_type = 'code';
390 data.cell_type = 'code';
378 if (this.input_prompt_number !== ' ') {
391 if (this.input_prompt_number !== ' ') {
379 data.prompt_number = this.input_prompt_number
392 data.prompt_number = this.input_prompt_number
380 };
393 };
381 var outputs = [];
394 var outputs = [];
382 var len = this.outputs.length;
395 var len = this.outputs.length;
383 for (var i=0; i<len; i++) {
396 for (var i=0; i<len; i++) {
384 outputs[i] = this.outputs[i];
397 outputs[i] = this.outputs[i];
385 };
398 };
386 data.outputs = outputs;
399 data.outputs = outputs;
387 data.language = 'python';
400 data.language = 'python';
388 // console.log('Export to JSON:',data);
401 // console.log('Export to JSON:',data);
389 return data;
402 return data;
390 };
403 };
391
404
392
405
393 IPython.CodeCell = CodeCell;
406 IPython.CodeCell = CodeCell;
394
407
395 return IPython;
408 return IPython;
396 }(IPython));
409 }(IPython));
397
410
@@ -1,729 +1,732 b''
1
1
2 //============================================================================
2 //============================================================================
3 // Notebook
3 // Notebook
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var Notebook = function (selector) {
10 var Notebook = function (selector) {
11 this.element = $(selector);
11 this.element = $(selector);
12 this.element.scroll();
12 this.element.scroll();
13 this.element.data("notebook", this);
13 this.element.data("notebook", this);
14 this.next_prompt_number = 1;
14 this.next_prompt_number = 1;
15 this.kernel = null;
15 this.kernel = null;
16 this.msg_cell_map = {};
16 this.msg_cell_map = {};
17 this.style();
17 this.style();
18 this.create_elements();
18 this.create_elements();
19 this.bind_events();
19 this.bind_events();
20 };
20 };
21
21
22
22
23 Notebook.prototype.style = function () {
23 Notebook.prototype.style = function () {
24 $('div#notebook').addClass('border-box-sizing');
24 $('div#notebook').addClass('border-box-sizing');
25 };
25 };
26
26
27
27
28 Notebook.prototype.create_elements = function () {
28 Notebook.prototype.create_elements = function () {
29 // We add this end_space div to the end of the notebook div to:
29 // We add this end_space div to the end of the notebook div to:
30 // i) provide a margin between the last cell and the end of the notebook
30 // i) provide a margin between the last cell and the end of the notebook
31 // ii) to prevent the div from scrolling up when the last cell is being
31 // ii) to prevent the div from scrolling up when the last cell is being
32 // edited, but is too low on the page, which browsers will do automatically.
32 // edited, but is too low on the page, which browsers will do automatically.
33 this.element.append($('<div class="end_space"></div>').height(50));
33 this.element.append($('<div class="end_space"></div>').height(50));
34 $('div#notebook').addClass('border-box-sizing');
34 $('div#notebook').addClass('border-box-sizing');
35 };
35 };
36
36
37
37
38 Notebook.prototype.bind_events = function () {
38 Notebook.prototype.bind_events = function () {
39 var that = this;
39 var that = this;
40 $(document).keydown(function (event) {
40 $(document).keydown(function (event) {
41 // console.log(event);
41 // console.log(event);
42 if (event.which === 38) {
42 if (event.which === 38) {
43 var cell = that.selected_cell();
43 var cell = that.selected_cell();
44 if (cell.at_top()) {
44 if (cell.at_top()) {
45 event.preventDefault();
45 event.preventDefault();
46 that.select_prev();
46 that.select_prev();
47 };
47 };
48 } else if (event.which === 40) {
48 } else if (event.which === 40) {
49 var cell = that.selected_cell();
49 var cell = that.selected_cell();
50 if (cell.at_bottom()) {
50 if (cell.at_bottom()) {
51 event.preventDefault();
51 event.preventDefault();
52 that.select_next();
52 that.select_next();
53 };
53 };
54 } else if (event.which === 13 && event.shiftKey) {
54 } else if (event.which === 13 && event.shiftKey) {
55 that.execute_selected_cell();
55 that.execute_selected_cell();
56 return false;
56 return false;
57 } else if (event.which === 13 && event.ctrlKey) {
57 } else if (event.which === 13 && event.ctrlKey) {
58 that.execute_selected_cell({terminal:true});
58 that.execute_selected_cell({terminal:true});
59 return false;
59 return false;
60 };
60 };
61 });
61 });
62
62
63 this.element.bind('collapse_pager', function () {
63 this.element.bind('collapse_pager', function () {
64 var app_height = $('div#main_app').height(); // content height
64 var app_height = $('div#main_app').height(); // content height
65 var splitter_height = $('div#pager_splitter').outerHeight(true);
65 var splitter_height = $('div#pager_splitter').outerHeight(true);
66 var new_height = app_height - splitter_height;
66 var new_height = app_height - splitter_height;
67 that.element.animate({height : new_height + 'px'}, 'fast');
67 that.element.animate({height : new_height + 'px'}, 'fast');
68 });
68 });
69
69
70 this.element.bind('expand_pager', function () {
70 this.element.bind('expand_pager', function () {
71 var app_height = $('div#main_app').height(); // content height
71 var app_height = $('div#main_app').height(); // content height
72 var splitter_height = $('div#pager_splitter').outerHeight(true);
72 var splitter_height = $('div#pager_splitter').outerHeight(true);
73 var pager_height = $('div#pager').outerHeight(true);
73 var pager_height = $('div#pager').outerHeight(true);
74 var new_height = app_height - pager_height - splitter_height;
74 var new_height = app_height - pager_height - splitter_height;
75 that.element.animate({height : new_height + 'px'}, 'fast');
75 that.element.animate({height : new_height + 'px'}, 'fast');
76 });
76 });
77
77
78 this.element.bind('collapse_left_panel', function () {
78 this.element.bind('collapse_left_panel', function () {
79 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
79 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
80 var new_margin = splitter_width;
80 var new_margin = splitter_width;
81 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
81 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
82 });
82 });
83
83
84 this.element.bind('expand_left_panel', function () {
84 this.element.bind('expand_left_panel', function () {
85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
86 var left_panel_width = IPython.left_panel.width;
86 var left_panel_width = IPython.left_panel.width;
87 var new_margin = splitter_width + left_panel_width;
87 var new_margin = splitter_width + left_panel_width;
88 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
88 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
89 });
89 });
90 };
90 };
91
91
92
92
93 Notebook.prototype.scroll_to_bottom = function () {
93 Notebook.prototype.scroll_to_bottom = function () {
94 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
94 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
95 };
95 };
96
96
97
97
98 Notebook.prototype.scroll_to_top = function () {
98 Notebook.prototype.scroll_to_top = function () {
99 this.element.animate({scrollTop:0}, 0);
99 this.element.animate({scrollTop:0}, 0);
100 };
100 };
101
101
102
102
103 // Cell indexing, retrieval, etc.
103 // Cell indexing, retrieval, etc.
104
104
105
105
106 Notebook.prototype.cell_elements = function () {
106 Notebook.prototype.cell_elements = function () {
107 return this.element.children("div.cell");
107 return this.element.children("div.cell");
108 }
108 }
109
109
110
110
111 Notebook.prototype.ncells = function (cell) {
111 Notebook.prototype.ncells = function (cell) {
112 return this.cell_elements().length;
112 return this.cell_elements().length;
113 }
113 }
114
114
115
115
116 // TODO: we are often calling cells as cells()[i], which we should optimize
116 // TODO: we are often calling cells as cells()[i], which we should optimize
117 // to cells(i) or a new method.
117 // to cells(i) or a new method.
118 Notebook.prototype.cells = function () {
118 Notebook.prototype.cells = function () {
119 return this.cell_elements().toArray().map(function (e) {
119 return this.cell_elements().toArray().map(function (e) {
120 return $(e).data("cell");
120 return $(e).data("cell");
121 });
121 });
122 }
122 }
123
123
124
124
125 Notebook.prototype.find_cell_index = function (cell) {
125 Notebook.prototype.find_cell_index = function (cell) {
126 var result = null;
126 var result = null;
127 this.cell_elements().filter(function (index) {
127 this.cell_elements().filter(function (index) {
128 if ($(this).data("cell") === cell) {
128 if ($(this).data("cell") === cell) {
129 result = index;
129 result = index;
130 };
130 };
131 });
131 });
132 return result;
132 return result;
133 };
133 };
134
134
135
135
136 Notebook.prototype.index_or_selected = function (index) {
136 Notebook.prototype.index_or_selected = function (index) {
137 return index || this.selected_index() || 0;
137 return index || this.selected_index() || 0;
138 }
138 }
139
139
140
140
141 Notebook.prototype.select = function (index) {
141 Notebook.prototype.select = function (index) {
142 if (index !== undefined && index >= 0 && index < this.ncells()) {
142 if (index !== undefined && index >= 0 && index < this.ncells()) {
143 if (this.selected_index() !== null) {
143 if (this.selected_index() !== null) {
144 this.selected_cell().unselect();
144 this.selected_cell().unselect();
145 };
145 };
146 this.cells()[index].select();
146 this.cells()[index].select();
147 if (index === (this.ncells()-1)) {
147 if (index === (this.ncells()-1)) {
148 this.scroll_to_bottom();
148 this.scroll_to_bottom();
149 };
149 };
150 };
150 };
151 return this;
151 return this;
152 };
152 };
153
153
154
154
155 Notebook.prototype.select_next = function () {
155 Notebook.prototype.select_next = function () {
156 var index = this.selected_index();
156 var index = this.selected_index();
157 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
157 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
158 this.select(index+1);
158 this.select(index+1);
159 };
159 };
160 return this;
160 return this;
161 };
161 };
162
162
163
163
164 Notebook.prototype.select_prev = function () {
164 Notebook.prototype.select_prev = function () {
165 var index = this.selected_index();
165 var index = this.selected_index();
166 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
166 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
167 this.select(index-1);
167 this.select(index-1);
168 };
168 };
169 return this;
169 return this;
170 };
170 };
171
171
172
172
173 Notebook.prototype.selected_index = function () {
173 Notebook.prototype.selected_index = function () {
174 var result = null;
174 var result = null;
175 this.cell_elements().filter(function (index) {
175 this.cell_elements().filter(function (index) {
176 if ($(this).data("cell").selected === true) {
176 if ($(this).data("cell").selected === true) {
177 result = index;
177 result = index;
178 };
178 };
179 });
179 });
180 return result;
180 return result;
181 };
181 };
182
182
183
183
184 Notebook.prototype.cell_for_msg = function (msg_id) {
184 Notebook.prototype.cell_for_msg = function (msg_id) {
185 var cell_id = this.msg_cell_map[msg_id];
185 var cell_id = this.msg_cell_map[msg_id];
186 var result = null;
186 var result = null;
187 this.cell_elements().filter(function (index) {
187 this.cell_elements().filter(function (index) {
188 cell = $(this).data("cell");
188 cell = $(this).data("cell");
189 if (cell.cell_id === cell_id) {
189 if (cell.cell_id === cell_id) {
190 result = cell;
190 result = cell;
191 };
191 };
192 });
192 });
193 return result;
193 return result;
194 };
194 };
195
195
196
196
197 Notebook.prototype.selected_cell = function () {
197 Notebook.prototype.selected_cell = function () {
198 return this.cell_elements().eq(this.selected_index()).data("cell");
198 return this.cell_elements().eq(this.selected_index()).data("cell");
199 }
199 }
200
200
201
201
202 // Cell insertion, deletion and moving.
202 // Cell insertion, deletion and moving.
203
203
204
204
205 Notebook.prototype.delete_cell = function (index) {
205 Notebook.prototype.delete_cell = function (index) {
206 var i = index || this.selected_index();
206 var i = index || this.selected_index();
207 if (i !== null && i >= 0 && i < this.ncells()) {
207 if (i !== null && i >= 0 && i < this.ncells()) {
208 this.cell_elements().eq(i).remove();
208 this.cell_elements().eq(i).remove();
209 if (i === (this.ncells())) {
209 if (i === (this.ncells())) {
210 this.select(i-1);
210 this.select(i-1);
211 } else {
211 } else {
212 this.select(i);
212 this.select(i);
213 };
213 };
214 };
214 };
215 return this;
215 return this;
216 };
216 };
217
217
218
218
219 Notebook.prototype.append_cell = function (cell) {
219 Notebook.prototype.append_cell = function (cell) {
220 this.element.find('div.end_space').before(cell.element);
220 this.element.find('div.end_space').before(cell.element);
221 return this;
221 return this;
222 };
222 };
223
223
224
224
225 Notebook.prototype.insert_cell_after = function (cell, index) {
225 Notebook.prototype.insert_cell_after = function (cell, index) {
226 var ncells = this.ncells();
226 var ncells = this.ncells();
227 if (ncells === 0) {
227 if (ncells === 0) {
228 this.append_cell(cell);
228 this.append_cell(cell);
229 return this;
229 return this;
230 };
230 };
231 if (index >= 0 && index < ncells) {
231 if (index >= 0 && index < ncells) {
232 this.cell_elements().eq(index).after(cell.element);
232 this.cell_elements().eq(index).after(cell.element);
233 };
233 };
234 return this
234 return this
235 };
235 };
236
236
237
237
238 Notebook.prototype.insert_cell_before = function (cell, index) {
238 Notebook.prototype.insert_cell_before = function (cell, index) {
239 var ncells = this.ncells();
239 var ncells = this.ncells();
240 if (ncells === 0) {
240 if (ncells === 0) {
241 this.append_cell(cell);
241 this.append_cell(cell);
242 return this;
242 return this;
243 };
243 };
244 if (index >= 0 && index < ncells) {
244 if (index >= 0 && index < ncells) {
245 this.cell_elements().eq(index).before(cell.element);
245 this.cell_elements().eq(index).before(cell.element);
246 };
246 };
247 return this;
247 return this;
248 };
248 };
249
249
250
250
251 Notebook.prototype.move_cell_up = function (index) {
251 Notebook.prototype.move_cell_up = function (index) {
252 var i = index || this.selected_index();
252 var i = index || this.selected_index();
253 if (i !== null && i < this.ncells() && i > 0) {
253 if (i !== null && i < this.ncells() && i > 0) {
254 var pivot = this.cell_elements().eq(i-1);
254 var pivot = this.cell_elements().eq(i-1);
255 var tomove = this.cell_elements().eq(i);
255 var tomove = this.cell_elements().eq(i);
256 if (pivot !== null && tomove !== null) {
256 if (pivot !== null && tomove !== null) {
257 tomove.detach();
257 tomove.detach();
258 pivot.before(tomove);
258 pivot.before(tomove);
259 this.select(i-1);
259 this.select(i-1);
260 };
260 };
261 };
261 };
262 return this;
262 return this;
263 }
263 }
264
264
265
265
266 Notebook.prototype.move_cell_down = function (index) {
266 Notebook.prototype.move_cell_down = function (index) {
267 var i = index || this.selected_index();
267 var i = index || this.selected_index();
268 if (i !== null && i < (this.ncells()-1) && i >= 0) {
268 if (i !== null && i < (this.ncells()-1) && i >= 0) {
269 var pivot = this.cell_elements().eq(i+1)
269 var pivot = this.cell_elements().eq(i+1)
270 var tomove = this.cell_elements().eq(i)
270 var tomove = this.cell_elements().eq(i)
271 if (pivot !== null && tomove !== null) {
271 if (pivot !== null && tomove !== null) {
272 tomove.detach();
272 tomove.detach();
273 pivot.after(tomove);
273 pivot.after(tomove);
274 this.select(i+1);
274 this.select(i+1);
275 };
275 };
276 };
276 };
277 return this;
277 return this;
278 }
278 }
279
279
280
280
281 Notebook.prototype.sort_cells = function () {
281 Notebook.prototype.sort_cells = function () {
282 var ncells = this.ncells();
282 var ncells = this.ncells();
283 var sindex = this.selected_index();
283 var sindex = this.selected_index();
284 var swapped;
284 var swapped;
285 do {
285 do {
286 swapped = false
286 swapped = false
287 for (var i=1; i<ncells; i++) {
287 for (var i=1; i<ncells; i++) {
288 current = this.cell_elements().eq(i).data("cell");
288 current = this.cell_elements().eq(i).data("cell");
289 previous = this.cell_elements().eq(i-1).data("cell");
289 previous = this.cell_elements().eq(i-1).data("cell");
290 if (previous.input_prompt_number > current.input_prompt_number) {
290 if (previous.input_prompt_number > current.input_prompt_number) {
291 this.move_cell_up(i);
291 this.move_cell_up(i);
292 swapped = true;
292 swapped = true;
293 };
293 };
294 };
294 };
295 } while (swapped);
295 } while (swapped);
296 this.select(sindex);
296 this.select(sindex);
297 return this;
297 return this;
298 };
298 };
299
299
300
300
301 Notebook.prototype.insert_code_cell_before = function (index) {
301 Notebook.prototype.insert_code_cell_before = function (index) {
302 // TODO: Bounds check for i
302 // TODO: Bounds check for i
303 var i = this.index_or_selected(index);
303 var i = this.index_or_selected(index);
304 var cell = new IPython.CodeCell(this);
304 var cell = new IPython.CodeCell(this);
305 cell.set_input_prompt();
305 cell.set_input_prompt();
306 this.insert_cell_before(cell, i);
306 this.insert_cell_before(cell, i);
307 this.select(this.find_cell_index(cell));
307 this.select(this.find_cell_index(cell));
308 return cell;
308 return cell;
309 }
309 }
310
310
311
311
312 Notebook.prototype.insert_code_cell_after = function (index) {
312 Notebook.prototype.insert_code_cell_after = function (index) {
313 // TODO: Bounds check for i
313 // TODO: Bounds check for i
314 var i = this.index_or_selected(index);
314 var i = this.index_or_selected(index);
315 var cell = new IPython.CodeCell(this);
315 var cell = new IPython.CodeCell(this);
316 cell.set_input_prompt();
316 cell.set_input_prompt();
317 this.insert_cell_after(cell, i);
317 this.insert_cell_after(cell, i);
318 this.select(this.find_cell_index(cell));
318 this.select(this.find_cell_index(cell));
319 return cell;
319 return cell;
320 }
320 }
321
321
322
322
323 Notebook.prototype.insert_html_cell_before = function (index) {
323 Notebook.prototype.insert_html_cell_before = function (index) {
324 // TODO: Bounds check for i
324 // TODO: Bounds check for i
325 var i = this.index_or_selected(index);
325 var i = this.index_or_selected(index);
326 var cell = new IPython.HTMLCell(this);
326 var cell = new IPython.HTMLCell(this);
327 cell.config_mathjax();
327 cell.config_mathjax();
328 this.insert_cell_before(cell, i);
328 this.insert_cell_before(cell, i);
329 this.select(this.find_cell_index(cell));
329 this.select(this.find_cell_index(cell));
330 return cell;
330 return cell;
331 }
331 }
332
332
333
333
334 Notebook.prototype.insert_html_cell_after = function (index) {
334 Notebook.prototype.insert_html_cell_after = function (index) {
335 // TODO: Bounds check for i
335 // TODO: Bounds check for i
336 var i = this.index_or_selected(index);
336 var i = this.index_or_selected(index);
337 var cell = new IPython.HTMLCell(this);
337 var cell = new IPython.HTMLCell(this);
338 cell.config_mathjax();
338 cell.config_mathjax();
339 this.insert_cell_after(cell, i);
339 this.insert_cell_after(cell, i);
340 this.select(this.find_cell_index(cell));
340 this.select(this.find_cell_index(cell));
341 return cell;
341 return cell;
342 }
342 }
343
343
344
344
345 Notebook.prototype.insert_markdown_cell_before = function (index) {
345 Notebook.prototype.insert_markdown_cell_before = function (index) {
346 // TODO: Bounds check for i
346 // TODO: Bounds check for i
347 var i = this.index_or_selected(index);
347 var i = this.index_or_selected(index);
348 var cell = new IPython.MarkdownCell(this);
348 var cell = new IPython.MarkdownCell(this);
349 cell.config_mathjax();
349 cell.config_mathjax();
350 this.insert_cell_before(cell, i);
350 this.insert_cell_before(cell, i);
351 this.select(this.find_cell_index(cell));
351 this.select(this.find_cell_index(cell));
352 return cell;
352 return cell;
353 }
353 }
354
354
355
355
356 Notebook.prototype.insert_markdown_cell_after = function (index) {
356 Notebook.prototype.insert_markdown_cell_after = function (index) {
357 // TODO: Bounds check for i
357 // TODO: Bounds check for i
358 var i = this.index_or_selected(index);
358 var i = this.index_or_selected(index);
359 var cell = new IPython.MarkdownCell(this);
359 var cell = new IPython.MarkdownCell(this);
360 cell.config_mathjax();
360 cell.config_mathjax();
361 this.insert_cell_after(cell, i);
361 this.insert_cell_after(cell, i);
362 this.select(this.find_cell_index(cell));
362 this.select(this.find_cell_index(cell));
363 return cell;
363 return cell;
364 }
364 }
365
365
366
366
367 Notebook.prototype.to_code = function (index) {
367 Notebook.prototype.to_code = function (index) {
368 // TODO: Bounds check for i
368 // TODO: Bounds check for i
369 var i = this.index_or_selected(index);
369 var i = this.index_or_selected(index);
370 var source_element = this.cell_elements().eq(i);
370 var source_element = this.cell_elements().eq(i);
371 var source_cell = source_element.data("cell");
371 var source_cell = source_element.data("cell");
372 if (source_cell instanceof IPython.HTMLCell ||
372 if (source_cell instanceof IPython.HTMLCell ||
373 source_cell instanceof IPython.MarkdownCell) {
373 source_cell instanceof IPython.MarkdownCell) {
374 this.insert_code_cell_after(i);
374 this.insert_code_cell_after(i);
375 var target_cell = this.cells()[i+1];
375 var target_cell = this.cells()[i+1];
376 target_cell.set_code(source_cell.get_source());
376 target_cell.set_code(source_cell.get_source());
377 source_element.remove();
377 source_element.remove();
378 };
378 };
379 };
379 };
380
380
381
381
382 Notebook.prototype.to_markdown = function (index) {
382 Notebook.prototype.to_markdown = function (index) {
383 // TODO: Bounds check for i
383 // TODO: Bounds check for i
384 var i = this.index_or_selected(index);
384 var i = this.index_or_selected(index);
385 var source_element = this.cell_elements().eq(i);
385 var source_element = this.cell_elements().eq(i);
386 var source_cell = source_element.data("cell");
386 var source_cell = source_element.data("cell");
387 var target_cell = null;
387 var target_cell = null;
388 if (source_cell instanceof IPython.CodeCell) {
388 if (source_cell instanceof IPython.CodeCell) {
389 this.insert_markdown_cell_after(i);
389 this.insert_markdown_cell_after(i);
390 var target_cell = this.cells()[i+1];
390 var target_cell = this.cells()[i+1];
391 var text = source_cell.get_code();
391 var text = source_cell.get_code();
392 } else if (source_cell instanceof IPython.HTMLCell) {
392 } else if (source_cell instanceof IPython.HTMLCell) {
393 this.insert_markdown_cell_after(i);
393 this.insert_markdown_cell_after(i);
394 var target_cell = this.cells()[i+1];
394 var target_cell = this.cells()[i+1];
395 var text = source_cell.get_source();
395 var text = source_cell.get_source();
396 if (text === source_cell.placeholder) {
396 if (text === source_cell.placeholder) {
397 text = target_cell.placeholder;
397 text = target_cell.placeholder;
398 }
398 }
399 }
399 }
400 if (target_cell !== null) {
400 if (target_cell !== null) {
401 if (text === "") {text = target_cell.placeholder;};
401 if (text === "") {text = target_cell.placeholder;};
402 target_cell.set_source(text);
402 target_cell.set_source(text);
403 source_element.remove();
403 source_element.remove();
404 target_cell.edit();
404 target_cell.edit();
405 }
405 }
406 };
406 };
407
407
408
408
409 Notebook.prototype.to_html = function (index) {
409 Notebook.prototype.to_html = function (index) {
410 // TODO: Bounds check for i
410 // TODO: Bounds check for i
411 var i = this.index_or_selected(index);
411 var i = this.index_or_selected(index);
412 var source_element = this.cell_elements().eq(i);
412 var source_element = this.cell_elements().eq(i);
413 var source_cell = source_element.data("cell");
413 var source_cell = source_element.data("cell");
414 var target_cell = null;
414 var target_cell = null;
415 if (source_cell instanceof IPython.CodeCell) {
415 if (source_cell instanceof IPython.CodeCell) {
416 this.insert_html_cell_after(i);
416 this.insert_html_cell_after(i);
417 var target_cell = this.cells()[i+1];
417 var target_cell = this.cells()[i+1];
418 var text = source_cell.get_code();
418 var text = source_cell.get_code();
419 } else if (source_cell instanceof IPython.MarkdownCell) {
419 } else if (source_cell instanceof IPython.MarkdownCell) {
420 this.insert_html_cell_after(i);
420 this.insert_html_cell_after(i);
421 var target_cell = this.cells()[i+1];
421 var target_cell = this.cells()[i+1];
422 var text = source_cell.get_source();
422 var text = source_cell.get_source();
423 if (text === source_cell.placeholder) {
423 if (text === source_cell.placeholder) {
424 text = target_cell.placeholder;
424 text = target_cell.placeholder;
425 }
425 }
426 }
426 }
427 if (target_cell !== null) {
427 if (target_cell !== null) {
428 if (text === "") {text = target_cell.placeholder;};
428 if (text === "") {text = target_cell.placeholder;};
429 target_cell.set_source(text);
429 target_cell.set_source(text);
430 source_element.remove();
430 source_element.remove();
431 target_cell.edit();
431 target_cell.edit();
432 }
432 }
433 };
433 };
434
434
435
435
436 // Cell collapsing
436 // Cell collapsing
437
437
438 Notebook.prototype.collapse = function (index) {
438 Notebook.prototype.collapse = function (index) {
439 var i = this.index_or_selected(index);
439 var i = this.index_or_selected(index);
440 this.cells()[i].collapse();
440 this.cells()[i].collapse();
441 };
441 };
442
442
443
443
444 Notebook.prototype.expand = function (index) {
444 Notebook.prototype.expand = function (index) {
445 var i = this.index_or_selected(index);
445 var i = this.index_or_selected(index);
446 this.cells()[i].expand();
446 this.cells()[i].expand();
447 };
447 };
448
448
449
449
450 Notebook.prototype.set_autoindent = function (state) {
450 Notebook.prototype.set_autoindent = function (state) {
451 var cells = this.cells();
451 var cells = this.cells();
452 len = cells.length;
452 len = cells.length;
453 for (var i=0; i<len; i++) {
453 for (var i=0; i<len; i++) {
454 cells[i].set_autoindent(state)
454 cells[i].set_autoindent(state)
455 };
455 };
456 };
456 };
457
457
458 // Kernel related things
458 // Kernel related things
459
459
460 Notebook.prototype.start_kernel = function () {
460 Notebook.prototype.start_kernel = function () {
461 this.kernel = new IPython.Kernel();
461 this.kernel = new IPython.Kernel();
462 var notebook_id = IPython.save_widget.get_notebook_id();
462 var notebook_id = IPython.save_widget.get_notebook_id();
463 this.kernel.start_kernel(notebook_id, $.proxy(this.kernel_started, this));
463 this.kernel.start_kernel(notebook_id, $.proxy(this.kernel_started, this));
464 };
464 };
465
465
466
466
467 Notebook.prototype.handle_shell_reply = function (e) {
467 Notebook.prototype.handle_shell_reply = function (e) {
468 reply = $.parseJSON(e.data);
468 reply = $.parseJSON(e.data);
469 var header = reply.header;
469 var header = reply.header;
470 var content = reply.content;
470 var content = reply.content;
471 var msg_type = header.msg_type;
471 var msg_type = header.msg_type;
472 // console.log(reply);
472 // console.log(reply);
473 var cell = this.cell_for_msg(reply.parent_header.msg_id);
473 var cell = this.cell_for_msg(reply.parent_header.msg_id);
474 if (msg_type === "execute_reply") {
474 if (msg_type === "execute_reply") {
475 cell.set_input_prompt(content.execution_count);
475 cell.set_input_prompt(content.execution_count);
476 } else if (msg_type === "complete_reply") {
476 } else if (msg_type === "complete_reply") {
477 cell.finish_completing(content.matched_text, content.matches);
477 cell.finish_completing(content.matched_text, content.matches);
478 };
478 };
479 var payload = content.payload || [];
479 var payload = content.payload || [];
480 this.handle_payload(payload);
480 this.handle_payload(payload);
481 };
481 };
482
482
483
483
484 Notebook.prototype.handle_payload = function (payload) {
484 Notebook.prototype.handle_payload = function (payload) {
485 var l = payload.length;
485 var l = payload.length;
486 if (l > 0) {
486 if (l > 0) {
487 IPython.pager.clear();
487 IPython.pager.clear();
488 IPython.pager.expand();
488 IPython.pager.expand();
489 };
489 };
490 for (var i=0; i<l; i++) {
490 for (var i=0; i<l; i++) {
491 IPython.pager.append_text(payload[i].text);
491 IPython.pager.append_text(payload[i].text);
492 };
492 };
493 };
493 };
494
494
495
495
496 Notebook.prototype.handle_iopub_reply = function (e) {
496 Notebook.prototype.handle_iopub_reply = function (e) {
497 reply = $.parseJSON(e.data);
497 reply = $.parseJSON(e.data);
498 var content = reply.content;
498 var content = reply.content;
499 // console.log(reply);
499 // console.log(reply);
500 var msg_type = reply.header.msg_type;
500 var msg_type = reply.header.msg_type;
501 var cell = this.cell_for_msg(reply.parent_header.msg_id);
501 var cell = this.cell_for_msg(reply.parent_header.msg_id);
502 var output_types = ['stream','display_data','pyout','pyerr'];
502 var output_types = ['stream','display_data','pyout','pyerr'];
503 if (output_types.indexOf(msg_type) >= 0) {
503 if (output_types.indexOf(msg_type) >= 0) {
504 this.handle_output(cell, msg_type, content);
504 this.handle_output(cell, msg_type, content);
505 } else if (msg_type === "status") {
505 } else if (msg_type === "status") {
506 if (content.execution_state === "busy") {
506 if (content.execution_state === "busy") {
507 IPython.kernel_status_widget.status_busy();
507 IPython.kernel_status_widget.status_busy();
508 } else if (content.execution_state === "idle") {
508 } else if (content.execution_state === "idle") {
509 IPython.kernel_status_widget.status_idle();
509 IPython.kernel_status_widget.status_idle();
510 };
510 };
511 }
511 }
512 };
512 };
513
513
514
514
515 Notebook.prototype.handle_output = function (cell, msg_type, content) {
515 Notebook.prototype.handle_output = function (cell, msg_type, content) {
516 var json = {};
516 var json = {};
517 json.output_type = msg_type;
517 json.output_type = msg_type;
518 if (msg_type === "stream") {
518 if (msg_type === "stream") {
519 json.text = content.data + '\n';
519 json.text = content.data + '\n';
520 } else if (msg_type === "display_data") {
520 } else if (msg_type === "display_data") {
521 json = this.convert_mime_types(json, content.data);
521 json = this.convert_mime_types(json, content.data);
522 } else if (msg_type === "pyout") {
522 } else if (msg_type === "pyout") {
523 json.prompt_number = content.execution_count;
523 json.prompt_number = content.execution_count;
524 json = this.convert_mime_types(json, content.data);
524 json = this.convert_mime_types(json, content.data);
525 } else if (msg_type === "pyerr") {
525 } else if (msg_type === "pyerr") {
526 json.ename = content.ename;
526 json.ename = content.ename;
527 json.evalue = content.evalue;
527 json.evalue = content.evalue;
528 json.traceback = content.traceback;
528 json.traceback = content.traceback;
529 };
529 };
530 cell.append_output(json);
530 cell.append_output(json);
531 };
531 };
532
532
533
533
534 Notebook.prototype.convert_mime_types = function (json, data) {
534 Notebook.prototype.convert_mime_types = function (json, data) {
535 if (data['text/plain'] !== undefined) {
535 if (data['text/plain'] !== undefined) {
536 json.text = data['text/plain'];
536 json.text = data['text/plain'];
537 };
537 };
538 if (data['text/html'] !== undefined) {
538 if (data['text/html'] !== undefined) {
539 json.html = data['text/html'];
539 json.html = data['text/html'];
540 };
540 };
541 if (data['image/svg+xml'] !== undefined) {
541 if (data['image/svg+xml'] !== undefined) {
542 json.svg = data['image/svg+xml'];
542 json.svg = data['image/svg+xml'];
543 };
543 };
544 if (data['image/png'] !== undefined) {
544 if (data['image/png'] !== undefined) {
545 json.png = data['image/png'];
545 json.png = data['image/png'];
546 };
546 };
547 if (data['image/jpeg'] !== undefined) {
548 json.jpeg = data['image/jpeg'];
549 };
547 if (data['text/latex'] !== undefined) {
550 if (data['text/latex'] !== undefined) {
548 json.latex = data['text/latex'];
551 json.latex = data['text/latex'];
549 };
552 };
550 if (data['application/json'] !== undefined) {
553 if (data['application/json'] !== undefined) {
551 json.json = data['application/json'];
554 json.json = data['application/json'];
552 };
555 };
553 if (data['application/javascript'] !== undefined) {
556 if (data['application/javascript'] !== undefined) {
554 json.javascript = data['application/javascript'];
557 json.javascript = data['application/javascript'];
555 }
558 }
556 return json;
559 return json;
557 };
560 };
558
561
559 Notebook.prototype.kernel_started = function () {
562 Notebook.prototype.kernel_started = function () {
560 console.log("Kernel started: ", this.kernel.kernel_id);
563 console.log("Kernel started: ", this.kernel.kernel_id);
561 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
564 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
562 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
565 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
563 };
566 };
564
567
565
568
566 Notebook.prototype.execute_selected_cell = function (options) {
569 Notebook.prototype.execute_selected_cell = function (options) {
567 // add_new: should a new cell be added if we are at the end of the nb
570 // add_new: should a new cell be added if we are at the end of the nb
568 // terminal: execute in terminal mode, which stays in the current cell
571 // terminal: execute in terminal mode, which stays in the current cell
569 default_options = {terminal: false, add_new: true}
572 default_options = {terminal: false, add_new: true}
570 $.extend(default_options, options)
573 $.extend(default_options, options)
571 var that = this;
574 var that = this;
572 var cell = that.selected_cell();
575 var cell = that.selected_cell();
573 var cell_index = that.find_cell_index(cell);
576 var cell_index = that.find_cell_index(cell);
574 if (cell instanceof IPython.CodeCell) {
577 if (cell instanceof IPython.CodeCell) {
575 cell.clear_output();
578 cell.clear_output();
576 var code = cell.get_code();
579 var code = cell.get_code();
577 var msg_id = that.kernel.execute(cell.get_code());
580 var msg_id = that.kernel.execute(cell.get_code());
578 that.msg_cell_map[msg_id] = cell.cell_id;
581 that.msg_cell_map[msg_id] = cell.cell_id;
579 } else if (cell instanceof IPython.HTMLCell) {
582 } else if (cell instanceof IPython.HTMLCell) {
580 cell.render();
583 cell.render();
581 }
584 }
582 if (default_options.terminal) {
585 if (default_options.terminal) {
583 cell.clear_input();
586 cell.clear_input();
584 } else {
587 } else {
585 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
588 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
586 that.insert_code_cell_after();
589 that.insert_code_cell_after();
587 // If we are adding a new cell at the end, scroll down to show it.
590 // If we are adding a new cell at the end, scroll down to show it.
588 that.scroll_to_bottom();
591 that.scroll_to_bottom();
589 } else {
592 } else {
590 that.select(cell_index+1);
593 that.select(cell_index+1);
591 };
594 };
592 };
595 };
593 };
596 };
594
597
595
598
596 Notebook.prototype.execute_all_cells = function () {
599 Notebook.prototype.execute_all_cells = function () {
597 var ncells = this.ncells();
600 var ncells = this.ncells();
598 for (var i=0; i<ncells; i++) {
601 for (var i=0; i<ncells; i++) {
599 this.select(i);
602 this.select(i);
600 this.execute_selected_cell({add_new:false});
603 this.execute_selected_cell({add_new:false});
601 };
604 };
602 this.scroll_to_bottom();
605 this.scroll_to_bottom();
603 };
606 };
604
607
605
608
606 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
609 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
607 var msg_id = this.kernel.complete(line, cursor_pos);
610 var msg_id = this.kernel.complete(line, cursor_pos);
608 this.msg_cell_map[msg_id] = cell.cell_id;
611 this.msg_cell_map[msg_id] = cell.cell_id;
609 };
612 };
610
613
611 // Persistance and loading
614 // Persistance and loading
612
615
613
616
614 Notebook.prototype.fromJSON = function (data) {
617 Notebook.prototype.fromJSON = function (data) {
615 var ncells = this.ncells();
618 var ncells = this.ncells();
616 for (var i=0; i<ncells; i++) {
619 for (var i=0; i<ncells; i++) {
617 // Always delete cell 0 as they get renumbered as they are deleted.
620 // Always delete cell 0 as they get renumbered as they are deleted.
618 this.delete_cell(0);
621 this.delete_cell(0);
619 };
622 };
620 // Only handle 1 worksheet for now.
623 // Only handle 1 worksheet for now.
621 var worksheet = data.worksheets[0];
624 var worksheet = data.worksheets[0];
622 if (worksheet !== undefined) {
625 if (worksheet !== undefined) {
623 var new_cells = worksheet.cells;
626 var new_cells = worksheet.cells;
624 ncells = new_cells.length;
627 ncells = new_cells.length;
625 var cell_data = null;
628 var cell_data = null;
626 var new_cell = null;
629 var new_cell = null;
627 for (var i=0; i<ncells; i++) {
630 for (var i=0; i<ncells; i++) {
628 cell_data = new_cells[i];
631 cell_data = new_cells[i];
629 if (cell_data.cell_type == 'code') {
632 if (cell_data.cell_type == 'code') {
630 new_cell = this.insert_code_cell_after();
633 new_cell = this.insert_code_cell_after();
631 new_cell.fromJSON(cell_data);
634 new_cell.fromJSON(cell_data);
632 } else if (cell_data.cell_type === 'html') {
635 } else if (cell_data.cell_type === 'html') {
633 new_cell = this.insert_html_cell_after();
636 new_cell = this.insert_html_cell_after();
634 new_cell.fromJSON(cell_data);
637 new_cell.fromJSON(cell_data);
635 } else if (cell_data.cell_type === 'markdown') {
638 } else if (cell_data.cell_type === 'markdown') {
636 new_cell = this.insert_markdown_cell_after();
639 new_cell = this.insert_markdown_cell_after();
637 new_cell.fromJSON(cell_data);
640 new_cell.fromJSON(cell_data);
638 };
641 };
639 };
642 };
640 };
643 };
641 };
644 };
642
645
643
646
644 Notebook.prototype.toJSON = function () {
647 Notebook.prototype.toJSON = function () {
645 var cells = this.cells();
648 var cells = this.cells();
646 var ncells = cells.length;
649 var ncells = cells.length;
647 cell_array = new Array(ncells);
650 cell_array = new Array(ncells);
648 for (var i=0; i<ncells; i++) {
651 for (var i=0; i<ncells; i++) {
649 cell_array[i] = cells[i].toJSON();
652 cell_array[i] = cells[i].toJSON();
650 };
653 };
651 data = {
654 data = {
652 // Only handle 1 worksheet for now.
655 // Only handle 1 worksheet for now.
653 worksheets : [{cells:cell_array}]
656 worksheets : [{cells:cell_array}]
654 }
657 }
655 return data
658 return data
656 };
659 };
657
660
658 Notebook.prototype.save_notebook = function () {
661 Notebook.prototype.save_notebook = function () {
659 if (IPython.save_widget.test_notebook_name()) {
662 if (IPython.save_widget.test_notebook_name()) {
660 var notebook_id = IPython.save_widget.get_notebook_id();
663 var notebook_id = IPython.save_widget.get_notebook_id();
661 var nbname = IPython.save_widget.get_notebook_name();
664 var nbname = IPython.save_widget.get_notebook_name();
662 // We may want to move the name/id/nbformat logic inside toJSON?
665 // We may want to move the name/id/nbformat logic inside toJSON?
663 var data = this.toJSON();
666 var data = this.toJSON();
664 data.name = nbname;
667 data.name = nbname;
665 data.nbformat = 2;
668 data.nbformat = 2;
666 data.id = notebook_id
669 data.id = notebook_id
667 // We do the call with settings so we can set cache to false.
670 // We do the call with settings so we can set cache to false.
668 var settings = {
671 var settings = {
669 processData : false,
672 processData : false,
670 cache : false,
673 cache : false,
671 type : "PUT",
674 type : "PUT",
672 data : JSON.stringify(data),
675 data : JSON.stringify(data),
673 headers : {'Content-Type': 'application/json'},
676 headers : {'Content-Type': 'application/json'},
674 success : $.proxy(this.notebook_saved,this)
677 success : $.proxy(this.notebook_saved,this)
675 };
678 };
676 IPython.save_widget.status_saving();
679 IPython.save_widget.status_saving();
677 $.ajax("/notebooks/" + notebook_id, settings);
680 $.ajax("/notebooks/" + notebook_id, settings);
678 };
681 };
679 };
682 };
680
683
681
684
682 Notebook.prototype.notebook_saved = function (data, status, xhr) {
685 Notebook.prototype.notebook_saved = function (data, status, xhr) {
683 IPython.save_widget.status_save();
686 IPython.save_widget.status_save();
684 }
687 }
685
688
686
689
687 Notebook.prototype.load_notebook = function (callback) {
690 Notebook.prototype.load_notebook = function (callback) {
688 var that = this;
691 var that = this;
689 var notebook_id = IPython.save_widget.get_notebook_id();
692 var notebook_id = IPython.save_widget.get_notebook_id();
690 // We do the call with settings so we can set cache to false.
693 // We do the call with settings so we can set cache to false.
691 var settings = {
694 var settings = {
692 processData : false,
695 processData : false,
693 cache : false,
696 cache : false,
694 type : "GET",
697 type : "GET",
695 dataType : "json",
698 dataType : "json",
696 success : function (data, status, xhr) {
699 success : function (data, status, xhr) {
697 that.notebook_loaded(data, status, xhr);
700 that.notebook_loaded(data, status, xhr);
698 if (callback !== undefined) {
701 if (callback !== undefined) {
699 callback();
702 callback();
700 };
703 };
701 }
704 }
702 };
705 };
703 IPython.save_widget.status_loading();
706 IPython.save_widget.status_loading();
704 $.ajax("/notebooks/" + notebook_id, settings);
707 $.ajax("/notebooks/" + notebook_id, settings);
705 }
708 }
706
709
707
710
708 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
711 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
709 this.fromJSON(data);
712 this.fromJSON(data);
710 if (this.ncells() === 0) {
713 if (this.ncells() === 0) {
711 this.insert_code_cell_after();
714 this.insert_code_cell_after();
712 };
715 };
713 IPython.save_widget.status_save();
716 IPython.save_widget.status_save();
714 IPython.save_widget.set_notebook_name(data.name);
717 IPython.save_widget.set_notebook_name(data.name);
715 this.start_kernel();
718 this.start_kernel();
716 // fromJSON always selects the last cell inserted. We need to wait
719 // fromJSON always selects the last cell inserted. We need to wait
717 // until that is done before scrolling to the top.
720 // until that is done before scrolling to the top.
718 setTimeout(function () {
721 setTimeout(function () {
719 IPython.notebook.select(0);
722 IPython.notebook.select(0);
720 IPython.notebook.scroll_to_top();
723 IPython.notebook.scroll_to_top();
721 }, 50);
724 }, 50);
722 };
725 };
723
726
724 IPython.Notebook = Notebook;
727 IPython.Notebook = Notebook;
725
728
726 return IPython;
729 return IPython;
727
730
728 }(IPython));
731 }(IPython));
729
732
@@ -1,106 +1,108 b''
1 """The basic dict based notebook format."""
1 """The basic dict based notebook format."""
2
2
3 import pprint
3 import pprint
4 import uuid
4 import uuid
5
5
6 from IPython.utils.ipstruct import Struct
6 from IPython.utils.ipstruct import Struct
7
7
8
8
9 class NotebookNode(Struct):
9 class NotebookNode(Struct):
10 pass
10 pass
11
11
12
12
13 def from_dict(d):
13 def from_dict(d):
14 if isinstance(d, dict):
14 if isinstance(d, dict):
15 newd = NotebookNode()
15 newd = NotebookNode()
16 for k,v in d.items():
16 for k,v in d.items():
17 newd[k] = from_dict(v)
17 newd[k] = from_dict(v)
18 return newd
18 return newd
19 elif isinstance(d, (tuple, list)):
19 elif isinstance(d, (tuple, list)):
20 return [from_dict(i) for i in d]
20 return [from_dict(i) for i in d]
21 else:
21 else:
22 return d
22 return d
23
23
24
24
25 def new_output(output_type=None, output_text=None, output_png=None,
25 def new_output(output_type=None, output_text=None, output_png=None,
26 output_html=None, output_svg=None, output_latex=None, output_json=None,
26 output_html=None, output_svg=None, output_latex=None, output_json=None,
27 output_javascript=None, prompt_number=None):
27 output_javascript=None, output_jpeg=None, prompt_number=None):
28 """Create a new code cell with input and output"""
28 """Create a new code cell with input and output"""
29 output = NotebookNode()
29 output = NotebookNode()
30 if output_type is not None:
30 if output_type is not None:
31 output.output_type = unicode(output_type)
31 output.output_type = unicode(output_type)
32 if output_text is not None:
32 if output_text is not None:
33 output.text = unicode(output_text)
33 output.text = unicode(output_text)
34 if output_png is not None:
34 if output_png is not None:
35 output.png = bytes(output_png)
35 output.png = bytes(output_png)
36 if output_jpeg is not None:
37 output.jpeg = bytes(output_jpeg)
36 if output_html is not None:
38 if output_html is not None:
37 output.html = unicode(output_html)
39 output.html = unicode(output_html)
38 if output_svg is not None:
40 if output_svg is not None:
39 output.svg = unicode(output_svg)
41 output.svg = unicode(output_svg)
40 if output_latex is not None:
42 if output_latex is not None:
41 output.latex = unicode(output_latex)
43 output.latex = unicode(output_latex)
42 if output_json is not None:
44 if output_json is not None:
43 output.json = unicode(output_json)
45 output.json = unicode(output_json)
44 if output_javascript is not None:
46 if output_javascript is not None:
45 output.javascript = unicode(output_javascript)
47 output.javascript = unicode(output_javascript)
46 if prompt_number is not None:
48 if prompt_number is not None:
47 output.prompt_number = int(prompt_number)
49 output.prompt_number = int(prompt_number)
48 return output
50 return output
49
51
50
52
51 def new_code_cell(input=None, prompt_number=None, outputs=None, language=u'python'):
53 def new_code_cell(input=None, prompt_number=None, outputs=None, language=u'python'):
52 """Create a new code cell with input and output"""
54 """Create a new code cell with input and output"""
53 cell = NotebookNode()
55 cell = NotebookNode()
54 cell.cell_type = u'code'
56 cell.cell_type = u'code'
55 if language is not None:
57 if language is not None:
56 cell.language = unicode(language)
58 cell.language = unicode(language)
57 if input is not None:
59 if input is not None:
58 cell.input = unicode(input)
60 cell.input = unicode(input)
59 if prompt_number is not None:
61 if prompt_number is not None:
60 cell.prompt_number = int(prompt_number)
62 cell.prompt_number = int(prompt_number)
61 if outputs is None:
63 if outputs is None:
62 cell.outputs = []
64 cell.outputs = []
63 else:
65 else:
64 cell.outputs = outputs
66 cell.outputs = outputs
65
67
66 return cell
68 return cell
67
69
68 def new_text_cell(cell_type, source=None, rendered=None):
70 def new_text_cell(cell_type, source=None, rendered=None):
69 """Create a new text cell."""
71 """Create a new text cell."""
70 cell = NotebookNode()
72 cell = NotebookNode()
71 if source is not None:
73 if source is not None:
72 cell.source = unicode(source)
74 cell.source = unicode(source)
73 if rendered is not None:
75 if rendered is not None:
74 cell.rendered = unicode(rendered)
76 cell.rendered = unicode(rendered)
75 cell.cell_type = cell_type
77 cell.cell_type = cell_type
76 return cell
78 return cell
77
79
78
80
79 def new_worksheet(name=None, cells=None):
81 def new_worksheet(name=None, cells=None):
80 """Create a worksheet by name with with a list of cells."""
82 """Create a worksheet by name with with a list of cells."""
81 ws = NotebookNode()
83 ws = NotebookNode()
82 if name is not None:
84 if name is not None:
83 ws.name = unicode(name)
85 ws.name = unicode(name)
84 if cells is None:
86 if cells is None:
85 ws.cells = []
87 ws.cells = []
86 else:
88 else:
87 ws.cells = list(cells)
89 ws.cells = list(cells)
88 return ws
90 return ws
89
91
90
92
91 def new_notebook(name=None, id=None, worksheets=None):
93 def new_notebook(name=None, id=None, worksheets=None):
92 """Create a notebook by name, id and a list of worksheets."""
94 """Create a notebook by name, id and a list of worksheets."""
93 nb = NotebookNode()
95 nb = NotebookNode()
94 nb.nbformat = 2
96 nb.nbformat = 2
95 if name is not None:
97 if name is not None:
96 nb.name = unicode(name)
98 nb.name = unicode(name)
97 if id is None:
99 if id is None:
98 nb.id = unicode(uuid.uuid4())
100 nb.id = unicode(uuid.uuid4())
99 else:
101 else:
100 nb.id = unicode(id)
102 nb.id = unicode(id)
101 if worksheets is None:
103 if worksheets is None:
102 nb.worksheets = []
104 nb.worksheets = []
103 else:
105 else:
104 nb.worksheets = list(worksheets)
106 nb.worksheets = list(worksheets)
105 return nb
107 return nb
106
108
@@ -1,178 +1,180 b''
1 """Read and write notebook files as XML."""
1 """Read and write notebook files as XML."""
2
2
3 from base64 import encodestring, decodestring
3 from base64 import encodestring, decodestring
4 from xml.etree import ElementTree as ET
4 from xml.etree import ElementTree as ET
5
5
6 from .rwbase import NotebookReader, NotebookWriter
6 from .rwbase import NotebookReader, NotebookWriter
7 from .nbbase import (
7 from .nbbase import (
8 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
8 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
9 )
9 )
10
10
11 def indent(elem, level=0):
11 def indent(elem, level=0):
12 i = "\n" + level*" "
12 i = "\n" + level*" "
13 if len(elem):
13 if len(elem):
14 if not elem.text or not elem.text.strip():
14 if not elem.text or not elem.text.strip():
15 elem.text = i + " "
15 elem.text = i + " "
16 if not elem.tail or not elem.tail.strip():
16 if not elem.tail or not elem.tail.strip():
17 elem.tail = i
17 elem.tail = i
18 for elem in elem:
18 for elem in elem:
19 indent(elem, level+1)
19 indent(elem, level+1)
20 if not elem.tail or not elem.tail.strip():
20 if not elem.tail or not elem.tail.strip():
21 elem.tail = i
21 elem.tail = i
22 else:
22 else:
23 if level and (not elem.tail or not elem.tail.strip()):
23 if level and (not elem.tail or not elem.tail.strip()):
24 elem.tail = i
24 elem.tail = i
25
25
26
26
27 def _get_text(e, tag):
27 def _get_text(e, tag):
28 sub_e = e.find(tag)
28 sub_e = e.find(tag)
29 if sub_e is None:
29 if sub_e is None:
30 return None
30 return None
31 else:
31 else:
32 return sub_e.text
32 return sub_e.text
33
33
34
34
35 def _set_text(nbnode, attr, parent, tag):
35 def _set_text(nbnode, attr, parent, tag):
36 if attr in nbnode:
36 if attr in nbnode:
37 e = ET.SubElement(parent, tag)
37 e = ET.SubElement(parent, tag)
38 e.text = nbnode[attr]
38 e.text = nbnode[attr]
39
39
40
40
41 def _get_int(e, tag):
41 def _get_int(e, tag):
42 sub_e = e.find(tag)
42 sub_e = e.find(tag)
43 if sub_e is None:
43 if sub_e is None:
44 return None
44 return None
45 else:
45 else:
46 return int(sub_e.text)
46 return int(sub_e.text)
47
47
48
48
49 def _set_int(nbnode, attr, parent, tag):
49 def _set_int(nbnode, attr, parent, tag):
50 if attr in nbnode:
50 if attr in nbnode:
51 e = ET.SubElement(parent, tag)
51 e = ET.SubElement(parent, tag)
52 e.text = unicode(nbnode[attr])
52 e.text = unicode(nbnode[attr])
53
53
54
54
55 def _get_binary(e, tag):
55 def _get_binary(e, tag):
56 sub_e = e.find(tag)
56 sub_e = e.find(tag)
57 if sub_e is None:
57 if sub_e is None:
58 return None
58 return None
59 else:
59 else:
60 return decodestring(sub_e.text)
60 return decodestring(sub_e.text)
61
61
62
62
63 def _set_binary(nbnode, attr, parent, tag):
63 def _set_binary(nbnode, attr, parent, tag):
64 if attr in nbnode:
64 if attr in nbnode:
65 e = ET.SubElement(parent, tag)
65 e = ET.SubElement(parent, tag)
66 e.text = encodestring(nbnode[attr])
66 e.text = encodestring(nbnode[attr])
67
67
68
68
69 class XMLReader(NotebookReader):
69 class XMLReader(NotebookReader):
70
70
71 def reads(self, s, **kwargs):
71 def reads(self, s, **kwargs):
72 root = ET.fromstring(s)
72 root = ET.fromstring(s)
73 return self.to_notebook(root, **kwargs)
73 return self.to_notebook(root, **kwargs)
74
74
75 def to_notebook(self, root, **kwargs):
75 def to_notebook(self, root, **kwargs):
76 nbname = _get_text(root,'name')
76 nbname = _get_text(root,'name')
77 nbid = _get_text(root,'id')
77 nbid = _get_text(root,'id')
78
78
79 worksheets = []
79 worksheets = []
80 for ws_e in root.find('worksheets').getiterator('worksheet'):
80 for ws_e in root.find('worksheets').getiterator('worksheet'):
81 wsname = _get_text(ws_e,'name')
81 wsname = _get_text(ws_e,'name')
82 cells = []
82 cells = []
83 for cell_e in ws_e.find('cells').getiterator():
83 for cell_e in ws_e.find('cells').getiterator():
84 if cell_e.tag == 'codecell':
84 if cell_e.tag == 'codecell':
85 input = _get_text(cell_e,'input')
85 input = _get_text(cell_e,'input')
86 prompt_number = _get_int(cell_e,'prompt_number')
86 prompt_number = _get_int(cell_e,'prompt_number')
87 language = _get_text(cell_e,'language')
87 language = _get_text(cell_e,'language')
88 outputs = []
88 outputs = []
89 for output_e in cell_e.find('outputs').getiterator('output'):
89 for output_e in cell_e.find('outputs').getiterator('output'):
90 out_prompt_number = _get_int(output_e,'prompt_number')
90 out_prompt_number = _get_int(output_e,'prompt_number')
91 output_type = _get_text(output_e,'output_type')
91 output_type = _get_text(output_e,'output_type')
92 output_text = _get_text(output_e,'text')
92 output_text = _get_text(output_e,'text')
93 output_png = _get_binary(output_e,'png')
93 output_png = _get_binary(output_e,'png')
94 output_jpeg = _get_binary(output_e,'jpeg')
94 output_svg = _get_text(output_e,'svg')
95 output_svg = _get_text(output_e,'svg')
95 output_html = _get_text(output_e,'html')
96 output_html = _get_text(output_e,'html')
96 output_latex = _get_text(output_e,'latex')
97 output_latex = _get_text(output_e,'latex')
97 output_json = _get_text(output_e,'json')
98 output_json = _get_text(output_e,'json')
98 output_javascript = _get_text(output_e,'javascript')
99 output_javascript = _get_text(output_e,'javascript')
99 output = new_output(output_type=output_type,output_png=output_png,
100 output = new_output(output_type=output_type,output_png=output_png,
100 output_text=output_text,output_svg=output_svg,
101 output_text=output_text, output_svg=output_svg,
101 output_html=output_html,output_latex=output_latex,
102 output_html=output_html, output_latex=output_latex,
102 output_json=output_json,output_javascript=output_javascript,
103 output_json=output_json, output_javascript=output_javascript,
103 prompt_number=out_prompt_number
104 output_jpeg=output_jpeg, prompt_number=out_prompt_number
104 )
105 )
105 outputs.append(output)
106 outputs.append(output)
106 cc = new_code_cell(input=input,prompt_number=prompt_number,
107 cc = new_code_cell(input=input,prompt_number=prompt_number,
107 language=language,outputs=outputs)
108 language=language,outputs=outputs)
108 cells.append(cc)
109 cells.append(cc)
109 if cell_e.tag == 'htmlcell':
110 if cell_e.tag == 'htmlcell':
110 source = _get_text(cell_e,'source')
111 source = _get_text(cell_e,'source')
111 rendered = _get_text(cell_e,'rendered')
112 rendered = _get_text(cell_e,'rendered')
112 cells.append(new_text_cell(u'html', source=source, rendered=rendered))
113 cells.append(new_text_cell(u'html', source=source, rendered=rendered))
113 if cell_e.tag == 'markdowncell':
114 if cell_e.tag == 'markdowncell':
114 source = _get_text(cell_e,'source')
115 source = _get_text(cell_e,'source')
115 rendered = _get_text(cell_e,'rendered')
116 rendered = _get_text(cell_e,'rendered')
116 cells.append(new_text_cell(u'markdown', source=source, rendered=rendered))
117 cells.append(new_text_cell(u'markdown', source=source, rendered=rendered))
117 ws = new_worksheet(name=wsname,cells=cells)
118 ws = new_worksheet(name=wsname,cells=cells)
118 worksheets.append(ws)
119 worksheets.append(ws)
119
120
120 nb = new_notebook(name=nbname,id=nbid,worksheets=worksheets)
121 nb = new_notebook(name=nbname,id=nbid,worksheets=worksheets)
121 return nb
122 return nb
122
123
123
124
124 class XMLWriter(NotebookWriter):
125 class XMLWriter(NotebookWriter):
125
126
126 def writes(self, nb, **kwargs):
127 def writes(self, nb, **kwargs):
127 nb_e = ET.Element('notebook')
128 nb_e = ET.Element('notebook')
128 _set_text(nb,'name',nb_e,'name')
129 _set_text(nb,'name',nb_e,'name')
129 _set_text(nb,'id',nb_e,'id')
130 _set_text(nb,'id',nb_e,'id')
130 _set_int(nb,'nbformat',nb_e,'nbformat')
131 _set_int(nb,'nbformat',nb_e,'nbformat')
131 wss_e = ET.SubElement(nb_e,'worksheets')
132 wss_e = ET.SubElement(nb_e,'worksheets')
132 for ws in nb.worksheets:
133 for ws in nb.worksheets:
133 ws_e = ET.SubElement(wss_e, 'worksheet')
134 ws_e = ET.SubElement(wss_e, 'worksheet')
134 _set_text(ws,'name',ws_e,'name')
135 _set_text(ws,'name',ws_e,'name')
135 cells_e = ET.SubElement(ws_e,'cells')
136 cells_e = ET.SubElement(ws_e,'cells')
136 for cell in ws.cells:
137 for cell in ws.cells:
137 cell_type = cell.cell_type
138 cell_type = cell.cell_type
138 if cell_type == 'code':
139 if cell_type == 'code':
139 cell_e = ET.SubElement(cells_e, 'codecell')
140 cell_e = ET.SubElement(cells_e, 'codecell')
140 _set_text(cell,'input',cell_e,'input')
141 _set_text(cell,'input',cell_e,'input')
141 _set_text(cell,'language',cell_e,'language')
142 _set_text(cell,'language',cell_e,'language')
142 _set_int(cell,'prompt_number',cell_e,'prompt_number')
143 _set_int(cell,'prompt_number',cell_e,'prompt_number')
143 outputs_e = ET.SubElement(cell_e, 'outputs')
144 outputs_e = ET.SubElement(cell_e, 'outputs')
144 for output in cell.outputs:
145 for output in cell.outputs:
145 output_e = ET.SubElement(outputs_e, 'output')
146 output_e = ET.SubElement(outputs_e, 'output')
146 _set_int(output,'prompt_number',output_e,'prompt_number')
147 _set_int(output,'prompt_number',output_e,'prompt_number')
147 _set_text(output,'output_type',output_e,'output_type')
148 _set_text(output,'output_type',output_e,'output_type')
148 _set_text(output,'text',output_e,'text')
149 _set_text(output,'text',output_e,'text')
149 _set_binary(output,'png',output_e,'png')
150 _set_binary(output,'png',output_e,'png')
151 _set_binary(output,'jpeg',output_e,'jpeg')
150 _set_text(output,'html',output_e,'html')
152 _set_text(output,'html',output_e,'html')
151 _set_text(output,'svg',output_e,'svg')
153 _set_text(output,'svg',output_e,'svg')
152 _set_text(output,'latex',output_e,'latex')
154 _set_text(output,'latex',output_e,'latex')
153 _set_text(output,'json',output_e,'json')
155 _set_text(output,'json',output_e,'json')
154 _set_text(output,'javascript',output_e,'javascript')
156 _set_text(output,'javascript',output_e,'javascript')
155 elif cell_type == 'html':
157 elif cell_type == 'html':
156 cell_e = ET.SubElement(cells_e, 'htmlcell')
158 cell_e = ET.SubElement(cells_e, 'htmlcell')
157 _set_text(cell,'source',cell_e,'source')
159 _set_text(cell,'source',cell_e,'source')
158 _set_text(cell,'rendered',cell_e,'rendered')
160 _set_text(cell,'rendered',cell_e,'rendered')
159 elif cell_type == 'markdown':
161 elif cell_type == 'markdown':
160 cell_e = ET.SubElement(cells_e, 'markdowncell')
162 cell_e = ET.SubElement(cells_e, 'markdowncell')
161 _set_text(cell,'source',cell_e,'source')
163 _set_text(cell,'source',cell_e,'source')
162 _set_text(cell,'rendered',cell_e,'rendered')
164 _set_text(cell,'rendered',cell_e,'rendered')
163
165
164 indent(nb_e)
166 indent(nb_e)
165 txt = ET.tostring(nb_e, encoding="utf-8")
167 txt = ET.tostring(nb_e, encoding="utf-8")
166 txt = '<?xml version="1.0" encoding="utf-8"?>\n' + txt
168 txt = '<?xml version="1.0" encoding="utf-8"?>\n' + txt
167 return txt
169 return txt
168
170
169
171
170 _reader = XMLReader()
172 _reader = XMLReader()
171 _writer = XMLWriter()
173 _writer = XMLWriter()
172
174
173 reads = _reader.reads
175 reads = _reader.reads
174 read = _reader.read
176 read = _reader.read
175 to_notebook = _reader.to_notebook
177 to_notebook = _reader.to_notebook
176 write = _writer.write
178 write = _writer.write
177 writes = _writer.writes
179 writes = _writer.writes
178
180
@@ -1,46 +1,50 b''
1 from base64 import encodestring, decodestring
1 from base64 import encodestring, decodestring
2 import pprint
2 import pprint
3
3
4 def base64_decode(nb):
4 def base64_decode(nb):
5 """Base64 encode all bytes objects in the notebook."""
5 """Base64 encode all bytes objects in the notebook."""
6 for ws in nb.worksheets:
6 for ws in nb.worksheets:
7 for cell in ws.cells:
7 for cell in ws.cells:
8 if cell.cell_type == 'code':
8 if cell.cell_type == 'code':
9 if 'png' in cell:
9 if 'png' in cell:
10 cell.png = bytes(decodestring(cell.png))
10 cell.png = bytes(decodestring(cell.png))
11 if 'jpeg' in cell:
12 cell.jpeg = bytes(decodestring(cell.jpeg))
11 return nb
13 return nb
12
14
13
15
14 def base64_encode(nb):
16 def base64_encode(nb):
15 """Base64 decode all binary objects in the notebook."""
17 """Base64 decode all binary objects in the notebook."""
16 for ws in nb.worksheets:
18 for ws in nb.worksheets:
17 for cell in ws.cells:
19 for cell in ws.cells:
18 if cell.cell_type == 'code':
20 if cell.cell_type == 'code':
19 if 'png' in cell:
21 if 'png' in cell:
20 cell.png = unicode(encodestring(cell.png))
22 cell.png = unicode(encodestring(cell.png))
23 if 'jpeg' in cell:
24 cell.jpeg = unicode(encodestring(cell.jpeg))
21 return nb
25 return nb
22
26
23
27
24 class NotebookReader(object):
28 class NotebookReader(object):
25
29
26 def reads(self, s, **kwargs):
30 def reads(self, s, **kwargs):
27 """Read a notebook from a string."""
31 """Read a notebook from a string."""
28 raise NotImplementedError("loads must be implemented in a subclass")
32 raise NotImplementedError("loads must be implemented in a subclass")
29
33
30 def read(self, fp, **kwargs):
34 def read(self, fp, **kwargs):
31 """Read a notebook from a file like object"""
35 """Read a notebook from a file like object"""
32 return self.read(fp.read(), **kwargs)
36 return self.read(fp.read(), **kwargs)
33
37
34
38
35 class NotebookWriter(object):
39 class NotebookWriter(object):
36
40
37 def writes(self, nb, **kwargs):
41 def writes(self, nb, **kwargs):
38 """Write a notebook to a string."""
42 """Write a notebook to a string."""
39 raise NotImplementedError("loads must be implemented in a subclass")
43 raise NotImplementedError("loads must be implemented in a subclass")
40
44
41 def write(self, nb, fp, **kwargs):
45 def write(self, nb, fp, **kwargs):
42 """Write a notebook to a file like object"""
46 """Write a notebook to a file like object"""
43 return fp.write(self.writes(nb,**kwargs))
47 return fp.write(self.writes(nb,**kwargs))
44
48
45
49
46
50
@@ -1,83 +1,85 b''
1 from ..nbbase import (
1 from ..nbbase import (
2 NotebookNode,
2 NotebookNode,
3 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
3 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output
4 )
4 )
5
5
6
6
7
7
8 ws = new_worksheet(name='worksheet1')
8 ws = new_worksheet(name='worksheet1')
9
9
10 ws.cells.append(new_text_cell(
10 ws.cells.append(new_text_cell(
11 u'html',
11 u'html',
12 source='Some NumPy Examples',
12 source='Some NumPy Examples',
13 rendered='Some NumPy Examples'
13 rendered='Some NumPy Examples'
14 ))
14 ))
15
15
16
16
17 ws.cells.append(new_code_cell(
17 ws.cells.append(new_code_cell(
18 input='import numpy',
18 input='import numpy',
19 prompt_number=1
19 prompt_number=1
20 ))
20 ))
21
21
22 ws.cells.append(new_text_cell(
22 ws.cells.append(new_text_cell(
23 u'markdown',
23 u'markdown',
24 source='Some NumPy Examples',
24 source='Some NumPy Examples',
25 rendered='Some NumPy Examples'
25 rendered='Some NumPy Examples'
26 ))
26 ))
27
27
28 ws.cells.append(new_code_cell(
28 ws.cells.append(new_code_cell(
29 input='a = numpy.random.rand(100)',
29 input='a = numpy.random.rand(100)',
30 prompt_number=2
30 prompt_number=2
31 ))
31 ))
32
32
33 ws.cells.append(new_code_cell(
33 ws.cells.append(new_code_cell(
34 input='print a',
34 input='print a',
35 prompt_number=3,
35 prompt_number=3,
36 outputs=[new_output(
36 outputs=[new_output(
37 output_type=u'pyout',
37 output_type=u'pyout',
38 output_text=u'<array a>',
38 output_text=u'<array a>',
39 output_html=u'The HTML rep',
39 output_html=u'The HTML rep',
40 output_latex=u'$a$',
40 output_latex=u'$a$',
41 output_png=b'data',
41 output_png=b'data',
42 output_jpeg=b'data',
42 output_svg=u'<svg>',
43 output_svg=u'<svg>',
43 output_json=u'json data',
44 output_json=u'json data',
44 output_javascript=u'var i=0;',
45 output_javascript=u'var i=0;',
45 prompt_number=3
46 prompt_number=3
46 ),new_output(
47 ),new_output(
47 output_type=u'display_data',
48 output_type=u'display_data',
48 output_text=u'<array a>',
49 output_text=u'<array a>',
49 output_html=u'The HTML rep',
50 output_html=u'The HTML rep',
50 output_latex=u'$a$',
51 output_latex=u'$a$',
51 output_png=b'data',
52 output_png=b'data',
53 output_jpeg=b'data',
52 output_svg=u'<svg>',
54 output_svg=u'<svg>',
53 output_json=u'json data',
55 output_json=u'json data',
54 output_javascript=u'var i=0;',
56 output_javascript=u'var i=0;',
55 prompt_number=4
57 prompt_number=4
56 )]
58 )]
57 ))
59 ))
58
60
59 nb0 = new_notebook(
61 nb0 = new_notebook(
60 name='nb0',
62 name='nb0',
61 worksheets=[ws, new_worksheet(name='worksheet2')]
63 worksheets=[ws, new_worksheet(name='worksheet2')]
62 )
64 )
63
65
64 nb0_py = """# <nbformat>2</nbformat>
66 nb0_py = """# <nbformat>2</nbformat>
65
67
66 # <codecell>
68 # <codecell>
67
69
68 import numpy
70 import numpy
69
71
70 # </codecell>
72 # </codecell>
71 # <codecell>
73 # <codecell>
72
74
73 a = numpy.random.rand(100)
75 a = numpy.random.rand(100)
74
76
75 # </codecell>
77 # </codecell>
76 # <codecell>
78 # <codecell>
77
79
78 print a
80 print a
79
81
80 # </codecell>
82 # </codecell>
81 """
83 """
82
84
83
85
@@ -1,64 +1,68 b''
1 import __builtin__
1 import __builtin__
2 from base64 import encodestring
2 from base64 import encodestring
3
3
4 from IPython.core.displayhook import DisplayHook
4 from IPython.core.displayhook import DisplayHook
5 from IPython.utils.traitlets import Instance, Dict
5 from IPython.utils.traitlets import Instance, Dict
6 from session import extract_header, Session
6 from session import extract_header, Session
7
7
8 class ZMQDisplayHook(object):
8 class ZMQDisplayHook(object):
9 """A simple displayhook that publishes the object's repr over a ZeroMQ
9 """A simple displayhook that publishes the object's repr over a ZeroMQ
10 socket."""
10 socket."""
11 topic=None
11 topic=None
12
12
13 def __init__(self, session, pub_socket):
13 def __init__(self, session, pub_socket):
14 self.session = session
14 self.session = session
15 self.pub_socket = pub_socket
15 self.pub_socket = pub_socket
16 self.parent_header = {}
16 self.parent_header = {}
17
17
18 def __call__(self, obj):
18 def __call__(self, obj):
19 if obj is None:
19 if obj is None:
20 return
20 return
21
21
22 __builtin__._ = obj
22 __builtin__._ = obj
23 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
23 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
24 parent=self.parent_header, ident=self.topic)
24 parent=self.parent_header, ident=self.topic)
25
25
26 def set_parent(self, parent):
26 def set_parent(self, parent):
27 self.parent_header = extract_header(parent)
27 self.parent_header = extract_header(parent)
28
28
29
29
30 def _encode_png(data):
30 def _encode_binary(format_dict):
31 pngdata = data.get('image/png')
31 pngdata = format_dict.get('image/png')
32 if pngdata is not None:
32 if pngdata is not None:
33 data['image/png'] = encodestring(pngdata)
33 format_dict['image/png'] = encodestring(pngdata)
34 jpegdata = format_dict.get('image/jpeg')
35 if jpegdata is not None:
36 format_dict['image/jpeg'] = encodestring(jpegdata)
37
34
38
35 class ZMQShellDisplayHook(DisplayHook):
39 class ZMQShellDisplayHook(DisplayHook):
36 """A displayhook subclass that publishes data using ZeroMQ. This is intended
40 """A displayhook subclass that publishes data using ZeroMQ. This is intended
37 to work with an InteractiveShell instance. It sends a dict of different
41 to work with an InteractiveShell instance. It sends a dict of different
38 representations of the object."""
42 representations of the object."""
39
43
40 session = Instance(Session)
44 session = Instance(Session)
41 pub_socket = Instance('zmq.Socket')
45 pub_socket = Instance('zmq.Socket')
42 parent_header = Dict({})
46 parent_header = Dict({})
43
47
44 def set_parent(self, parent):
48 def set_parent(self, parent):
45 """Set the parent for outbound messages."""
49 """Set the parent for outbound messages."""
46 self.parent_header = extract_header(parent)
50 self.parent_header = extract_header(parent)
47
51
48 def start_displayhook(self):
52 def start_displayhook(self):
49 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
53 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
50
54
51 def write_output_prompt(self):
55 def write_output_prompt(self):
52 """Write the output prompt."""
56 """Write the output prompt."""
53 if self.do_full_cache:
57 if self.do_full_cache:
54 self.msg['content']['execution_count'] = self.prompt_count
58 self.msg['content']['execution_count'] = self.prompt_count
55
59
56 def write_format_data(self, format_dict):
60 def write_format_data(self, format_dict):
57 pngdata = format_dict.get('image/png')
61 _encode_binary(format_dict)
58 _encode_png(format_dict)
59 self.msg['content']['data'] = format_dict
62 self.msg['content']['data'] = format_dict
60
63
61 def finish_displayhook(self):
64 def finish_displayhook(self):
62 """Finish up all displayhook activities."""
65 """Finish up all displayhook activities."""
63 self.session.send(self.pub_socket, self.msg)
66 self.session.send(self.pub_socket, self.msg)
64 self.msg = None
67 self.msg = None
68
@@ -1,457 +1,457 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
12 """
12 """
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import inspect
19 import inspect
20 import os
20 import os
21
21
22 # Our own
22 # Our own
23 from IPython.core.interactiveshell import (
23 from IPython.core.interactiveshell import (
24 InteractiveShell, InteractiveShellABC
24 InteractiveShell, InteractiveShellABC
25 )
25 )
26 from IPython.core import page
26 from IPython.core import page
27 from IPython.core.autocall import ZMQExitAutocall
27 from IPython.core.autocall import ZMQExitAutocall
28 from IPython.core.displaypub import DisplayPublisher
28 from IPython.core.displaypub import DisplayPublisher
29 from IPython.core.macro import Macro
29 from IPython.core.macro import Macro
30 from IPython.core.magic import MacroToEdit
30 from IPython.core.magic import MacroToEdit
31 from IPython.core.payloadpage import install_payload_page
31 from IPython.core.payloadpage import install_payload_page
32 from IPython.utils import io
32 from IPython.utils import io
33 from IPython.utils.path import get_py_filename
33 from IPython.utils.path import get_py_filename
34 from IPython.utils.traitlets import Instance, Type, Dict, CBool
34 from IPython.utils.traitlets import Instance, Type, Dict, CBool
35 from IPython.utils.warn import warn
35 from IPython.utils.warn import warn
36 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_png
36 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
37 from IPython.zmq.session import extract_header
37 from IPython.zmq.session import extract_header
38 from session import Session
38 from session import Session
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Globals and side-effects
41 # Globals and side-effects
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 # Install the payload version of page.
44 # Install the payload version of page.
45 install_payload_page()
45 install_payload_page()
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Functions and classes
48 # Functions and classes
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 class ZMQDisplayPublisher(DisplayPublisher):
51 class ZMQDisplayPublisher(DisplayPublisher):
52 """A display publisher that publishes data using a ZeroMQ PUB socket."""
52 """A display publisher that publishes data using a ZeroMQ PUB socket."""
53
53
54 session = Instance(Session)
54 session = Instance(Session)
55 pub_socket = Instance('zmq.Socket')
55 pub_socket = Instance('zmq.Socket')
56 parent_header = Dict({})
56 parent_header = Dict({})
57
57
58 def set_parent(self, parent):
58 def set_parent(self, parent):
59 """Set the parent for outbound messages."""
59 """Set the parent for outbound messages."""
60 self.parent_header = extract_header(parent)
60 self.parent_header = extract_header(parent)
61
61
62 def publish(self, source, data, metadata=None):
62 def publish(self, source, data, metadata=None):
63 if metadata is None:
63 if metadata is None:
64 metadata = {}
64 metadata = {}
65 self._validate_data(source, data, metadata)
65 self._validate_data(source, data, metadata)
66 content = {}
66 content = {}
67 content['source'] = source
67 content['source'] = source
68 _encode_png(data)
68 _encode_binary(data)
69 content['data'] = data
69 content['data'] = data
70 content['metadata'] = metadata
70 content['metadata'] = metadata
71 self.session.send(
71 self.session.send(
72 self.pub_socket, u'display_data', content,
72 self.pub_socket, u'display_data', content,
73 parent=self.parent_header
73 parent=self.parent_header
74 )
74 )
75
75
76
76
77 class ZMQInteractiveShell(InteractiveShell):
77 class ZMQInteractiveShell(InteractiveShell):
78 """A subclass of InteractiveShell for ZMQ."""
78 """A subclass of InteractiveShell for ZMQ."""
79
79
80 displayhook_class = Type(ZMQShellDisplayHook)
80 displayhook_class = Type(ZMQShellDisplayHook)
81 display_pub_class = Type(ZMQDisplayPublisher)
81 display_pub_class = Type(ZMQDisplayPublisher)
82
82
83 # Override the traitlet in the parent class, because there's no point using
83 # Override the traitlet in the parent class, because there's no point using
84 # readline for the kernel. Can be removed when the readline code is moved
84 # readline for the kernel. Can be removed when the readline code is moved
85 # to the terminal frontend.
85 # to the terminal frontend.
86
86
87 # FIXME. This is disabled for now, even though it may cause problems under
87 # FIXME. This is disabled for now, even though it may cause problems under
88 # Windows, because it breaks %run in the Qt console. See gh-617 for more
88 # Windows, because it breaks %run in the Qt console. See gh-617 for more
89 # details. Re-enable once we've fully tested that %run works in the Qt
89 # details. Re-enable once we've fully tested that %run works in the Qt
90 # console with syntax highlighting in tracebacks.
90 # console with syntax highlighting in tracebacks.
91 # readline_use = CBool(False)
91 # readline_use = CBool(False)
92 # /FIXME
92 # /FIXME
93
93
94 exiter = Instance(ZMQExitAutocall)
94 exiter = Instance(ZMQExitAutocall)
95 def _exiter_default(self):
95 def _exiter_default(self):
96 return ZMQExitAutocall(self)
96 return ZMQExitAutocall(self)
97
97
98 keepkernel_on_exit = None
98 keepkernel_on_exit = None
99
99
100 def init_environment(self):
100 def init_environment(self):
101 """Configure the user's environment.
101 """Configure the user's environment.
102
102
103 """
103 """
104 env = os.environ
104 env = os.environ
105 # These two ensure 'ls' produces nice coloring on BSD-derived systems
105 # These two ensure 'ls' produces nice coloring on BSD-derived systems
106 env['TERM'] = 'xterm-color'
106 env['TERM'] = 'xterm-color'
107 env['CLICOLOR'] = '1'
107 env['CLICOLOR'] = '1'
108 # Since normal pagers don't work at all (over pexpect we don't have
108 # Since normal pagers don't work at all (over pexpect we don't have
109 # single-key control of the subprocess), try to disable paging in
109 # single-key control of the subprocess), try to disable paging in
110 # subprocesses as much as possible.
110 # subprocesses as much as possible.
111 env['PAGER'] = 'cat'
111 env['PAGER'] = 'cat'
112 env['GIT_PAGER'] = 'cat'
112 env['GIT_PAGER'] = 'cat'
113
113
114 def auto_rewrite_input(self, cmd):
114 def auto_rewrite_input(self, cmd):
115 """Called to show the auto-rewritten input for autocall and friends.
115 """Called to show the auto-rewritten input for autocall and friends.
116
116
117 FIXME: this payload is currently not correctly processed by the
117 FIXME: this payload is currently not correctly processed by the
118 frontend.
118 frontend.
119 """
119 """
120 new = self.displayhook.prompt1.auto_rewrite() + cmd
120 new = self.displayhook.prompt1.auto_rewrite() + cmd
121 payload = dict(
121 payload = dict(
122 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
122 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
123 transformed_input=new,
123 transformed_input=new,
124 )
124 )
125 self.payload_manager.write_payload(payload)
125 self.payload_manager.write_payload(payload)
126
126
127 def ask_exit(self):
127 def ask_exit(self):
128 """Engage the exit actions."""
128 """Engage the exit actions."""
129 payload = dict(
129 payload = dict(
130 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
130 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
131 exit=True,
131 exit=True,
132 keepkernel=self.keepkernel_on_exit,
132 keepkernel=self.keepkernel_on_exit,
133 )
133 )
134 self.payload_manager.write_payload(payload)
134 self.payload_manager.write_payload(payload)
135
135
136 def _showtraceback(self, etype, evalue, stb):
136 def _showtraceback(self, etype, evalue, stb):
137
137
138 exc_content = {
138 exc_content = {
139 u'traceback' : stb,
139 u'traceback' : stb,
140 u'ename' : unicode(etype.__name__),
140 u'ename' : unicode(etype.__name__),
141 u'evalue' : unicode(evalue)
141 u'evalue' : unicode(evalue)
142 }
142 }
143
143
144 dh = self.displayhook
144 dh = self.displayhook
145 # Send exception info over pub socket for other clients than the caller
145 # Send exception info over pub socket for other clients than the caller
146 # to pick up
146 # to pick up
147 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', exc_content, dh.parent_header)
147 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', exc_content, dh.parent_header)
148
148
149 # FIXME - Hack: store exception info in shell object. Right now, the
149 # FIXME - Hack: store exception info in shell object. Right now, the
150 # caller is reading this info after the fact, we need to fix this logic
150 # caller is reading this info after the fact, we need to fix this logic
151 # to remove this hack. Even uglier, we need to store the error status
151 # to remove this hack. Even uglier, we need to store the error status
152 # here, because in the main loop, the logic that sets it is being
152 # here, because in the main loop, the logic that sets it is being
153 # skipped because runlines swallows the exceptions.
153 # skipped because runlines swallows the exceptions.
154 exc_content[u'status'] = u'error'
154 exc_content[u'status'] = u'error'
155 self._reply_content = exc_content
155 self._reply_content = exc_content
156 # /FIXME
156 # /FIXME
157
157
158 return exc_content
158 return exc_content
159
159
160 #------------------------------------------------------------------------
160 #------------------------------------------------------------------------
161 # Magic overrides
161 # Magic overrides
162 #------------------------------------------------------------------------
162 #------------------------------------------------------------------------
163 # Once the base class stops inheriting from magic, this code needs to be
163 # Once the base class stops inheriting from magic, this code needs to be
164 # moved into a separate machinery as well. For now, at least isolate here
164 # moved into a separate machinery as well. For now, at least isolate here
165 # the magics which this class needs to implement differently from the base
165 # the magics which this class needs to implement differently from the base
166 # class, or that are unique to it.
166 # class, or that are unique to it.
167
167
168 def magic_doctest_mode(self,parameter_s=''):
168 def magic_doctest_mode(self,parameter_s=''):
169 """Toggle doctest mode on and off.
169 """Toggle doctest mode on and off.
170
170
171 This mode is intended to make IPython behave as much as possible like a
171 This mode is intended to make IPython behave as much as possible like a
172 plain Python shell, from the perspective of how its prompts, exceptions
172 plain Python shell, from the perspective of how its prompts, exceptions
173 and output look. This makes it easy to copy and paste parts of a
173 and output look. This makes it easy to copy and paste parts of a
174 session into doctests. It does so by:
174 session into doctests. It does so by:
175
175
176 - Changing the prompts to the classic ``>>>`` ones.
176 - Changing the prompts to the classic ``>>>`` ones.
177 - Changing the exception reporting mode to 'Plain'.
177 - Changing the exception reporting mode to 'Plain'.
178 - Disabling pretty-printing of output.
178 - Disabling pretty-printing of output.
179
179
180 Note that IPython also supports the pasting of code snippets that have
180 Note that IPython also supports the pasting of code snippets that have
181 leading '>>>' and '...' prompts in them. This means that you can paste
181 leading '>>>' and '...' prompts in them. This means that you can paste
182 doctests from files or docstrings (even if they have leading
182 doctests from files or docstrings (even if they have leading
183 whitespace), and the code will execute correctly. You can then use
183 whitespace), and the code will execute correctly. You can then use
184 '%history -t' to see the translated history; this will give you the
184 '%history -t' to see the translated history; this will give you the
185 input after removal of all the leading prompts and whitespace, which
185 input after removal of all the leading prompts and whitespace, which
186 can be pasted back into an editor.
186 can be pasted back into an editor.
187
187
188 With these features, you can switch into this mode easily whenever you
188 With these features, you can switch into this mode easily whenever you
189 need to do testing and changes to doctests, without having to leave
189 need to do testing and changes to doctests, without having to leave
190 your existing IPython session.
190 your existing IPython session.
191 """
191 """
192
192
193 from IPython.utils.ipstruct import Struct
193 from IPython.utils.ipstruct import Struct
194
194
195 # Shorthands
195 # Shorthands
196 shell = self.shell
196 shell = self.shell
197 disp_formatter = self.shell.display_formatter
197 disp_formatter = self.shell.display_formatter
198 ptformatter = disp_formatter.formatters['text/plain']
198 ptformatter = disp_formatter.formatters['text/plain']
199 # dstore is a data store kept in the instance metadata bag to track any
199 # dstore is a data store kept in the instance metadata bag to track any
200 # changes we make, so we can undo them later.
200 # changes we make, so we can undo them later.
201 dstore = shell.meta.setdefault('doctest_mode', Struct())
201 dstore = shell.meta.setdefault('doctest_mode', Struct())
202 save_dstore = dstore.setdefault
202 save_dstore = dstore.setdefault
203
203
204 # save a few values we'll need to recover later
204 # save a few values we'll need to recover later
205 mode = save_dstore('mode', False)
205 mode = save_dstore('mode', False)
206 save_dstore('rc_pprint', ptformatter.pprint)
206 save_dstore('rc_pprint', ptformatter.pprint)
207 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
207 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
208 save_dstore('xmode', shell.InteractiveTB.mode)
208 save_dstore('xmode', shell.InteractiveTB.mode)
209
209
210 if mode == False:
210 if mode == False:
211 # turn on
211 # turn on
212 ptformatter.pprint = False
212 ptformatter.pprint = False
213 disp_formatter.plain_text_only = True
213 disp_formatter.plain_text_only = True
214 shell.magic_xmode('Plain')
214 shell.magic_xmode('Plain')
215 else:
215 else:
216 # turn off
216 # turn off
217 ptformatter.pprint = dstore.rc_pprint
217 ptformatter.pprint = dstore.rc_pprint
218 disp_formatter.plain_text_only = dstore.rc_plain_text_only
218 disp_formatter.plain_text_only = dstore.rc_plain_text_only
219 shell.magic_xmode(dstore.xmode)
219 shell.magic_xmode(dstore.xmode)
220
220
221 # Store new mode and inform on console
221 # Store new mode and inform on console
222 dstore.mode = bool(1-int(mode))
222 dstore.mode = bool(1-int(mode))
223 mode_label = ['OFF','ON'][dstore.mode]
223 mode_label = ['OFF','ON'][dstore.mode]
224 print('Doctest mode is:', mode_label)
224 print('Doctest mode is:', mode_label)
225
225
226 # Send the payload back so that clients can modify their prompt display
226 # Send the payload back so that clients can modify their prompt display
227 payload = dict(
227 payload = dict(
228 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
228 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
229 mode=dstore.mode)
229 mode=dstore.mode)
230 self.payload_manager.write_payload(payload)
230 self.payload_manager.write_payload(payload)
231
231
232 def magic_edit(self,parameter_s='',last_call=['','']):
232 def magic_edit(self,parameter_s='',last_call=['','']):
233 """Bring up an editor and execute the resulting code.
233 """Bring up an editor and execute the resulting code.
234
234
235 Usage:
235 Usage:
236 %edit [options] [args]
236 %edit [options] [args]
237
237
238 %edit runs IPython's editor hook. The default version of this hook is
238 %edit runs IPython's editor hook. The default version of this hook is
239 set to call the __IPYTHON__.rc.editor command. This is read from your
239 set to call the __IPYTHON__.rc.editor command. This is read from your
240 environment variable $EDITOR. If this isn't found, it will default to
240 environment variable $EDITOR. If this isn't found, it will default to
241 vi under Linux/Unix and to notepad under Windows. See the end of this
241 vi under Linux/Unix and to notepad under Windows. See the end of this
242 docstring for how to change the editor hook.
242 docstring for how to change the editor hook.
243
243
244 You can also set the value of this editor via the command line option
244 You can also set the value of this editor via the command line option
245 '-editor' or in your ipythonrc file. This is useful if you wish to use
245 '-editor' or in your ipythonrc file. This is useful if you wish to use
246 specifically for IPython an editor different from your typical default
246 specifically for IPython an editor different from your typical default
247 (and for Windows users who typically don't set environment variables).
247 (and for Windows users who typically don't set environment variables).
248
248
249 This command allows you to conveniently edit multi-line code right in
249 This command allows you to conveniently edit multi-line code right in
250 your IPython session.
250 your IPython session.
251
251
252 If called without arguments, %edit opens up an empty editor with a
252 If called without arguments, %edit opens up an empty editor with a
253 temporary file and will execute the contents of this file when you
253 temporary file and will execute the contents of this file when you
254 close it (don't forget to save it!).
254 close it (don't forget to save it!).
255
255
256
256
257 Options:
257 Options:
258
258
259 -n <number>: open the editor at a specified line number. By default,
259 -n <number>: open the editor at a specified line number. By default,
260 the IPython editor hook uses the unix syntax 'editor +N filename', but
260 the IPython editor hook uses the unix syntax 'editor +N filename', but
261 you can configure this by providing your own modified hook if your
261 you can configure this by providing your own modified hook if your
262 favorite editor supports line-number specifications with a different
262 favorite editor supports line-number specifications with a different
263 syntax.
263 syntax.
264
264
265 -p: this will call the editor with the same data as the previous time
265 -p: this will call the editor with the same data as the previous time
266 it was used, regardless of how long ago (in your current session) it
266 it was used, regardless of how long ago (in your current session) it
267 was.
267 was.
268
268
269 -r: use 'raw' input. This option only applies to input taken from the
269 -r: use 'raw' input. This option only applies to input taken from the
270 user's history. By default, the 'processed' history is used, so that
270 user's history. By default, the 'processed' history is used, so that
271 magics are loaded in their transformed version to valid Python. If
271 magics are loaded in their transformed version to valid Python. If
272 this option is given, the raw input as typed as the command line is
272 this option is given, the raw input as typed as the command line is
273 used instead. When you exit the editor, it will be executed by
273 used instead. When you exit the editor, it will be executed by
274 IPython's own processor.
274 IPython's own processor.
275
275
276 -x: do not execute the edited code immediately upon exit. This is
276 -x: do not execute the edited code immediately upon exit. This is
277 mainly useful if you are editing programs which need to be called with
277 mainly useful if you are editing programs which need to be called with
278 command line arguments, which you can then do using %run.
278 command line arguments, which you can then do using %run.
279
279
280
280
281 Arguments:
281 Arguments:
282
282
283 If arguments are given, the following possibilites exist:
283 If arguments are given, the following possibilites exist:
284
284
285 - The arguments are numbers or pairs of colon-separated numbers (like
285 - The arguments are numbers or pairs of colon-separated numbers (like
286 1 4:8 9). These are interpreted as lines of previous input to be
286 1 4:8 9). These are interpreted as lines of previous input to be
287 loaded into the editor. The syntax is the same of the %macro command.
287 loaded into the editor. The syntax is the same of the %macro command.
288
288
289 - If the argument doesn't start with a number, it is evaluated as a
289 - If the argument doesn't start with a number, it is evaluated as a
290 variable and its contents loaded into the editor. You can thus edit
290 variable and its contents loaded into the editor. You can thus edit
291 any string which contains python code (including the result of
291 any string which contains python code (including the result of
292 previous edits).
292 previous edits).
293
293
294 - If the argument is the name of an object (other than a string),
294 - If the argument is the name of an object (other than a string),
295 IPython will try to locate the file where it was defined and open the
295 IPython will try to locate the file where it was defined and open the
296 editor at the point where it is defined. You can use `%edit function`
296 editor at the point where it is defined. You can use `%edit function`
297 to load an editor exactly at the point where 'function' is defined,
297 to load an editor exactly at the point where 'function' is defined,
298 edit it and have the file be executed automatically.
298 edit it and have the file be executed automatically.
299
299
300 If the object is a macro (see %macro for details), this opens up your
300 If the object is a macro (see %macro for details), this opens up your
301 specified editor with a temporary file containing the macro's data.
301 specified editor with a temporary file containing the macro's data.
302 Upon exit, the macro is reloaded with the contents of the file.
302 Upon exit, the macro is reloaded with the contents of the file.
303
303
304 Note: opening at an exact line is only supported under Unix, and some
304 Note: opening at an exact line is only supported under Unix, and some
305 editors (like kedit and gedit up to Gnome 2.8) do not understand the
305 editors (like kedit and gedit up to Gnome 2.8) do not understand the
306 '+NUMBER' parameter necessary for this feature. Good editors like
306 '+NUMBER' parameter necessary for this feature. Good editors like
307 (X)Emacs, vi, jed, pico and joe all do.
307 (X)Emacs, vi, jed, pico and joe all do.
308
308
309 - If the argument is not found as a variable, IPython will look for a
309 - If the argument is not found as a variable, IPython will look for a
310 file with that name (adding .py if necessary) and load it into the
310 file with that name (adding .py if necessary) and load it into the
311 editor. It will execute its contents with execfile() when you exit,
311 editor. It will execute its contents with execfile() when you exit,
312 loading any code in the file into your interactive namespace.
312 loading any code in the file into your interactive namespace.
313
313
314 After executing your code, %edit will return as output the code you
314 After executing your code, %edit will return as output the code you
315 typed in the editor (except when it was an existing file). This way
315 typed in the editor (except when it was an existing file). This way
316 you can reload the code in further invocations of %edit as a variable,
316 you can reload the code in further invocations of %edit as a variable,
317 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
317 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
318 the output.
318 the output.
319
319
320 Note that %edit is also available through the alias %ed.
320 Note that %edit is also available through the alias %ed.
321
321
322 This is an example of creating a simple function inside the editor and
322 This is an example of creating a simple function inside the editor and
323 then modifying it. First, start up the editor:
323 then modifying it. First, start up the editor:
324
324
325 In [1]: ed
325 In [1]: ed
326 Editing... done. Executing edited code...
326 Editing... done. Executing edited code...
327 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
327 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
328
328
329 We can then call the function foo():
329 We can then call the function foo():
330
330
331 In [2]: foo()
331 In [2]: foo()
332 foo() was defined in an editing session
332 foo() was defined in an editing session
333
333
334 Now we edit foo. IPython automatically loads the editor with the
334 Now we edit foo. IPython automatically loads the editor with the
335 (temporary) file where foo() was previously defined:
335 (temporary) file where foo() was previously defined:
336
336
337 In [3]: ed foo
337 In [3]: ed foo
338 Editing... done. Executing edited code...
338 Editing... done. Executing edited code...
339
339
340 And if we call foo() again we get the modified version:
340 And if we call foo() again we get the modified version:
341
341
342 In [4]: foo()
342 In [4]: foo()
343 foo() has now been changed!
343 foo() has now been changed!
344
344
345 Here is an example of how to edit a code snippet successive
345 Here is an example of how to edit a code snippet successive
346 times. First we call the editor:
346 times. First we call the editor:
347
347
348 In [5]: ed
348 In [5]: ed
349 Editing... done. Executing edited code...
349 Editing... done. Executing edited code...
350 hello
350 hello
351 Out[5]: "print 'hello'n"
351 Out[5]: "print 'hello'n"
352
352
353 Now we call it again with the previous output (stored in _):
353 Now we call it again with the previous output (stored in _):
354
354
355 In [6]: ed _
355 In [6]: ed _
356 Editing... done. Executing edited code...
356 Editing... done. Executing edited code...
357 hello world
357 hello world
358 Out[6]: "print 'hello world'n"
358 Out[6]: "print 'hello world'n"
359
359
360 Now we call it with the output #8 (stored in _8, also as Out[8]):
360 Now we call it with the output #8 (stored in _8, also as Out[8]):
361
361
362 In [7]: ed _8
362 In [7]: ed _8
363 Editing... done. Executing edited code...
363 Editing... done. Executing edited code...
364 hello again
364 hello again
365 Out[7]: "print 'hello again'n"
365 Out[7]: "print 'hello again'n"
366
366
367
367
368 Changing the default editor hook:
368 Changing the default editor hook:
369
369
370 If you wish to write your own editor hook, you can put it in a
370 If you wish to write your own editor hook, you can put it in a
371 configuration file which you load at startup time. The default hook
371 configuration file which you load at startup time. The default hook
372 is defined in the IPython.core.hooks module, and you can use that as a
372 is defined in the IPython.core.hooks module, and you can use that as a
373 starting example for further modifications. That file also has
373 starting example for further modifications. That file also has
374 general instructions on how to set a new hook for use once you've
374 general instructions on how to set a new hook for use once you've
375 defined it."""
375 defined it."""
376
376
377 opts,args = self.parse_options(parameter_s,'prn:')
377 opts,args = self.parse_options(parameter_s,'prn:')
378
378
379 try:
379 try:
380 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
380 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
381 except MacroToEdit as e:
381 except MacroToEdit as e:
382 # TODO: Implement macro editing over 2 processes.
382 # TODO: Implement macro editing over 2 processes.
383 print("Macro editing not yet implemented in 2-process model.")
383 print("Macro editing not yet implemented in 2-process model.")
384 return
384 return
385
385
386 # Make sure we send to the client an absolute path, in case the working
386 # Make sure we send to the client an absolute path, in case the working
387 # directory of client and kernel don't match
387 # directory of client and kernel don't match
388 filename = os.path.abspath(filename)
388 filename = os.path.abspath(filename)
389
389
390 payload = {
390 payload = {
391 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
391 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
392 'filename' : filename,
392 'filename' : filename,
393 'line_number' : lineno
393 'line_number' : lineno
394 }
394 }
395 self.payload_manager.write_payload(payload)
395 self.payload_manager.write_payload(payload)
396
396
397 def magic_gui(self, *args, **kwargs):
397 def magic_gui(self, *args, **kwargs):
398 raise NotImplementedError(
398 raise NotImplementedError(
399 'Kernel GUI support is not implemented yet, except for --pylab.')
399 'Kernel GUI support is not implemented yet, except for --pylab.')
400
400
401 def magic_pylab(self, *args, **kwargs):
401 def magic_pylab(self, *args, **kwargs):
402 raise NotImplementedError(
402 raise NotImplementedError(
403 'pylab support must be enabled in command line options.')
403 'pylab support must be enabled in command line options.')
404
404
405 # A few magics that are adapted to the specifics of using pexpect and a
405 # A few magics that are adapted to the specifics of using pexpect and a
406 # remote terminal
406 # remote terminal
407
407
408 def magic_clear(self, arg_s):
408 def magic_clear(self, arg_s):
409 """Clear the terminal."""
409 """Clear the terminal."""
410 if os.name == 'posix':
410 if os.name == 'posix':
411 self.shell.system("clear")
411 self.shell.system("clear")
412 else:
412 else:
413 self.shell.system("cls")
413 self.shell.system("cls")
414
414
415 if os.name == 'nt':
415 if os.name == 'nt':
416 # This is the usual name in windows
416 # This is the usual name in windows
417 magic_cls = magic_clear
417 magic_cls = magic_clear
418
418
419 # Terminal pagers won't work over pexpect, but we do have our own pager
419 # Terminal pagers won't work over pexpect, but we do have our own pager
420
420
421 def magic_less(self, arg_s):
421 def magic_less(self, arg_s):
422 """Show a file through the pager.
422 """Show a file through the pager.
423
423
424 Files ending in .py are syntax-highlighted."""
424 Files ending in .py are syntax-highlighted."""
425 cont = open(arg_s).read()
425 cont = open(arg_s).read()
426 if arg_s.endswith('.py'):
426 if arg_s.endswith('.py'):
427 cont = self.shell.pycolorize(cont)
427 cont = self.shell.pycolorize(cont)
428 page.page(cont)
428 page.page(cont)
429
429
430 magic_more = magic_less
430 magic_more = magic_less
431
431
432 # Man calls a pager, so we also need to redefine it
432 # Man calls a pager, so we also need to redefine it
433 if os.name == 'posix':
433 if os.name == 'posix':
434 def magic_man(self, arg_s):
434 def magic_man(self, arg_s):
435 """Find the man page for the given command and display in pager."""
435 """Find the man page for the given command and display in pager."""
436 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
436 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
437 split=False))
437 split=False))
438
438
439 # FIXME: this is specific to the GUI, so we should let the gui app load
439 # FIXME: this is specific to the GUI, so we should let the gui app load
440 # magics at startup that are only for the gui. Once the gui app has proper
440 # magics at startup that are only for the gui. Once the gui app has proper
441 # profile and configuration management, we can have it initialize a kernel
441 # profile and configuration management, we can have it initialize a kernel
442 # with a special config file that provides these.
442 # with a special config file that provides these.
443 def magic_guiref(self, arg_s):
443 def magic_guiref(self, arg_s):
444 """Show a basic reference about the GUI console."""
444 """Show a basic reference about the GUI console."""
445 from IPython.core.usage import gui_reference
445 from IPython.core.usage import gui_reference
446 page.page(gui_reference, auto_html=True)
446 page.page(gui_reference, auto_html=True)
447
447
448 def set_next_input(self, text):
448 def set_next_input(self, text):
449 """Send the specified text to the frontend to be presented at the next
449 """Send the specified text to the frontend to be presented at the next
450 input cell."""
450 input cell."""
451 payload = dict(
451 payload = dict(
452 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
452 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
453 text=text
453 text=text
454 )
454 )
455 self.payload_manager.write_payload(payload)
455 self.payload_manager.write_payload(payload)
456
456
457 InteractiveShellABC.register(ZMQInteractiveShell)
457 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now