##// END OF EJS Templates
Merge pull request #3381 from minrk/retina...
Paul Ivanov -
r11049:d820363e merge
parent child Browse files
Show More
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -1,635 +1,689 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) 2013 The IPython Development Team
10 # Copyright (C) 2013 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 __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import struct
23
24
24 from IPython.utils.py3compat import string_types
25 from IPython.utils.py3compat import string_types
25
26
26 from .displaypub import publish_display_data
27 from .displaypub import publish_display_data
27
28
28 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
29 # utility functions
30 # utility functions
30 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
31
32
32 def _safe_exists(path):
33 def _safe_exists(path):
33 """Check path, but don't let exceptions raise"""
34 """Check path, but don't let exceptions raise"""
34 try:
35 try:
35 return os.path.exists(path)
36 return os.path.exists(path)
36 except Exception:
37 except Exception:
37 return False
38 return False
38
39
39 def _merge(d1, d2):
40 def _merge(d1, d2):
40 """Like update, but merges sub-dicts instead of clobbering at the top level.
41 """Like update, but merges sub-dicts instead of clobbering at the top level.
41
42
42 Updates d1 in-place
43 Updates d1 in-place
43 """
44 """
44
45
45 if not isinstance(d2, dict) or not isinstance(d1, dict):
46 if not isinstance(d2, dict) or not isinstance(d1, dict):
46 return d2
47 return d2
47 for key, value in d2.items():
48 for key, value in d2.items():
48 d1[key] = _merge(d1.get(key), value)
49 d1[key] = _merge(d1.get(key), value)
49 return d1
50 return d1
50
51
51 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
52 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
52 """internal implementation of all display_foo methods
53 """internal implementation of all display_foo methods
53
54
54 Parameters
55 Parameters
55 ----------
56 ----------
56 mimetype : str
57 mimetype : str
57 The mimetype to be published (e.g. 'image/png')
58 The mimetype to be published (e.g. 'image/png')
58 objs : tuple of objects
59 objs : tuple of objects
59 The Python objects to display, or if raw=True raw text data to
60 The Python objects to display, or if raw=True raw text data to
60 display.
61 display.
61 raw : bool
62 raw : bool
62 Are the data objects raw data or Python objects that need to be
63 Are the data objects raw data or Python objects that need to be
63 formatted before display? [default: False]
64 formatted before display? [default: False]
64 metadata : dict (optional)
65 metadata : dict (optional)
65 Metadata to be associated with the specific mimetype output.
66 Metadata to be associated with the specific mimetype output.
66 """
67 """
67 if metadata:
68 if metadata:
68 metadata = {mimetype: metadata}
69 metadata = {mimetype: metadata}
69 if raw:
70 if raw:
70 # turn list of pngdata into list of { 'image/png': pngdata }
71 # turn list of pngdata into list of { 'image/png': pngdata }
71 objs = [ {mimetype: obj} for obj in objs ]
72 objs = [ {mimetype: obj} for obj in objs ]
72 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
73 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
73
74
74 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
75 # Main functions
76 # Main functions
76 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
77
78
78 def display(*objs, **kwargs):
79 def display(*objs, **kwargs):
79 """Display a Python object in all frontends.
80 """Display a Python object in all frontends.
80
81
81 By default all representations will be computed and sent to the frontends.
82 By default all representations will be computed and sent to the frontends.
82 Frontends can decide which representation is used and how.
83 Frontends can decide which representation is used and how.
83
84
84 Parameters
85 Parameters
85 ----------
86 ----------
86 objs : tuple of objects
87 objs : tuple of objects
87 The Python objects to display.
88 The Python objects to display.
88 raw : bool, optional
89 raw : bool, optional
89 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
90 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
90 or Python objects that need to be formatted before display? [default: False]
91 or Python objects that need to be formatted before display? [default: False]
91 include : list or tuple, optional
92 include : list or tuple, optional
92 A list of format type strings (MIME types) to include in the
93 A list of format type strings (MIME types) to include in the
93 format data dict. If this is set *only* the format types included
94 format data dict. If this is set *only* the format types included
94 in this list will be computed.
95 in this list will be computed.
95 exclude : list or tuple, optional
96 exclude : list or tuple, optional
96 A list of format type strings (MIME types) to exclude in the format
97 A list of format type strings (MIME types) to exclude in the format
97 data dict. If this is set all format types will be computed,
98 data dict. If this is set all format types will be computed,
98 except for those included in this argument.
99 except for those included in this argument.
99 metadata : dict, optional
100 metadata : dict, optional
100 A dictionary of metadata to associate with the output.
101 A dictionary of metadata to associate with the output.
101 mime-type keys in this dictionary will be associated with the individual
102 mime-type keys in this dictionary will be associated with the individual
102 representation formats, if they exist.
103 representation formats, if they exist.
103 """
104 """
104 raw = kwargs.get('raw', False)
105 raw = kwargs.get('raw', False)
105 include = kwargs.get('include')
106 include = kwargs.get('include')
106 exclude = kwargs.get('exclude')
107 exclude = kwargs.get('exclude')
107 metadata = kwargs.get('metadata')
108 metadata = kwargs.get('metadata')
108
109
109 from IPython.core.interactiveshell import InteractiveShell
110 from IPython.core.interactiveshell import InteractiveShell
110
111
111 if raw:
112 if raw:
112 for obj in objs:
113 for obj in objs:
113 publish_display_data('display', obj, metadata)
114 publish_display_data('display', obj, metadata)
114 else:
115 else:
115 format = InteractiveShell.instance().display_formatter.format
116 format = InteractiveShell.instance().display_formatter.format
116 for obj in objs:
117 for obj in objs:
117 format_dict, md_dict = format(obj, include=include, exclude=exclude)
118 format_dict, md_dict = format(obj, include=include, exclude=exclude)
118 if metadata:
119 if metadata:
119 # kwarg-specified metadata gets precedence
120 # kwarg-specified metadata gets precedence
120 _merge(md_dict, metadata)
121 _merge(md_dict, metadata)
121 publish_display_data('display', format_dict, md_dict)
122 publish_display_data('display', format_dict, md_dict)
122
123
123
124
124 def display_pretty(*objs, **kwargs):
125 def display_pretty(*objs, **kwargs):
125 """Display the pretty (default) representation of an object.
126 """Display the pretty (default) representation of an object.
126
127
127 Parameters
128 Parameters
128 ----------
129 ----------
129 objs : tuple of objects
130 objs : tuple of objects
130 The Python objects to display, or if raw=True raw text data to
131 The Python objects to display, or if raw=True raw text data to
131 display.
132 display.
132 raw : bool
133 raw : bool
133 Are the data objects raw data or Python objects that need to be
134 Are the data objects raw data or Python objects that need to be
134 formatted before display? [default: False]
135 formatted before display? [default: False]
135 metadata : dict (optional)
136 metadata : dict (optional)
136 Metadata to be associated with the specific mimetype output.
137 Metadata to be associated with the specific mimetype output.
137 """
138 """
138 _display_mimetype('text/plain', objs, **kwargs)
139 _display_mimetype('text/plain', objs, **kwargs)
139
140
140
141
141 def display_html(*objs, **kwargs):
142 def display_html(*objs, **kwargs):
142 """Display the HTML representation of an object.
143 """Display the HTML representation of an object.
143
144
144 Parameters
145 Parameters
145 ----------
146 ----------
146 objs : tuple of objects
147 objs : tuple of objects
147 The Python objects to display, or if raw=True raw HTML data to
148 The Python objects to display, or if raw=True raw HTML data to
148 display.
149 display.
149 raw : bool
150 raw : bool
150 Are the data objects raw data or Python objects that need to be
151 Are the data objects raw data or Python objects that need to be
151 formatted before display? [default: False]
152 formatted before display? [default: False]
152 metadata : dict (optional)
153 metadata : dict (optional)
153 Metadata to be associated with the specific mimetype output.
154 Metadata to be associated with the specific mimetype output.
154 """
155 """
155 _display_mimetype('text/html', objs, **kwargs)
156 _display_mimetype('text/html', objs, **kwargs)
156
157
157
158
158 def display_svg(*objs, **kwargs):
159 def display_svg(*objs, **kwargs):
159 """Display the SVG representation of an object.
160 """Display the SVG representation of an object.
160
161
161 Parameters
162 Parameters
162 ----------
163 ----------
163 objs : tuple of objects
164 objs : tuple of objects
164 The Python objects to display, or if raw=True raw svg data to
165 The Python objects to display, or if raw=True raw svg data to
165 display.
166 display.
166 raw : bool
167 raw : bool
167 Are the data objects raw data or Python objects that need to be
168 Are the data objects raw data or Python objects that need to be
168 formatted before display? [default: False]
169 formatted before display? [default: False]
169 metadata : dict (optional)
170 metadata : dict (optional)
170 Metadata to be associated with the specific mimetype output.
171 Metadata to be associated with the specific mimetype output.
171 """
172 """
172 _display_mimetype('image/svg+xml', objs, **kwargs)
173 _display_mimetype('image/svg+xml', objs, **kwargs)
173
174
174
175
175 def display_png(*objs, **kwargs):
176 def display_png(*objs, **kwargs):
176 """Display the PNG representation of an object.
177 """Display the PNG representation of an object.
177
178
178 Parameters
179 Parameters
179 ----------
180 ----------
180 objs : tuple of objects
181 objs : tuple of objects
181 The Python objects to display, or if raw=True raw png data to
182 The Python objects to display, or if raw=True raw png data to
182 display.
183 display.
183 raw : bool
184 raw : bool
184 Are the data objects raw data or Python objects that need to be
185 Are the data objects raw data or Python objects that need to be
185 formatted before display? [default: False]
186 formatted before display? [default: False]
186 metadata : dict (optional)
187 metadata : dict (optional)
187 Metadata to be associated with the specific mimetype output.
188 Metadata to be associated with the specific mimetype output.
188 """
189 """
189 _display_mimetype('image/png', objs, **kwargs)
190 _display_mimetype('image/png', objs, **kwargs)
190
191
191
192
192 def display_jpeg(*objs, **kwargs):
193 def display_jpeg(*objs, **kwargs):
193 """Display the JPEG representation of an object.
194 """Display the JPEG representation of an object.
194
195
195 Parameters
196 Parameters
196 ----------
197 ----------
197 objs : tuple of objects
198 objs : tuple of objects
198 The Python objects to display, or if raw=True raw JPEG data to
199 The Python objects to display, or if raw=True raw JPEG data to
199 display.
200 display.
200 raw : bool
201 raw : bool
201 Are the data objects raw data or Python objects that need to be
202 Are the data objects raw data or Python objects that need to be
202 formatted before display? [default: False]
203 formatted before display? [default: False]
203 metadata : dict (optional)
204 metadata : dict (optional)
204 Metadata to be associated with the specific mimetype output.
205 Metadata to be associated with the specific mimetype output.
205 """
206 """
206 _display_mimetype('image/jpeg', objs, **kwargs)
207 _display_mimetype('image/jpeg', objs, **kwargs)
207
208
208
209
209 def display_latex(*objs, **kwargs):
210 def display_latex(*objs, **kwargs):
210 """Display the LaTeX representation of an object.
211 """Display the LaTeX representation of an object.
211
212
212 Parameters
213 Parameters
213 ----------
214 ----------
214 objs : tuple of objects
215 objs : tuple of objects
215 The Python objects to display, or if raw=True raw latex data to
216 The Python objects to display, or if raw=True raw latex data to
216 display.
217 display.
217 raw : bool
218 raw : bool
218 Are the data objects raw data or Python objects that need to be
219 Are the data objects raw data or Python objects that need to be
219 formatted before display? [default: False]
220 formatted before display? [default: False]
220 metadata : dict (optional)
221 metadata : dict (optional)
221 Metadata to be associated with the specific mimetype output.
222 Metadata to be associated with the specific mimetype output.
222 """
223 """
223 _display_mimetype('text/latex', objs, **kwargs)
224 _display_mimetype('text/latex', objs, **kwargs)
224
225
225
226
226 def display_json(*objs, **kwargs):
227 def display_json(*objs, **kwargs):
227 """Display the JSON representation of an object.
228 """Display the JSON representation of an object.
228
229
229 Note that not many frontends support displaying JSON.
230 Note that not many frontends support displaying JSON.
230
231
231 Parameters
232 Parameters
232 ----------
233 ----------
233 objs : tuple of objects
234 objs : tuple of objects
234 The Python objects to display, or if raw=True raw json data to
235 The Python objects to display, or if raw=True raw json data to
235 display.
236 display.
236 raw : bool
237 raw : bool
237 Are the data objects raw data or Python objects that need to be
238 Are the data objects raw data or Python objects that need to be
238 formatted before display? [default: False]
239 formatted before display? [default: False]
239 metadata : dict (optional)
240 metadata : dict (optional)
240 Metadata to be associated with the specific mimetype output.
241 Metadata to be associated with the specific mimetype output.
241 """
242 """
242 _display_mimetype('application/json', objs, **kwargs)
243 _display_mimetype('application/json', objs, **kwargs)
243
244
244
245
245 def display_javascript(*objs, **kwargs):
246 def display_javascript(*objs, **kwargs):
246 """Display the Javascript representation of an object.
247 """Display the Javascript representation of an object.
247
248
248 Parameters
249 Parameters
249 ----------
250 ----------
250 objs : tuple of objects
251 objs : tuple of objects
251 The Python objects to display, or if raw=True raw javascript data to
252 The Python objects to display, or if raw=True raw javascript data to
252 display.
253 display.
253 raw : bool
254 raw : bool
254 Are the data objects raw data or Python objects that need to be
255 Are the data objects raw data or Python objects that need to be
255 formatted before display? [default: False]
256 formatted before display? [default: False]
256 metadata : dict (optional)
257 metadata : dict (optional)
257 Metadata to be associated with the specific mimetype output.
258 Metadata to be associated with the specific mimetype output.
258 """
259 """
259 _display_mimetype('application/javascript', objs, **kwargs)
260 _display_mimetype('application/javascript', objs, **kwargs)
260
261
261 #-----------------------------------------------------------------------------
262 #-----------------------------------------------------------------------------
262 # Smart classes
263 # Smart classes
263 #-----------------------------------------------------------------------------
264 #-----------------------------------------------------------------------------
264
265
265
266
266 class DisplayObject(object):
267 class DisplayObject(object):
267 """An object that wraps data to be displayed."""
268 """An object that wraps data to be displayed."""
268
269
269 _read_flags = 'r'
270 _read_flags = 'r'
270
271
271 def __init__(self, data=None, url=None, filename=None):
272 def __init__(self, data=None, url=None, filename=None):
272 """Create a display object given raw data.
273 """Create a display object given raw data.
273
274
274 When this object is returned by an expression or passed to the
275 When this object is returned by an expression or passed to the
275 display function, it will result in the data being displayed
276 display function, it will result in the data being displayed
276 in the frontend. The MIME type of the data should match the
277 in the frontend. The MIME type of the data should match the
277 subclasses used, so the Png subclass should be used for 'image/png'
278 subclasses used, so the Png subclass should be used for 'image/png'
278 data. If the data is a URL, the data will first be downloaded
279 data. If the data is a URL, the data will first be downloaded
279 and then displayed. If
280 and then displayed. If
280
281
281 Parameters
282 Parameters
282 ----------
283 ----------
283 data : unicode, str or bytes
284 data : unicode, str or bytes
284 The raw data or a URL or file to load the data from
285 The raw data or a URL or file to load the data from
285 url : unicode
286 url : unicode
286 A URL to download the data from.
287 A URL to download the data from.
287 filename : unicode
288 filename : unicode
288 Path to a local file to load the data from.
289 Path to a local file to load the data from.
289 """
290 """
290 if data is not None and isinstance(data, string_types):
291 if data is not None and isinstance(data, string_types):
291 if data.startswith('http') and url is None:
292 if data.startswith('http') and url is None:
292 url = data
293 url = data
293 filename = None
294 filename = None
294 data = None
295 data = None
295 elif _safe_exists(data) and filename is None:
296 elif _safe_exists(data) and filename is None:
296 url = None
297 url = None
297 filename = data
298 filename = data
298 data = None
299 data = None
299
300
300 self.data = data
301 self.data = data
301 self.url = url
302 self.url = url
302 self.filename = None if filename is None else unicode(filename)
303 self.filename = None if filename is None else unicode(filename)
303
304
304 self.reload()
305 self.reload()
305
306
306 def reload(self):
307 def reload(self):
307 """Reload the raw data from file or URL."""
308 """Reload the raw data from file or URL."""
308 if self.filename is not None:
309 if self.filename is not None:
309 with open(self.filename, self._read_flags) as f:
310 with open(self.filename, self._read_flags) as f:
310 self.data = f.read()
311 self.data = f.read()
311 elif self.url is not None:
312 elif self.url is not None:
312 try:
313 try:
313 import urllib2
314 import urllib2
314 response = urllib2.urlopen(self.url)
315 response = urllib2.urlopen(self.url)
315 self.data = response.read()
316 self.data = response.read()
316 # extract encoding from header, if there is one:
317 # extract encoding from header, if there is one:
317 encoding = None
318 encoding = None
318 for sub in response.headers['content-type'].split(';'):
319 for sub in response.headers['content-type'].split(';'):
319 sub = sub.strip()
320 sub = sub.strip()
320 if sub.startswith('charset'):
321 if sub.startswith('charset'):
321 encoding = sub.split('=')[-1].strip()
322 encoding = sub.split('=')[-1].strip()
322 break
323 break
323 # decode data, if an encoding was specified
324 # decode data, if an encoding was specified
324 if encoding:
325 if encoding:
325 self.data = self.data.decode(encoding, 'replace')
326 self.data = self.data.decode(encoding, 'replace')
326 except:
327 except:
327 self.data = None
328 self.data = None
328
329
329 class Pretty(DisplayObject):
330 class Pretty(DisplayObject):
330
331
331 def _repr_pretty_(self):
332 def _repr_pretty_(self):
332 return self.data
333 return self.data
333
334
334
335
335 class HTML(DisplayObject):
336 class HTML(DisplayObject):
336
337
337 def _repr_html_(self):
338 def _repr_html_(self):
338 return self.data
339 return self.data
339
340
340 def __html__(self):
341 def __html__(self):
341 """
342 """
342 This method exists to inform other HTML-using modules (e.g. Markupsafe,
343 This method exists to inform other HTML-using modules (e.g. Markupsafe,
343 htmltag, etc) that this object is HTML and does not need things like
344 htmltag, etc) that this object is HTML and does not need things like
344 special characters (<>&) escaped.
345 special characters (<>&) escaped.
345 """
346 """
346 return self._repr_html_()
347 return self._repr_html_()
347
348
348
349
349 class Math(DisplayObject):
350 class Math(DisplayObject):
350
351
351 def _repr_latex_(self):
352 def _repr_latex_(self):
352 s = self.data.strip('$')
353 s = self.data.strip('$')
353 return "$$%s$$" % s
354 return "$$%s$$" % s
354
355
355
356
356 class Latex(DisplayObject):
357 class Latex(DisplayObject):
357
358
358 def _repr_latex_(self):
359 def _repr_latex_(self):
359 return self.data
360 return self.data
360
361
361
362
362 class SVG(DisplayObject):
363 class SVG(DisplayObject):
363
364
364 # wrap data in a property, which extracts the <svg> tag, discarding
365 # wrap data in a property, which extracts the <svg> tag, discarding
365 # document headers
366 # document headers
366 _data = None
367 _data = None
367
368
368 @property
369 @property
369 def data(self):
370 def data(self):
370 return self._data
371 return self._data
371
372
372 @data.setter
373 @data.setter
373 def data(self, svg):
374 def data(self, svg):
374 if svg is None:
375 if svg is None:
375 self._data = None
376 self._data = None
376 return
377 return
377 # parse into dom object
378 # parse into dom object
378 from xml.dom import minidom
379 from xml.dom import minidom
379 x = minidom.parseString(svg)
380 x = minidom.parseString(svg)
380 # get svg tag (should be 1)
381 # get svg tag (should be 1)
381 found_svg = x.getElementsByTagName('svg')
382 found_svg = x.getElementsByTagName('svg')
382 if found_svg:
383 if found_svg:
383 svg = found_svg[0].toxml()
384 svg = found_svg[0].toxml()
384 else:
385 else:
385 # fallback on the input, trust the user
386 # fallback on the input, trust the user
386 # but this is probably an error.
387 # but this is probably an error.
387 pass
388 pass
388 self._data = svg
389 self._data = svg
389
390
390 def _repr_svg_(self):
391 def _repr_svg_(self):
391 return self.data
392 return self.data
392
393
393
394
394 class JSON(DisplayObject):
395 class JSON(DisplayObject):
395
396
396 def _repr_json_(self):
397 def _repr_json_(self):
397 return self.data
398 return self.data
398
399
399 css_t = """$("head").append($("<link/>").attr({
400 css_t = """$("head").append($("<link/>").attr({
400 rel: "stylesheet",
401 rel: "stylesheet",
401 type: "text/css",
402 type: "text/css",
402 href: "%s"
403 href: "%s"
403 }));
404 }));
404 """
405 """
405
406
406 lib_t1 = """$.getScript("%s", function () {
407 lib_t1 = """$.getScript("%s", function () {
407 """
408 """
408 lib_t2 = """});
409 lib_t2 = """});
409 """
410 """
410
411
411 class Javascript(DisplayObject):
412 class Javascript(DisplayObject):
412
413
413 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
414 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
414 """Create a Javascript display object given raw data.
415 """Create a Javascript display object given raw data.
415
416
416 When this object is returned by an expression or passed to the
417 When this object is returned by an expression or passed to the
417 display function, it will result in the data being displayed
418 display function, it will result in the data being displayed
418 in the frontend. If the data is a URL, the data will first be
419 in the frontend. If the data is a URL, the data will first be
419 downloaded and then displayed.
420 downloaded and then displayed.
420
421
421 In the Notebook, the containing element will be available as `element`,
422 In the Notebook, the containing element will be available as `element`,
422 and jQuery will be available. The output area starts hidden, so if
423 and jQuery will be available. The output area starts hidden, so if
423 the js appends content to `element` that should be visible, then
424 the js appends content to `element` that should be visible, then
424 it must call `container.show()` to unhide the area.
425 it must call `container.show()` to unhide the area.
425
426
426 Parameters
427 Parameters
427 ----------
428 ----------
428 data : unicode, str or bytes
429 data : unicode, str or bytes
429 The Javascript source code or a URL to download it from.
430 The Javascript source code or a URL to download it from.
430 url : unicode
431 url : unicode
431 A URL to download the data from.
432 A URL to download the data from.
432 filename : unicode
433 filename : unicode
433 Path to a local file to load the data from.
434 Path to a local file to load the data from.
434 lib : list or str
435 lib : list or str
435 A sequence of Javascript library URLs to load asynchronously before
436 A sequence of Javascript library URLs to load asynchronously before
436 running the source code. The full URLs of the libraries should
437 running the source code. The full URLs of the libraries should
437 be given. A single Javascript library URL can also be given as a
438 be given. A single Javascript library URL can also be given as a
438 string.
439 string.
439 css: : list or str
440 css: : list or str
440 A sequence of css files to load before running the source code.
441 A sequence of css files to load before running the source code.
441 The full URLs of the css files should be given. A single css URL
442 The full URLs of the css files should be given. A single css URL
442 can also be given as a string.
443 can also be given as a string.
443 """
444 """
444 if isinstance(lib, basestring):
445 if isinstance(lib, basestring):
445 lib = [lib]
446 lib = [lib]
446 elif lib is None:
447 elif lib is None:
447 lib = []
448 lib = []
448 if isinstance(css, basestring):
449 if isinstance(css, basestring):
449 css = [css]
450 css = [css]
450 elif css is None:
451 elif css is None:
451 css = []
452 css = []
452 if not isinstance(lib, (list,tuple)):
453 if not isinstance(lib, (list,tuple)):
453 raise TypeError('expected sequence, got: %r' % lib)
454 raise TypeError('expected sequence, got: %r' % lib)
454 if not isinstance(css, (list,tuple)):
455 if not isinstance(css, (list,tuple)):
455 raise TypeError('expected sequence, got: %r' % css)
456 raise TypeError('expected sequence, got: %r' % css)
456 self.lib = lib
457 self.lib = lib
457 self.css = css
458 self.css = css
458 super(Javascript, self).__init__(data=data, url=url, filename=filename)
459 super(Javascript, self).__init__(data=data, url=url, filename=filename)
459
460
460 def _repr_javascript_(self):
461 def _repr_javascript_(self):
461 r = ''
462 r = ''
462 for c in self.css:
463 for c in self.css:
463 r += css_t % c
464 r += css_t % c
464 for l in self.lib:
465 for l in self.lib:
465 r += lib_t1 % l
466 r += lib_t1 % l
466 r += self.data
467 r += self.data
467 r += lib_t2*len(self.lib)
468 r += lib_t2*len(self.lib)
468 return r
469 return r
469
470
470 # constants for identifying png/jpeg data
471 # constants for identifying png/jpeg data
471 _PNG = b'\x89PNG\r\n\x1a\n'
472 _PNG = b'\x89PNG\r\n\x1a\n'
472 _JPEG = b'\xff\xd8'
473 _JPEG = b'\xff\xd8'
473
474
475 def _pngxy(data):
476 """read the (width, height) from a PNG header"""
477 ihdr = data.index(b'IHDR')
478 # next 8 bytes are width/height
479 w4h4 = data[ihdr+4:ihdr+12]
480 return struct.unpack('>ii', w4h4)
481
482 def _jpegxy(data):
483 """read the (width, height) from a JPEG header"""
484 # adapted from http://www.64lines.com/jpeg-width-height
485
486 idx = 4
487 while True:
488 block_size = struct.unpack('>H', data[idx:idx+2])[0]
489 idx = idx + block_size
490 if data[idx:idx+2] == b'\xFF\xC0':
491 # found Start of Frame
492 iSOF = idx
493 break
494 else:
495 # read another block
496 idx += 2
497
498 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
499 return w, h
500
474 class Image(DisplayObject):
501 class Image(DisplayObject):
475
502
476 _read_flags = 'rb'
503 _read_flags = 'rb'
477 _FMT_JPEG = u'jpeg'
504 _FMT_JPEG = u'jpeg'
478 _FMT_PNG = u'png'
505 _FMT_PNG = u'png'
479 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
506 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
480
507
481 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None):
508 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
482 """Create a display an PNG/JPEG image given raw data.
509 """Create a display an PNG/JPEG image given raw data.
483
510
484 When this object is returned by an expression or passed to the
511 When this object is returned by an expression or passed to the
485 display function, it will result in the image being displayed
512 display function, it will result in the image being displayed
486 in the frontend.
513 in the frontend.
487
514
488 Parameters
515 Parameters
489 ----------
516 ----------
490 data : unicode, str or bytes
517 data : unicode, str or bytes
491 The raw image data or a URL or filename to load the data from.
518 The raw image data or a URL or filename to load the data from.
492 This always results in embedded image data.
519 This always results in embedded image data.
493 url : unicode
520 url : unicode
494 A URL to download the data from. If you specify `url=`,
521 A URL to download the data from. If you specify `url=`,
495 the image data will not be embedded unless you also specify `embed=True`.
522 the image data will not be embedded unless you also specify `embed=True`.
496 filename : unicode
523 filename : unicode
497 Path to a local file to load the data from.
524 Path to a local file to load the data from.
498 Images from a file are always embedded.
525 Images from a file are always embedded.
499 format : unicode
526 format : unicode
500 The format of the image data (png/jpeg/jpg). If a filename or URL is given
527 The format of the image data (png/jpeg/jpg). If a filename or URL is given
501 for format will be inferred from the filename extension.
528 for format will be inferred from the filename extension.
502 embed : bool
529 embed : bool
503 Should the image data be embedded using a data URI (True) or be
530 Should the image data be embedded using a data URI (True) or be
504 loaded using an <img> tag. Set this to True if you want the image
531 loaded using an <img> tag. Set this to True if you want the image
505 to be viewable later with no internet connection in the notebook.
532 to be viewable later with no internet connection in the notebook.
506
533
507 Default is `True`, unless the keyword argument `url` is set, then
534 Default is `True`, unless the keyword argument `url` is set, then
508 default value is `False`.
535 default value is `False`.
509
536
510 Note that QtConsole is not able to display images if `embed` is set to `False`
537 Note that QtConsole is not able to display images if `embed` is set to `False`
511 width : int
538 width : int
512 Width to which to constrain the image in html
539 Width to which to constrain the image in html
513 height : int
540 height : int
514 Height to which to constrain the image in html
541 Height to which to constrain the image in html
542 retina : bool
543 Automatically set the width and height to half of the measured
544 width and height.
545 This only works for embedded images because it reads the width/height
546 from image data.
547 For non-embedded images, you can just set the desired display width
548 and height directly.
515
549
516 Examples
550 Examples
517 --------
551 --------
518 # embedded image data, works in qtconsole and notebook
552 # embedded image data, works in qtconsole and notebook
519 # when passed positionally, the first arg can be any of raw image data,
553 # when passed positionally, the first arg can be any of raw image data,
520 # a URL, or a filename from which to load image data.
554 # a URL, or a filename from which to load image data.
521 # The result is always embedding image data for inline images.
555 # The result is always embedding image data for inline images.
522 Image('http://www.google.fr/images/srpr/logo3w.png')
556 Image('http://www.google.fr/images/srpr/logo3w.png')
523 Image('/path/to/image.jpg')
557 Image('/path/to/image.jpg')
524 Image(b'RAW_PNG_DATA...')
558 Image(b'RAW_PNG_DATA...')
525
559
526 # Specifying Image(url=...) does not embed the image data,
560 # Specifying Image(url=...) does not embed the image data,
527 # it only generates `<img>` tag with a link to the source.
561 # it only generates `<img>` tag with a link to the source.
528 # This will not work in the qtconsole or offline.
562 # This will not work in the qtconsole or offline.
529 Image(url='http://www.google.fr/images/srpr/logo3w.png')
563 Image(url='http://www.google.fr/images/srpr/logo3w.png')
530
564
531 """
565 """
532 if filename is not None:
566 if filename is not None:
533 ext = self._find_ext(filename)
567 ext = self._find_ext(filename)
534 elif url is not None:
568 elif url is not None:
535 ext = self._find_ext(url)
569 ext = self._find_ext(url)
536 elif data is None:
570 elif data is None:
537 raise ValueError("No image data found. Expecting filename, url, or data.")
571 raise ValueError("No image data found. Expecting filename, url, or data.")
538 elif isinstance(data, string_types) and (
572 elif isinstance(data, string_types) and (
539 data.startswith('http') or _safe_exists(data)
573 data.startswith('http') or _safe_exists(data)
540 ):
574 ):
541 ext = self._find_ext(data)
575 ext = self._find_ext(data)
542 else:
576 else:
543 ext = None
577 ext = None
544
578
545 if ext is not None:
579 if ext is not None:
546 format = ext.lower()
580 format = ext.lower()
547 if ext == u'jpg' or ext == u'jpeg':
581 if ext == u'jpg' or ext == u'jpeg':
548 format = self._FMT_JPEG
582 format = self._FMT_JPEG
549 if ext == u'png':
583 if ext == u'png':
550 format = self._FMT_PNG
584 format = self._FMT_PNG
551 elif isinstance(data, bytes) and format == 'png':
585 elif isinstance(data, bytes) and format == 'png':
552 # infer image type from image data header,
586 # infer image type from image data header,
553 # only if format might not have been specified.
587 # only if format might not have been specified.
554 if data[:2] == _JPEG:
588 if data[:2] == _JPEG:
555 format = 'jpeg'
589 format = 'jpeg'
556
590
557 self.format = unicode(format).lower()
591 self.format = unicode(format).lower()
558 self.embed = embed if embed is not None else (url is None)
592 self.embed = embed if embed is not None else (url is None)
559
593
560 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
594 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
561 raise ValueError("Cannot embed the '%s' image format" % (self.format))
595 raise ValueError("Cannot embed the '%s' image format" % (self.format))
562 self.width = width
596 self.width = width
563 self.height = height
597 self.height = height
598 self.retina = retina
564 super(Image, self).__init__(data=data, url=url, filename=filename)
599 super(Image, self).__init__(data=data, url=url, filename=filename)
565
600
601 if retina:
602 self._retina_shape()
603
604 def _retina_shape(self):
605 """load pixel-doubled width and height from image data"""
606 if not self.embed:
607 return
608 if self.format == 'png':
609 w, h = _pngxy(self.data)
610 elif self.format == 'jpeg':
611 w, h = _jpegxy(self.data)
612 else:
613 # retina only supports png
614 return
615 self.width = w // 2
616 self.height = h // 2
617
566 def reload(self):
618 def reload(self):
567 """Reload the raw data from file or URL."""
619 """Reload the raw data from file or URL."""
568 if self.embed:
620 if self.embed:
569 super(Image,self).reload()
621 super(Image,self).reload()
622 if self.retina:
623 self._retina_shape()
570
624
571 def _repr_html_(self):
625 def _repr_html_(self):
572 if not self.embed:
626 if not self.embed:
573 width = height = ''
627 width = height = ''
574 if self.width:
628 if self.width:
575 width = ' width="%d"' % self.width
629 width = ' width="%d"' % self.width
576 if self.height:
630 if self.height:
577 height = ' height="%d"' % self.height
631 height = ' height="%d"' % self.height
578 return u'<img src="%s"%s%s/>' % (self.url, width, height)
632 return u'<img src="%s"%s%s/>' % (self.url, width, height)
579
633
580 def _data_and_metadata(self):
634 def _data_and_metadata(self):
581 """shortcut for returning metadata with shape information, if defined"""
635 """shortcut for returning metadata with shape information, if defined"""
582 md = {}
636 md = {}
583 if self.width:
637 if self.width:
584 md['width'] = self.width
638 md['width'] = self.width
585 if self.height:
639 if self.height:
586 md['height'] = self.height
640 md['height'] = self.height
587 if md:
641 if md:
588 return self.data, md
642 return self.data, md
589 else:
643 else:
590 return self.data
644 return self.data
591
645
592 def _repr_png_(self):
646 def _repr_png_(self):
593 if self.embed and self.format == u'png':
647 if self.embed and self.format == u'png':
594 return self._data_and_metadata()
648 return self._data_and_metadata()
595
649
596 def _repr_jpeg_(self):
650 def _repr_jpeg_(self):
597 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
651 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
598 return self._data_and_metadata()
652 return self._data_and_metadata()
599
653
600 def _find_ext(self, s):
654 def _find_ext(self, s):
601 return unicode(s.split('.')[-1].lower())
655 return unicode(s.split('.')[-1].lower())
602
656
603
657
604 def clear_output(stdout=True, stderr=True, other=True):
658 def clear_output(stdout=True, stderr=True, other=True):
605 """Clear the output of the current cell receiving output.
659 """Clear the output of the current cell receiving output.
606
660
607 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
661 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
608 produced by display()) can be excluded from the clear event.
662 produced by display()) can be excluded from the clear event.
609
663
610 By default, everything is cleared.
664 By default, everything is cleared.
611
665
612 Parameters
666 Parameters
613 ----------
667 ----------
614 stdout : bool [default: True]
668 stdout : bool [default: True]
615 Whether to clear stdout.
669 Whether to clear stdout.
616 stderr : bool [default: True]
670 stderr : bool [default: True]
617 Whether to clear stderr.
671 Whether to clear stderr.
618 other : bool [default: True]
672 other : bool [default: True]
619 Whether to clear everything else that is not stdout/stderr
673 Whether to clear everything else that is not stdout/stderr
620 (e.g. figures,images,HTML, any result of display()).
674 (e.g. figures,images,HTML, any result of display()).
621 """
675 """
622 from IPython.core.interactiveshell import InteractiveShell
676 from IPython.core.interactiveshell import InteractiveShell
623 if InteractiveShell.initialized():
677 if InteractiveShell.initialized():
624 InteractiveShell.instance().display_pub.clear_output(
678 InteractiveShell.instance().display_pub.clear_output(
625 stdout=stdout, stderr=stderr, other=other,
679 stdout=stdout, stderr=stderr, other=other,
626 )
680 )
627 else:
681 else:
628 from IPython.utils import io
682 from IPython.utils import io
629 if stdout:
683 if stdout:
630 print('\033[2K\r', file=io.stdout, end='')
684 print('\033[2K\r', file=io.stdout, end='')
631 io.stdout.flush()
685 io.stdout.flush()
632 if stderr:
686 if stderr:
633 print('\033[2K\r', file=io.stderr, end='')
687 print('\033[2K\r', file=io.stderr, end='')
634 io.stderr.flush()
688 io.stderr.flush()
635
689
@@ -1,370 +1,385 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Pylab (matplotlib) support utilities.
2 """Pylab (matplotlib) support utilities.
3
3
4 Authors
4 Authors
5 -------
5 -------
6
6
7 * Fernando Perez.
7 * Fernando Perez.
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2009-2011 The IPython Development Team
12 # Copyright (C) 2009 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import sys
22 import sys
23 from io import BytesIO
23 from io import BytesIO
24
24
25 from IPython.core.display import _pngxy
25 from IPython.utils.decorators import flag_calls
26 from IPython.utils.decorators import flag_calls
26
27
27 # If user specifies a GUI, that dictates the backend, otherwise we read the
28 # If user specifies a GUI, that dictates the backend, otherwise we read the
28 # user's mpl default from the mpl rc structure
29 # user's mpl default from the mpl rc structure
29 backends = {'tk': 'TkAgg',
30 backends = {'tk': 'TkAgg',
30 'gtk': 'GTKAgg',
31 'gtk': 'GTKAgg',
31 'wx': 'WXAgg',
32 'wx': 'WXAgg',
32 'qt': 'Qt4Agg', # qt3 not supported
33 'qt': 'Qt4Agg', # qt3 not supported
33 'qt4': 'Qt4Agg',
34 'qt4': 'Qt4Agg',
34 'osx': 'MacOSX',
35 'osx': 'MacOSX',
35 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
36 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
36
37
37 # We also need a reverse backends2guis mapping that will properly choose which
38 # We also need a reverse backends2guis mapping that will properly choose which
38 # GUI support to activate based on the desired matplotlib backend. For the
39 # GUI support to activate based on the desired matplotlib backend. For the
39 # most part it's just a reverse of the above dict, but we also need to add a
40 # most part it's just a reverse of the above dict, but we also need to add a
40 # few others that map to the same GUI manually:
41 # few others that map to the same GUI manually:
41 backend2gui = dict(zip(backends.values(), backends.keys()))
42 backend2gui = dict(zip(backends.values(), backends.keys()))
42 # In the reverse mapping, there are a few extra valid matplotlib backends that
43 # In the reverse mapping, there are a few extra valid matplotlib backends that
43 # map to the same GUI support
44 # map to the same GUI support
44 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
45 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
45 backend2gui['WX'] = 'wx'
46 backend2gui['WX'] = 'wx'
46 backend2gui['CocoaAgg'] = 'osx'
47 backend2gui['CocoaAgg'] = 'osx'
47
48
48 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
49 # Matplotlib utilities
50 # Matplotlib utilities
50 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
51
52
52
53
53 def getfigs(*fig_nums):
54 def getfigs(*fig_nums):
54 """Get a list of matplotlib figures by figure numbers.
55 """Get a list of matplotlib figures by figure numbers.
55
56
56 If no arguments are given, all available figures are returned. If the
57 If no arguments are given, all available figures are returned. If the
57 argument list contains references to invalid figures, a warning is printed
58 argument list contains references to invalid figures, a warning is printed
58 but the function continues pasting further figures.
59 but the function continues pasting further figures.
59
60
60 Parameters
61 Parameters
61 ----------
62 ----------
62 figs : tuple
63 figs : tuple
63 A tuple of ints giving the figure numbers of the figures to return.
64 A tuple of ints giving the figure numbers of the figures to return.
64 """
65 """
65 from matplotlib._pylab_helpers import Gcf
66 from matplotlib._pylab_helpers import Gcf
66 if not fig_nums:
67 if not fig_nums:
67 fig_managers = Gcf.get_all_fig_managers()
68 fig_managers = Gcf.get_all_fig_managers()
68 return [fm.canvas.figure for fm in fig_managers]
69 return [fm.canvas.figure for fm in fig_managers]
69 else:
70 else:
70 figs = []
71 figs = []
71 for num in fig_nums:
72 for num in fig_nums:
72 f = Gcf.figs.get(num)
73 f = Gcf.figs.get(num)
73 if f is None:
74 if f is None:
74 print('Warning: figure %s not available.' % num)
75 print('Warning: figure %s not available.' % num)
75 else:
76 else:
76 figs.append(f.canvas.figure)
77 figs.append(f.canvas.figure)
77 return figs
78 return figs
78
79
79
80
80 def figsize(sizex, sizey):
81 def figsize(sizex, sizey):
81 """Set the default figure size to be [sizex, sizey].
82 """Set the default figure size to be [sizex, sizey].
82
83
83 This is just an easy to remember, convenience wrapper that sets::
84 This is just an easy to remember, convenience wrapper that sets::
84
85
85 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
86 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
86 """
87 """
87 import matplotlib
88 import matplotlib
88 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
89 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
89
90
90
91
91 def print_figure(fig, fmt='png'):
92 def print_figure(fig, fmt='png'):
92 """Convert a figure to svg or png for inline display."""
93 """Convert a figure to svg or png for inline display."""
94 from matplotlib import rcParams
93 # When there's an empty figure, we shouldn't return anything, otherwise we
95 # When there's an empty figure, we shouldn't return anything, otherwise we
94 # get big blank areas in the qt console.
96 # get big blank areas in the qt console.
95 if not fig.axes and not fig.lines:
97 if not fig.axes and not fig.lines:
96 return
98 return
97
99
98 fc = fig.get_facecolor()
100 fc = fig.get_facecolor()
99 ec = fig.get_edgecolor()
101 ec = fig.get_edgecolor()
100 bytes_io = BytesIO()
102 bytes_io = BytesIO()
103 dpi = rcParams['savefig.dpi']
104 if fmt == 'retina':
105 dpi = dpi * 2
106 fmt = 'png'
101 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight',
107 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight',
102 facecolor=fc, edgecolor=ec)
108 facecolor=fc, edgecolor=ec, dpi=dpi)
103 data = bytes_io.getvalue()
109 data = bytes_io.getvalue()
104 return data
110 return data
105
111
112 def retina_figure(fig):
113 """format a figure as a pixel-doubled (retina) PNG"""
114 pngdata = print_figure(fig, fmt='retina')
115 w, h = _pngxy(pngdata)
116 metadata = dict(width=w//2, height=h//2)
117 return pngdata, metadata
106
118
107 # We need a little factory function here to create the closure where
119 # We need a little factory function here to create the closure where
108 # safe_execfile can live.
120 # safe_execfile can live.
109 def mpl_runner(safe_execfile):
121 def mpl_runner(safe_execfile):
110 """Factory to return a matplotlib-enabled runner for %run.
122 """Factory to return a matplotlib-enabled runner for %run.
111
123
112 Parameters
124 Parameters
113 ----------
125 ----------
114 safe_execfile : function
126 safe_execfile : function
115 This must be a function with the same interface as the
127 This must be a function with the same interface as the
116 :meth:`safe_execfile` method of IPython.
128 :meth:`safe_execfile` method of IPython.
117
129
118 Returns
130 Returns
119 -------
131 -------
120 A function suitable for use as the ``runner`` argument of the %run magic
132 A function suitable for use as the ``runner`` argument of the %run magic
121 function.
133 function.
122 """
134 """
123
135
124 def mpl_execfile(fname,*where,**kw):
136 def mpl_execfile(fname,*where,**kw):
125 """matplotlib-aware wrapper around safe_execfile.
137 """matplotlib-aware wrapper around safe_execfile.
126
138
127 Its interface is identical to that of the :func:`execfile` builtin.
139 Its interface is identical to that of the :func:`execfile` builtin.
128
140
129 This is ultimately a call to execfile(), but wrapped in safeties to
141 This is ultimately a call to execfile(), but wrapped in safeties to
130 properly handle interactive rendering."""
142 properly handle interactive rendering."""
131
143
132 import matplotlib
144 import matplotlib
133 import matplotlib.pylab as pylab
145 import matplotlib.pylab as pylab
134
146
135 #print '*** Matplotlib runner ***' # dbg
147 #print '*** Matplotlib runner ***' # dbg
136 # turn off rendering until end of script
148 # turn off rendering until end of script
137 is_interactive = matplotlib.rcParams['interactive']
149 is_interactive = matplotlib.rcParams['interactive']
138 matplotlib.interactive(False)
150 matplotlib.interactive(False)
139 safe_execfile(fname,*where,**kw)
151 safe_execfile(fname,*where,**kw)
140 matplotlib.interactive(is_interactive)
152 matplotlib.interactive(is_interactive)
141 # make rendering call now, if the user tried to do it
153 # make rendering call now, if the user tried to do it
142 if pylab.draw_if_interactive.called:
154 if pylab.draw_if_interactive.called:
143 pylab.draw()
155 pylab.draw()
144 pylab.draw_if_interactive.called = False
156 pylab.draw_if_interactive.called = False
145
157
146 return mpl_execfile
158 return mpl_execfile
147
159
148
160
149 def select_figure_format(shell, fmt):
161 def select_figure_format(shell, fmt):
150 """Select figure format for inline backend, either 'png' or 'svg'.
162 """Select figure format for inline backend, can be 'png', 'retina', or 'svg'.
151
163
152 Using this method ensures only one figure format is active at a time.
164 Using this method ensures only one figure format is active at a time.
153 """
165 """
154 from matplotlib.figure import Figure
166 from matplotlib.figure import Figure
155 from IPython.kernel.zmq.pylab import backend_inline
167 from IPython.kernel.zmq.pylab import backend_inline
156
168
157 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
169 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
158 png_formatter = shell.display_formatter.formatters['image/png']
170 png_formatter = shell.display_formatter.formatters['image/png']
159
171
160 if fmt=='png':
172 if fmt == 'png':
161 svg_formatter.type_printers.pop(Figure, None)
173 svg_formatter.type_printers.pop(Figure, None)
162 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
174 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
175 elif fmt in ('png2x', 'retina'):
176 svg_formatter.type_printers.pop(Figure, None)
177 png_formatter.for_type(Figure, retina_figure)
163 elif fmt=='svg':
178 elif fmt == 'svg':
164 png_formatter.type_printers.pop(Figure, None)
179 png_formatter.type_printers.pop(Figure, None)
165 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
180 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
166 else:
181 else:
167 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
182 raise ValueError("supported formats are: 'png', 'retina', 'svg', not %r" % fmt)
168
183
169 # set the format to be used in the backend()
184 # set the format to be used in the backend()
170 backend_inline._figure_format = fmt
185 backend_inline._figure_format = fmt
171
186
172 #-----------------------------------------------------------------------------
187 #-----------------------------------------------------------------------------
173 # Code for initializing matplotlib and importing pylab
188 # Code for initializing matplotlib and importing pylab
174 #-----------------------------------------------------------------------------
189 #-----------------------------------------------------------------------------
175
190
176
191
177 def find_gui_and_backend(gui=None, gui_select=None):
192 def find_gui_and_backend(gui=None, gui_select=None):
178 """Given a gui string return the gui and mpl backend.
193 """Given a gui string return the gui and mpl backend.
179
194
180 Parameters
195 Parameters
181 ----------
196 ----------
182 gui : str
197 gui : str
183 Can be one of ('tk','gtk','wx','qt','qt4','inline').
198 Can be one of ('tk','gtk','wx','qt','qt4','inline').
184 gui_select : str
199 gui_select : str
185 Can be one of ('tk','gtk','wx','qt','qt4','inline').
200 Can be one of ('tk','gtk','wx','qt','qt4','inline').
186 This is any gui already selected by the shell.
201 This is any gui already selected by the shell.
187
202
188 Returns
203 Returns
189 -------
204 -------
190 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
205 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
191 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
206 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
192 """
207 """
193
208
194 import matplotlib
209 import matplotlib
195
210
196 if gui and gui != 'auto':
211 if gui and gui != 'auto':
197 # select backend based on requested gui
212 # select backend based on requested gui
198 backend = backends[gui]
213 backend = backends[gui]
199 else:
214 else:
200 backend = matplotlib.rcParams['backend']
215 backend = matplotlib.rcParams['backend']
201 # In this case, we need to find what the appropriate gui selection call
216 # In this case, we need to find what the appropriate gui selection call
202 # should be for IPython, so we can activate inputhook accordingly
217 # should be for IPython, so we can activate inputhook accordingly
203 gui = backend2gui.get(backend, None)
218 gui = backend2gui.get(backend, None)
204
219
205 # If we have already had a gui active, we need it and inline are the
220 # If we have already had a gui active, we need it and inline are the
206 # ones allowed.
221 # ones allowed.
207 if gui_select and gui != gui_select:
222 if gui_select and gui != gui_select:
208 gui = gui_select
223 gui = gui_select
209 backend = backends[gui]
224 backend = backends[gui]
210
225
211 return gui, backend
226 return gui, backend
212
227
213
228
214 def activate_matplotlib(backend):
229 def activate_matplotlib(backend):
215 """Activate the given backend and set interactive to True."""
230 """Activate the given backend and set interactive to True."""
216
231
217 import matplotlib
232 import matplotlib
218 matplotlib.interactive(True)
233 matplotlib.interactive(True)
219
234
220 # Matplotlib had a bug where even switch_backend could not force
235 # Matplotlib had a bug where even switch_backend could not force
221 # the rcParam to update. This needs to be set *before* the module
236 # the rcParam to update. This needs to be set *before* the module
222 # magic of switch_backend().
237 # magic of switch_backend().
223 matplotlib.rcParams['backend'] = backend
238 matplotlib.rcParams['backend'] = backend
224
239
225 import matplotlib.pyplot
240 import matplotlib.pyplot
226 matplotlib.pyplot.switch_backend(backend)
241 matplotlib.pyplot.switch_backend(backend)
227
242
228 # This must be imported last in the matplotlib series, after
243 # This must be imported last in the matplotlib series, after
229 # backend/interactivity choices have been made
244 # backend/interactivity choices have been made
230 import matplotlib.pylab as pylab
245 import matplotlib.pylab as pylab
231
246
232 pylab.show._needmain = False
247 pylab.show._needmain = False
233 # We need to detect at runtime whether show() is called by the user.
248 # We need to detect at runtime whether show() is called by the user.
234 # For this, we wrap it into a decorator which adds a 'called' flag.
249 # For this, we wrap it into a decorator which adds a 'called' flag.
235 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
250 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
236
251
237
252
238 def import_pylab(user_ns, import_all=True):
253 def import_pylab(user_ns, import_all=True):
239 """Import the standard pylab symbols into user_ns."""
254 """Import the standard pylab symbols into user_ns."""
240
255
241 # Import numpy as np/pyplot as plt are conventions we're trying to
256 # Import numpy as np/pyplot as plt are conventions we're trying to
242 # somewhat standardize on. Making them available to users by default
257 # somewhat standardize on. Making them available to users by default
243 # will greatly help this.
258 # will greatly help this.
244 s = ("import numpy\n"
259 s = ("import numpy\n"
245 "import matplotlib\n"
260 "import matplotlib\n"
246 "from matplotlib import pylab, mlab, pyplot\n"
261 "from matplotlib import pylab, mlab, pyplot\n"
247 "np = numpy\n"
262 "np = numpy\n"
248 "plt = pyplot\n"
263 "plt = pyplot\n"
249 )
264 )
250 exec s in user_ns
265 exec s in user_ns
251
266
252 if import_all:
267 if import_all:
253 s = ("from matplotlib.pylab import *\n"
268 s = ("from matplotlib.pylab import *\n"
254 "from numpy import *\n")
269 "from numpy import *\n")
255 exec s in user_ns
270 exec s in user_ns
256
271
257
272
258 def configure_inline_support(shell, backend, user_ns=None):
273 def configure_inline_support(shell, backend, user_ns=None):
259 """Configure an IPython shell object for matplotlib use.
274 """Configure an IPython shell object for matplotlib use.
260
275
261 Parameters
276 Parameters
262 ----------
277 ----------
263 shell : InteractiveShell instance
278 shell : InteractiveShell instance
264
279
265 backend : matplotlib backend
280 backend : matplotlib backend
266
281
267 user_ns : dict
282 user_ns : dict
268 A namespace where all configured variables will be placed. If not given,
283 A namespace where all configured variables will be placed. If not given,
269 the `user_ns` attribute of the shell object is used.
284 the `user_ns` attribute of the shell object is used.
270 """
285 """
271 # If using our svg payload backend, register the post-execution
286 # If using our svg payload backend, register the post-execution
272 # function that will pick up the results for display. This can only be
287 # function that will pick up the results for display. This can only be
273 # done with access to the real shell object.
288 # done with access to the real shell object.
274
289
275 # Note: if we can't load the inline backend, then there's no point
290 # Note: if we can't load the inline backend, then there's no point
276 # continuing (such as in terminal-only shells in environments without
291 # continuing (such as in terminal-only shells in environments without
277 # zeromq available).
292 # zeromq available).
278 try:
293 try:
279 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
294 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
280 except ImportError:
295 except ImportError:
281 return
296 return
282 from matplotlib import pyplot
297 from matplotlib import pyplot
283
298
284 user_ns = shell.user_ns if user_ns is None else user_ns
299 user_ns = shell.user_ns if user_ns is None else user_ns
285
300
286 cfg = InlineBackend.instance(config=shell.config)
301 cfg = InlineBackend.instance(config=shell.config)
287 cfg.shell = shell
302 cfg.shell = shell
288 if cfg not in shell.configurables:
303 if cfg not in shell.configurables:
289 shell.configurables.append(cfg)
304 shell.configurables.append(cfg)
290
305
291 if backend == backends['inline']:
306 if backend == backends['inline']:
292 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
307 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
293 shell.register_post_execute(flush_figures)
308 shell.register_post_execute(flush_figures)
294
309
295 # Save rcParams that will be overwrittern
310 # Save rcParams that will be overwrittern
296 shell._saved_rcParams = dict()
311 shell._saved_rcParams = dict()
297 for k in cfg.rc:
312 for k in cfg.rc:
298 shell._saved_rcParams[k] = pyplot.rcParams[k]
313 shell._saved_rcParams[k] = pyplot.rcParams[k]
299 # load inline_rc
314 # load inline_rc
300 pyplot.rcParams.update(cfg.rc)
315 pyplot.rcParams.update(cfg.rc)
301 # Add 'figsize' to pyplot and to the user's namespace
316 # Add 'figsize' to pyplot and to the user's namespace
302 user_ns['figsize'] = pyplot.figsize = figsize
317 user_ns['figsize'] = pyplot.figsize = figsize
303 else:
318 else:
304 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
319 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
305 if flush_figures in shell._post_execute:
320 if flush_figures in shell._post_execute:
306 shell._post_execute.pop(flush_figures)
321 shell._post_execute.pop(flush_figures)
307 if hasattr(shell, '_saved_rcParams'):
322 if hasattr(shell, '_saved_rcParams'):
308 pyplot.rcParams.update(shell._saved_rcParams)
323 pyplot.rcParams.update(shell._saved_rcParams)
309 del shell._saved_rcParams
324 del shell._saved_rcParams
310
325
311 # Setup the default figure format
326 # Setup the default figure format
312 fmt = cfg.figure_format
327 fmt = cfg.figure_format
313 select_figure_format(shell, fmt)
328 select_figure_format(shell, fmt)
314
329
315 # The old pastefig function has been replaced by display
330 # The old pastefig function has been replaced by display
316 from IPython.core.display import display
331 from IPython.core.display import display
317 # Add display and getfigs to the user's namespace
332 # Add display and getfigs to the user's namespace
318 user_ns['display'] = display
333 user_ns['display'] = display
319 user_ns['getfigs'] = getfigs
334 user_ns['getfigs'] = getfigs
320
335
321
336
322 def pylab_activate(user_ns, gui=None, import_all=True, shell=None, welcome_message=False):
337 def pylab_activate(user_ns, gui=None, import_all=True, shell=None, welcome_message=False):
323 """Activate pylab mode in the user's namespace.
338 """Activate pylab mode in the user's namespace.
324
339
325 Loads and initializes numpy, matplotlib and friends for interactive use.
340 Loads and initializes numpy, matplotlib and friends for interactive use.
326
341
327 Parameters
342 Parameters
328 ----------
343 ----------
329 user_ns : dict
344 user_ns : dict
330 Namespace where the imports will occur.
345 Namespace where the imports will occur.
331
346
332 gui : optional, string
347 gui : optional, string
333 A valid gui name following the conventions of the %gui magic.
348 A valid gui name following the conventions of the %gui magic.
334
349
335 import_all : optional, boolean
350 import_all : optional, boolean
336 If true, an 'import *' is done from numpy and pylab.
351 If true, an 'import *' is done from numpy and pylab.
337
352
338 welcome_message : optional, boolean
353 welcome_message : optional, boolean
339 If true, print a welcome message about pylab, which includes the backend
354 If true, print a welcome message about pylab, which includes the backend
340 being used.
355 being used.
341
356
342 Returns
357 Returns
343 -------
358 -------
344 The actual gui used (if not given as input, it was obtained from matplotlib
359 The actual gui used (if not given as input, it was obtained from matplotlib
345 itself, and will be needed next to configure IPython's gui integration.
360 itself, and will be needed next to configure IPython's gui integration.
346 """
361 """
347 pylab_gui_select = shell.pylab_gui_select if shell is not None else None
362 pylab_gui_select = shell.pylab_gui_select if shell is not None else None
348 # Try to find the appropriate gui and backend for the settings
363 # Try to find the appropriate gui and backend for the settings
349 gui, backend = find_gui_and_backend(gui, pylab_gui_select)
364 gui, backend = find_gui_and_backend(gui, pylab_gui_select)
350 if shell is not None and gui != 'inline':
365 if shell is not None and gui != 'inline':
351 # If we have our first gui selection, store it
366 # If we have our first gui selection, store it
352 if pylab_gui_select is None:
367 if pylab_gui_select is None:
353 shell.pylab_gui_select = gui
368 shell.pylab_gui_select = gui
354 # Otherwise if they are different
369 # Otherwise if they are different
355 elif gui != pylab_gui_select:
370 elif gui != pylab_gui_select:
356 print ('Warning: Cannot change to a different GUI toolkit: %s.'
371 print ('Warning: Cannot change to a different GUI toolkit: %s.'
357 ' Using %s instead.' % (gui, pylab_gui_select))
372 ' Using %s instead.' % (gui, pylab_gui_select))
358 gui, backend = find_gui_and_backend(pylab_gui_select)
373 gui, backend = find_gui_and_backend(pylab_gui_select)
359 activate_matplotlib(backend)
374 activate_matplotlib(backend)
360 import_pylab(user_ns, import_all)
375 import_pylab(user_ns, import_all)
361 if shell is not None:
376 if shell is not None:
362 configure_inline_support(shell, backend, user_ns)
377 configure_inline_support(shell, backend, user_ns)
363 if welcome_message:
378 if welcome_message:
364 print """
379 print """
365 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
380 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
366 For more information, type 'help(pylab)'.""" % backend
381 For more information, type 'help(pylab)'.""" % backend
367 # flush stdout, just to be safe
382 # flush stdout, just to be safe
368 sys.stdout.flush()
383 sys.stdout.flush()
369
384
370 return gui
385 return gui
@@ -1,38 +1,56 b''
1 #-----------------------------------------------------------------------------
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2010-2011 The IPython Development Team.
2 # Copyright (C) 2010-2011 The IPython Development Team.
3 #
3 #
4 # Distributed under the terms of the BSD License.
4 # Distributed under the terms of the BSD License.
5 #
5 #
6 # The full license is in the file COPYING.txt, distributed with this software.
6 # The full license is in the file COPYING.txt, distributed with this software.
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 import os
8 import os
9
9
10 import nose.tools as nt
10 import nose.tools as nt
11
11
12 from IPython.core import display
12 from IPython.core import display
13 from IPython.utils import path as ipath
13 from IPython.utils import path as ipath
14
14
15 def test_image_size():
15 def test_image_size():
16 """Simple test for display.Image(args, width=x,height=y)"""
16 """Simple test for display.Image(args, width=x,height=y)"""
17 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
17 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
18 img = display.Image(url=thisurl, width=200, height=200)
18 img = display.Image(url=thisurl, width=200, height=200)
19 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
19 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
20 img = display.Image(url=thisurl, width=200)
20 img = display.Image(url=thisurl, width=200)
21 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
21 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
22 img = display.Image(url=thisurl)
22 img = display.Image(url=thisurl)
23 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
23 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
24
24
25 def test_retina_png():
26 here = os.path.dirname(__file__)
27 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
28 nt.assert_equal(img.height, 1)
29 nt.assert_equal(img.width, 1)
30 data, md = img._repr_png_()
31 nt.assert_equal(md['width'], 1)
32 nt.assert_equal(md['height'], 1)
33
34 def test_retina_jpeg():
35 here = os.path.dirname(__file__)
36 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
37 nt.assert_equal(img.height, 1)
38 nt.assert_equal(img.width, 1)
39 data, md = img._repr_jpeg_()
40 nt.assert_equal(md['width'], 1)
41 nt.assert_equal(md['height'], 1)
42
25 def test_image_filename_defaults():
43 def test_image_filename_defaults():
26 '''test format constraint, and validity of jpeg and png'''
44 '''test format constraint, and validity of jpeg and png'''
27 tpath = ipath.get_ipython_package_dir()
45 tpath = ipath.get_ipython_package_dir()
28 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
46 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
29 embed=True)
47 embed=True)
30 nt.assert_raises(ValueError, display.Image)
48 nt.assert_raises(ValueError, display.Image)
31 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
49 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
32 imgfile = os.path.join(tpath, 'html/static/base/images/ipynblogo.png')
50 imgfile = os.path.join(tpath, 'html/static/base/images/ipynblogo.png')
33 img = display.Image(filename=imgfile)
51 img = display.Image(filename=imgfile)
34 nt.assert_equal('png', img.format)
52 nt.assert_equal('png', img.format)
35 nt.assert_is_not_none(img._repr_png_())
53 nt.assert_is_not_none(img._repr_png_())
36 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
54 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
37 nt.assert_equal('jpeg', img.format)
55 nt.assert_equal('jpeg', img.format)
38 nt.assert_is_none(img._repr_jpeg_())
56 nt.assert_is_none(img._repr_jpeg_())
@@ -1,215 +1,215 b''
1 """Produce SVG versions of active plots for display by the rich Qt frontend.
1 """Produce SVG versions of active plots for display by the rich Qt frontend.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 # Standard library imports
8 # Standard library imports
9 import sys
9 import sys
10
10
11 # Third-party imports
11 # Third-party imports
12 import matplotlib
12 import matplotlib
13 from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg
13 from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg
14 from matplotlib._pylab_helpers import Gcf
14 from matplotlib._pylab_helpers import Gcf
15
15
16 # Local imports.
16 # Local imports.
17 from IPython.config.configurable import SingletonConfigurable
17 from IPython.config.configurable import SingletonConfigurable
18 from IPython.core.display import display
18 from IPython.core.display import display
19 from IPython.core.displaypub import publish_display_data
19 from IPython.core.displaypub import publish_display_data
20 from IPython.core.pylabtools import print_figure, select_figure_format
20 from IPython.core.pylabtools import print_figure, select_figure_format
21 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, CBool
21 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool
22 from IPython.utils.warn import warn
22 from IPython.utils.warn import warn
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Configurable for inline backend options
25 # Configurable for inline backend options
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # inherit from InlineBackendConfig for deprecation purposes
27 # inherit from InlineBackendConfig for deprecation purposes
28 class InlineBackendConfig(SingletonConfigurable):
28 class InlineBackendConfig(SingletonConfigurable):
29 pass
29 pass
30
30
31 class InlineBackend(InlineBackendConfig):
31 class InlineBackend(InlineBackendConfig):
32 """An object to store configuration of the inline backend."""
32 """An object to store configuration of the inline backend."""
33
33
34 def _config_changed(self, name, old, new):
34 def _config_changed(self, name, old, new):
35 # warn on change of renamed config section
35 # warn on change of renamed config section
36 if new.InlineBackendConfig != old.InlineBackendConfig:
36 if new.InlineBackendConfig != old.InlineBackendConfig:
37 warn("InlineBackendConfig has been renamed to InlineBackend")
37 warn("InlineBackendConfig has been renamed to InlineBackend")
38 super(InlineBackend, self)._config_changed(name, old, new)
38 super(InlineBackend, self)._config_changed(name, old, new)
39
39
40 # The typical default figure size is too large for inline use,
40 # The typical default figure size is too large for inline use,
41 # so we shrink the figure size to 6x4, and tweak fonts to
41 # so we shrink the figure size to 6x4, and tweak fonts to
42 # make that fit.
42 # make that fit.
43 rc = Dict({'figure.figsize': (6.0,4.0),
43 rc = Dict({'figure.figsize': (6.0,4.0),
44 # play nicely with white background in the Qt and notebook frontend
44 # play nicely with white background in the Qt and notebook frontend
45 'figure.facecolor': 'white',
45 'figure.facecolor': 'white',
46 'figure.edgecolor': 'white',
46 'figure.edgecolor': 'white',
47 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
47 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
48 'font.size': 10,
48 'font.size': 10,
49 # 72 dpi matches SVG/qtconsole
49 # 72 dpi matches SVG/qtconsole
50 # this only affects PNG export, as SVG has no dpi setting
50 # this only affects PNG export, as SVG has no dpi setting
51 'savefig.dpi': 72,
51 'savefig.dpi': 72,
52 # 10pt still needs a little more room on the xlabel:
52 # 10pt still needs a little more room on the xlabel:
53 'figure.subplot.bottom' : .125
53 'figure.subplot.bottom' : .125
54 }, config=True,
54 }, config=True,
55 help="""Subset of matplotlib rcParams that should be different for the
55 help="""Subset of matplotlib rcParams that should be different for the
56 inline backend."""
56 inline backend."""
57 )
57 )
58
58
59 figure_format = CaselessStrEnum(['svg', 'png'], default_value='png', config=True,
59 figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True,
60 help="The image format for figures with the inline backend.")
60 help="The image format for figures with the inline backend.")
61
61
62 def _figure_format_changed(self, name, old, new):
62 def _figure_format_changed(self, name, old, new):
63 if self.shell is None:
63 if self.shell is None:
64 return
64 return
65 else:
65 else:
66 select_figure_format(self.shell, new)
66 select_figure_format(self.shell, new)
67
67
68 close_figures = CBool(True, config=True,
68 close_figures = Bool(True, config=True,
69 help="""Close all figures at the end of each cell.
69 help="""Close all figures at the end of each cell.
70
70
71 When True, ensures that each cell starts with no active figures, but it
71 When True, ensures that each cell starts with no active figures, but it
72 also means that one must keep track of references in order to edit or
72 also means that one must keep track of references in order to edit or
73 redraw figures in subsequent cells. This mode is ideal for the notebook,
73 redraw figures in subsequent cells. This mode is ideal for the notebook,
74 where residual plots from other cells might be surprising.
74 where residual plots from other cells might be surprising.
75
75
76 When False, one must call figure() to create new figures. This means
76 When False, one must call figure() to create new figures. This means
77 that gcf() and getfigs() can reference figures created in other cells,
77 that gcf() and getfigs() can reference figures created in other cells,
78 and the active figure can continue to be edited with pylab/pyplot
78 and the active figure can continue to be edited with pylab/pyplot
79 methods that reference the current active figure. This mode facilitates
79 methods that reference the current active figure. This mode facilitates
80 iterative editing of figures, and behaves most consistently with
80 iterative editing of figures, and behaves most consistently with
81 other matplotlib backends, but figure barriers between cells must
81 other matplotlib backends, but figure barriers between cells must
82 be explicit.
82 be explicit.
83 """)
83 """)
84
84
85 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
85 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
86
86
87
87
88 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
89 # Functions
89 # Functions
90 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
91
91
92 def show(close=None):
92 def show(close=None):
93 """Show all figures as SVG/PNG payloads sent to the IPython clients.
93 """Show all figures as SVG/PNG payloads sent to the IPython clients.
94
94
95 Parameters
95 Parameters
96 ----------
96 ----------
97 close : bool, optional
97 close : bool, optional
98 If true, a ``plt.close('all')`` call is automatically issued after
98 If true, a ``plt.close('all')`` call is automatically issued after
99 sending all the figures. If this is set, the figures will entirely
99 sending all the figures. If this is set, the figures will entirely
100 removed from the internal list of figures.
100 removed from the internal list of figures.
101 """
101 """
102 if close is None:
102 if close is None:
103 close = InlineBackend.instance().close_figures
103 close = InlineBackend.instance().close_figures
104 try:
104 try:
105 for figure_manager in Gcf.get_all_fig_managers():
105 for figure_manager in Gcf.get_all_fig_managers():
106 display(figure_manager.canvas.figure)
106 display(figure_manager.canvas.figure)
107 finally:
107 finally:
108 show._to_draw = []
108 show._to_draw = []
109 if close:
109 if close:
110 matplotlib.pyplot.close('all')
110 matplotlib.pyplot.close('all')
111
111
112
112
113
113
114 # This flag will be reset by draw_if_interactive when called
114 # This flag will be reset by draw_if_interactive when called
115 show._draw_called = False
115 show._draw_called = False
116 # list of figures to draw when flush_figures is called
116 # list of figures to draw when flush_figures is called
117 show._to_draw = []
117 show._to_draw = []
118
118
119
119
120 def draw_if_interactive():
120 def draw_if_interactive():
121 """
121 """
122 Is called after every pylab drawing command
122 Is called after every pylab drawing command
123 """
123 """
124 # signal that the current active figure should be sent at the end of
124 # signal that the current active figure should be sent at the end of
125 # execution. Also sets the _draw_called flag, signaling that there will be
125 # execution. Also sets the _draw_called flag, signaling that there will be
126 # something to send. At the end of the code execution, a separate call to
126 # something to send. At the end of the code execution, a separate call to
127 # flush_figures() will act upon these values
127 # flush_figures() will act upon these values
128
128
129 fig = Gcf.get_active().canvas.figure
129 fig = Gcf.get_active().canvas.figure
130
130
131 # Hack: matplotlib FigureManager objects in interacive backends (at least
131 # Hack: matplotlib FigureManager objects in interacive backends (at least
132 # in some of them) monkeypatch the figure object and add a .show() method
132 # in some of them) monkeypatch the figure object and add a .show() method
133 # to it. This applies the same monkeypatch in order to support user code
133 # to it. This applies the same monkeypatch in order to support user code
134 # that might expect `.show()` to be part of the official API of figure
134 # that might expect `.show()` to be part of the official API of figure
135 # objects.
135 # objects.
136 # For further reference:
136 # For further reference:
137 # https://github.com/ipython/ipython/issues/1612
137 # https://github.com/ipython/ipython/issues/1612
138 # https://github.com/matplotlib/matplotlib/issues/835
138 # https://github.com/matplotlib/matplotlib/issues/835
139
139
140 if not hasattr(fig, 'show'):
140 if not hasattr(fig, 'show'):
141 # Queue up `fig` for display
141 # Queue up `fig` for display
142 fig.show = lambda *a: display(fig)
142 fig.show = lambda *a: display(fig)
143
143
144 # If matplotlib was manually set to non-interactive mode, this function
144 # If matplotlib was manually set to non-interactive mode, this function
145 # should be a no-op (otherwise we'll generate duplicate plots, since a user
145 # should be a no-op (otherwise we'll generate duplicate plots, since a user
146 # who set ioff() manually expects to make separate draw/show calls).
146 # who set ioff() manually expects to make separate draw/show calls).
147 if not matplotlib.is_interactive():
147 if not matplotlib.is_interactive():
148 return
148 return
149
149
150 # ensure current figure will be drawn, and each subsequent call
150 # ensure current figure will be drawn, and each subsequent call
151 # of draw_if_interactive() moves the active figure to ensure it is
151 # of draw_if_interactive() moves the active figure to ensure it is
152 # drawn last
152 # drawn last
153 try:
153 try:
154 show._to_draw.remove(fig)
154 show._to_draw.remove(fig)
155 except ValueError:
155 except ValueError:
156 # ensure it only appears in the draw list once
156 # ensure it only appears in the draw list once
157 pass
157 pass
158 # Queue up the figure for drawing in next show() call
158 # Queue up the figure for drawing in next show() call
159 show._to_draw.append(fig)
159 show._to_draw.append(fig)
160 show._draw_called = True
160 show._draw_called = True
161
161
162
162
163 def flush_figures():
163 def flush_figures():
164 """Send all figures that changed
164 """Send all figures that changed
165
165
166 This is meant to be called automatically and will call show() if, during
166 This is meant to be called automatically and will call show() if, during
167 prior code execution, there had been any calls to draw_if_interactive.
167 prior code execution, there had been any calls to draw_if_interactive.
168
168
169 This function is meant to be used as a post_execute callback in IPython,
169 This function is meant to be used as a post_execute callback in IPython,
170 so user-caused errors are handled with showtraceback() instead of being
170 so user-caused errors are handled with showtraceback() instead of being
171 allowed to raise. If this function is not called from within IPython,
171 allowed to raise. If this function is not called from within IPython,
172 then these exceptions will raise.
172 then these exceptions will raise.
173 """
173 """
174 if not show._draw_called:
174 if not show._draw_called:
175 return
175 return
176
176
177 if InlineBackend.instance().close_figures:
177 if InlineBackend.instance().close_figures:
178 # ignore the tracking, just draw and close all figures
178 # ignore the tracking, just draw and close all figures
179 try:
179 try:
180 return show(True)
180 return show(True)
181 except Exception as e:
181 except Exception as e:
182 # safely show traceback if in IPython, else raise
182 # safely show traceback if in IPython, else raise
183 try:
183 try:
184 get_ipython
184 get_ipython
185 except NameError:
185 except NameError:
186 raise e
186 raise e
187 else:
187 else:
188 get_ipython().showtraceback()
188 get_ipython().showtraceback()
189 return
189 return
190 try:
190 try:
191 # exclude any figures that were closed:
191 # exclude any figures that were closed:
192 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
192 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
193 for fig in [ fig for fig in show._to_draw if fig in active ]:
193 for fig in [ fig for fig in show._to_draw if fig in active ]:
194 try:
194 try:
195 display(fig)
195 display(fig)
196 except Exception as e:
196 except Exception as e:
197 # safely show traceback if in IPython, else raise
197 # safely show traceback if in IPython, else raise
198 try:
198 try:
199 get_ipython
199 get_ipython
200 except NameError:
200 except NameError:
201 raise e
201 raise e
202 else:
202 else:
203 get_ipython().showtraceback()
203 get_ipython().showtraceback()
204 break
204 break
205 finally:
205 finally:
206 # clear flags for next round
206 # clear flags for next round
207 show._to_draw = []
207 show._to_draw = []
208 show._draw_called = False
208 show._draw_called = False
209
209
210
210
211 # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
211 # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
212 # figurecanvas. This is set here to a Agg canvas
212 # figurecanvas. This is set here to a Agg canvas
213 # See https://github.com/matplotlib/matplotlib/pull/1125
213 # See https://github.com/matplotlib/matplotlib/pull/1125
214 FigureCanvas = FigureCanvasAgg
214 FigureCanvas = FigureCanvasAgg
215
215
@@ -1,468 +1,469 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 This module defines the things that are used in setup.py for building IPython
3 This module defines the things that are used in setup.py for building IPython
4
4
5 This includes:
5 This includes:
6
6
7 * The basic arguments to setup
7 * The basic arguments to setup
8 * Functions for finding things like packages, package data, etc.
8 * Functions for finding things like packages, package data, etc.
9 * A function for checking dependencies.
9 * A function for checking dependencies.
10 """
10 """
11 from __future__ import print_function
11 from __future__ import print_function
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import os
23 import os
24 import sys
24 import sys
25
25
26 try:
26 try:
27 from configparser import ConfigParser
27 from configparser import ConfigParser
28 except:
28 except:
29 from ConfigParser import ConfigParser
29 from ConfigParser import ConfigParser
30 from distutils.command.build_py import build_py
30 from distutils.command.build_py import build_py
31 from distutils.cmd import Command
31 from distutils.cmd import Command
32 from glob import glob
32 from glob import glob
33
33
34 from setupext import install_data_ext
34 from setupext import install_data_ext
35
35
36 #-------------------------------------------------------------------------------
36 #-------------------------------------------------------------------------------
37 # Useful globals and utility functions
37 # Useful globals and utility functions
38 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
39
39
40 # A few handy globals
40 # A few handy globals
41 isfile = os.path.isfile
41 isfile = os.path.isfile
42 pjoin = os.path.join
42 pjoin = os.path.join
43 repo_root = os.path.dirname(os.path.abspath(__file__))
43 repo_root = os.path.dirname(os.path.abspath(__file__))
44
44
45 def oscmd(s):
45 def oscmd(s):
46 print(">", s)
46 print(">", s)
47 os.system(s)
47 os.system(s)
48
48
49 # Py3 compatibility hacks, without assuming IPython itself is installed with
49 # Py3 compatibility hacks, without assuming IPython itself is installed with
50 # the full py3compat machinery.
50 # the full py3compat machinery.
51
51
52 try:
52 try:
53 execfile
53 execfile
54 except NameError:
54 except NameError:
55 def execfile(fname, globs, locs=None):
55 def execfile(fname, globs, locs=None):
56 locs = locs or globs
56 locs = locs or globs
57 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
57 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
58
58
59 # A little utility we'll need below, since glob() does NOT allow you to do
59 # A little utility we'll need below, since glob() does NOT allow you to do
60 # exclusion on multiple endings!
60 # exclusion on multiple endings!
61 def file_doesnt_endwith(test,endings):
61 def file_doesnt_endwith(test,endings):
62 """Return true if test is a file and its name does NOT end with any
62 """Return true if test is a file and its name does NOT end with any
63 of the strings listed in endings."""
63 of the strings listed in endings."""
64 if not isfile(test):
64 if not isfile(test):
65 return False
65 return False
66 for e in endings:
66 for e in endings:
67 if test.endswith(e):
67 if test.endswith(e):
68 return False
68 return False
69 return True
69 return True
70
70
71 #---------------------------------------------------------------------------
71 #---------------------------------------------------------------------------
72 # Basic project information
72 # Basic project information
73 #---------------------------------------------------------------------------
73 #---------------------------------------------------------------------------
74
74
75 # release.py contains version, authors, license, url, keywords, etc.
75 # release.py contains version, authors, license, url, keywords, etc.
76 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
76 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
77
77
78 # Create a dict with the basic information
78 # Create a dict with the basic information
79 # This dict is eventually passed to setup after additional keys are added.
79 # This dict is eventually passed to setup after additional keys are added.
80 setup_args = dict(
80 setup_args = dict(
81 name = name,
81 name = name,
82 version = version,
82 version = version,
83 description = description,
83 description = description,
84 long_description = long_description,
84 long_description = long_description,
85 author = author,
85 author = author,
86 author_email = author_email,
86 author_email = author_email,
87 url = url,
87 url = url,
88 download_url = download_url,
88 download_url = download_url,
89 license = license,
89 license = license,
90 platforms = platforms,
90 platforms = platforms,
91 keywords = keywords,
91 keywords = keywords,
92 classifiers = classifiers,
92 classifiers = classifiers,
93 cmdclass = {'install_data': install_data_ext},
93 cmdclass = {'install_data': install_data_ext},
94 )
94 )
95
95
96
96
97 #---------------------------------------------------------------------------
97 #---------------------------------------------------------------------------
98 # Find packages
98 # Find packages
99 #---------------------------------------------------------------------------
99 #---------------------------------------------------------------------------
100
100
101 def find_packages():
101 def find_packages():
102 """
102 """
103 Find all of IPython's packages.
103 Find all of IPython's packages.
104 """
104 """
105 excludes = ['deathrow', 'quarantine']
105 excludes = ['deathrow', 'quarantine']
106 packages = []
106 packages = []
107 for dir,subdirs,files in os.walk('IPython'):
107 for dir,subdirs,files in os.walk('IPython'):
108 package = dir.replace(os.path.sep, '.')
108 package = dir.replace(os.path.sep, '.')
109 if any(package.startswith('IPython.'+exc) for exc in excludes):
109 if any(package.startswith('IPython.'+exc) for exc in excludes):
110 # package is to be excluded (e.g. deathrow)
110 # package is to be excluded (e.g. deathrow)
111 continue
111 continue
112 if '__init__.py' not in files:
112 if '__init__.py' not in files:
113 # not a package
113 # not a package
114 continue
114 continue
115 packages.append(package)
115 packages.append(package)
116 return packages
116 return packages
117
117
118 #---------------------------------------------------------------------------
118 #---------------------------------------------------------------------------
119 # Find package data
119 # Find package data
120 #---------------------------------------------------------------------------
120 #---------------------------------------------------------------------------
121
121
122 def find_package_data():
122 def find_package_data():
123 """
123 """
124 Find IPython's package_data.
124 Find IPython's package_data.
125 """
125 """
126 # This is not enough for these things to appear in an sdist.
126 # This is not enough for these things to appear in an sdist.
127 # We need to muck with the MANIFEST to get this to work
127 # We need to muck with the MANIFEST to get this to work
128
128
129 # exclude static things that we don't ship (e.g. mathjax)
129 # exclude static things that we don't ship (e.g. mathjax)
130 excludes = ['mathjax']
130 excludes = ['mathjax']
131
131
132 # add 'static/' prefix to exclusions, and tuplify for use in startswith
132 # add 'static/' prefix to exclusions, and tuplify for use in startswith
133 excludes = tuple([os.path.join('static', ex) for ex in excludes])
133 excludes = tuple([os.path.join('static', ex) for ex in excludes])
134
134
135 # walk notebook resources:
135 # walk notebook resources:
136 cwd = os.getcwd()
136 cwd = os.getcwd()
137 os.chdir(os.path.join('IPython', 'html'))
137 os.chdir(os.path.join('IPython', 'html'))
138 static_walk = list(os.walk('static'))
138 static_walk = list(os.walk('static'))
139 os.chdir(cwd)
139 os.chdir(cwd)
140 static_data = []
140 static_data = []
141 for parent, dirs, files in static_walk:
141 for parent, dirs, files in static_walk:
142 if parent.startswith(excludes):
142 if parent.startswith(excludes):
143 continue
143 continue
144 for f in files:
144 for f in files:
145 static_data.append(os.path.join(parent, f))
145 static_data.append(os.path.join(parent, f))
146
146
147 package_data = {
147 package_data = {
148 'IPython.config.profile' : ['README*', '*/*.py'],
148 'IPython.config.profile' : ['README*', '*/*.py'],
149 'IPython.core.tests' : ['*.png', '*.jpg'],
149 'IPython.testing' : ['*.txt'],
150 'IPython.testing' : ['*.txt'],
150 'IPython.testing.plugin' : ['*.txt'],
151 'IPython.testing.plugin' : ['*.txt'],
151 'IPython.html' : ['templates/*'] + static_data,
152 'IPython.html' : ['templates/*'] + static_data,
152 'IPython.qt.console' : ['resources/icon/*.svg'],
153 'IPython.qt.console' : ['resources/icon/*.svg'],
153 }
154 }
154 return package_data
155 return package_data
155
156
156
157
157 #---------------------------------------------------------------------------
158 #---------------------------------------------------------------------------
158 # Find data files
159 # Find data files
159 #---------------------------------------------------------------------------
160 #---------------------------------------------------------------------------
160
161
161 def make_dir_struct(tag,base,out_base):
162 def make_dir_struct(tag,base,out_base):
162 """Make the directory structure of all files below a starting dir.
163 """Make the directory structure of all files below a starting dir.
163
164
164 This is just a convenience routine to help build a nested directory
165 This is just a convenience routine to help build a nested directory
165 hierarchy because distutils is too stupid to do this by itself.
166 hierarchy because distutils is too stupid to do this by itself.
166
167
167 XXX - this needs a proper docstring!
168 XXX - this needs a proper docstring!
168 """
169 """
169
170
170 # we'll use these a lot below
171 # we'll use these a lot below
171 lbase = len(base)
172 lbase = len(base)
172 pathsep = os.path.sep
173 pathsep = os.path.sep
173 lpathsep = len(pathsep)
174 lpathsep = len(pathsep)
174
175
175 out = []
176 out = []
176 for (dirpath,dirnames,filenames) in os.walk(base):
177 for (dirpath,dirnames,filenames) in os.walk(base):
177 # we need to strip out the dirpath from the base to map it to the
178 # we need to strip out the dirpath from the base to map it to the
178 # output (installation) path. This requires possibly stripping the
179 # output (installation) path. This requires possibly stripping the
179 # path separator, because otherwise pjoin will not work correctly
180 # path separator, because otherwise pjoin will not work correctly
180 # (pjoin('foo/','/bar') returns '/bar').
181 # (pjoin('foo/','/bar') returns '/bar').
181
182
182 dp_eff = dirpath[lbase:]
183 dp_eff = dirpath[lbase:]
183 if dp_eff.startswith(pathsep):
184 if dp_eff.startswith(pathsep):
184 dp_eff = dp_eff[lpathsep:]
185 dp_eff = dp_eff[lpathsep:]
185 # The output path must be anchored at the out_base marker
186 # The output path must be anchored at the out_base marker
186 out_path = pjoin(out_base,dp_eff)
187 out_path = pjoin(out_base,dp_eff)
187 # Now we can generate the final filenames. Since os.walk only produces
188 # Now we can generate the final filenames. Since os.walk only produces
188 # filenames, we must join back with the dirpath to get full valid file
189 # filenames, we must join back with the dirpath to get full valid file
189 # paths:
190 # paths:
190 pfiles = [pjoin(dirpath,f) for f in filenames]
191 pfiles = [pjoin(dirpath,f) for f in filenames]
191 # Finally, generate the entry we need, which is a pari of (output
192 # Finally, generate the entry we need, which is a pari of (output
192 # path, files) for use as a data_files parameter in install_data.
193 # path, files) for use as a data_files parameter in install_data.
193 out.append((out_path, pfiles))
194 out.append((out_path, pfiles))
194
195
195 return out
196 return out
196
197
197
198
198 def find_data_files():
199 def find_data_files():
199 """
200 """
200 Find IPython's data_files.
201 Find IPython's data_files.
201
202
202 Most of these are docs.
203 Most of these are docs.
203 """
204 """
204
205
205 docdirbase = pjoin('share', 'doc', 'ipython')
206 docdirbase = pjoin('share', 'doc', 'ipython')
206 manpagebase = pjoin('share', 'man', 'man1')
207 manpagebase = pjoin('share', 'man', 'man1')
207
208
208 # Simple file lists can be made by hand
209 # Simple file lists can be made by hand
209 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
210 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
210 if not manpages:
211 if not manpages:
211 # When running from a source tree, the manpages aren't gzipped
212 # When running from a source tree, the manpages aren't gzipped
212 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
213 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
213
214
214 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
215 igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)]
215
216
216 # For nested structures, use the utility above
217 # For nested structures, use the utility above
217 example_files = make_dir_struct(
218 example_files = make_dir_struct(
218 'data',
219 'data',
219 pjoin('docs','examples'),
220 pjoin('docs','examples'),
220 pjoin(docdirbase,'examples')
221 pjoin(docdirbase,'examples')
221 )
222 )
222 manual_files = make_dir_struct(
223 manual_files = make_dir_struct(
223 'data',
224 'data',
224 pjoin('docs','html'),
225 pjoin('docs','html'),
225 pjoin(docdirbase,'manual')
226 pjoin(docdirbase,'manual')
226 )
227 )
227
228
228 # And assemble the entire output list
229 # And assemble the entire output list
229 data_files = [ (manpagebase, manpages),
230 data_files = [ (manpagebase, manpages),
230 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
231 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
231 ] + manual_files + example_files
232 ] + manual_files + example_files
232
233
233 return data_files
234 return data_files
234
235
235
236
236 def make_man_update_target(manpage):
237 def make_man_update_target(manpage):
237 """Return a target_update-compliant tuple for the given manpage.
238 """Return a target_update-compliant tuple for the given manpage.
238
239
239 Parameters
240 Parameters
240 ----------
241 ----------
241 manpage : string
242 manpage : string
242 Name of the manpage, must include the section number (trailing number).
243 Name of the manpage, must include the section number (trailing number).
243
244
244 Example
245 Example
245 -------
246 -------
246
247
247 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
248 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
248 ('docs/man/ipython.1.gz',
249 ('docs/man/ipython.1.gz',
249 ['docs/man/ipython.1'],
250 ['docs/man/ipython.1'],
250 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
251 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
251 """
252 """
252 man_dir = pjoin('docs', 'man')
253 man_dir = pjoin('docs', 'man')
253 manpage_gz = manpage + '.gz'
254 manpage_gz = manpage + '.gz'
254 manpath = pjoin(man_dir, manpage)
255 manpath = pjoin(man_dir, manpage)
255 manpath_gz = pjoin(man_dir, manpage_gz)
256 manpath_gz = pjoin(man_dir, manpage_gz)
256 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
257 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
257 locals() )
258 locals() )
258 return (manpath_gz, [manpath], gz_cmd)
259 return (manpath_gz, [manpath], gz_cmd)
259
260
260 # The two functions below are copied from IPython.utils.path, so we don't need
261 # The two functions below are copied from IPython.utils.path, so we don't need
261 # to import IPython during setup, which fails on Python 3.
262 # to import IPython during setup, which fails on Python 3.
262
263
263 def target_outdated(target,deps):
264 def target_outdated(target,deps):
264 """Determine whether a target is out of date.
265 """Determine whether a target is out of date.
265
266
266 target_outdated(target,deps) -> 1/0
267 target_outdated(target,deps) -> 1/0
267
268
268 deps: list of filenames which MUST exist.
269 deps: list of filenames which MUST exist.
269 target: single filename which may or may not exist.
270 target: single filename which may or may not exist.
270
271
271 If target doesn't exist or is older than any file listed in deps, return
272 If target doesn't exist or is older than any file listed in deps, return
272 true, otherwise return false.
273 true, otherwise return false.
273 """
274 """
274 try:
275 try:
275 target_time = os.path.getmtime(target)
276 target_time = os.path.getmtime(target)
276 except os.error:
277 except os.error:
277 return 1
278 return 1
278 for dep in deps:
279 for dep in deps:
279 dep_time = os.path.getmtime(dep)
280 dep_time = os.path.getmtime(dep)
280 if dep_time > target_time:
281 if dep_time > target_time:
281 #print "For target",target,"Dep failed:",dep # dbg
282 #print "For target",target,"Dep failed:",dep # dbg
282 #print "times (dep,tar):",dep_time,target_time # dbg
283 #print "times (dep,tar):",dep_time,target_time # dbg
283 return 1
284 return 1
284 return 0
285 return 0
285
286
286
287
287 def target_update(target,deps,cmd):
288 def target_update(target,deps,cmd):
288 """Update a target with a given command given a list of dependencies.
289 """Update a target with a given command given a list of dependencies.
289
290
290 target_update(target,deps,cmd) -> runs cmd if target is outdated.
291 target_update(target,deps,cmd) -> runs cmd if target is outdated.
291
292
292 This is just a wrapper around target_outdated() which calls the given
293 This is just a wrapper around target_outdated() which calls the given
293 command if target is outdated."""
294 command if target is outdated."""
294
295
295 if target_outdated(target,deps):
296 if target_outdated(target,deps):
296 os.system(cmd)
297 os.system(cmd)
297
298
298 #---------------------------------------------------------------------------
299 #---------------------------------------------------------------------------
299 # Find scripts
300 # Find scripts
300 #---------------------------------------------------------------------------
301 #---------------------------------------------------------------------------
301
302
302 def find_scripts(entry_points=False, suffix=''):
303 def find_scripts(entry_points=False, suffix=''):
303 """Find IPython's scripts.
304 """Find IPython's scripts.
304
305
305 if entry_points is True:
306 if entry_points is True:
306 return setuptools entry_point-style definitions
307 return setuptools entry_point-style definitions
307 else:
308 else:
308 return file paths of plain scripts [default]
309 return file paths of plain scripts [default]
309
310
310 suffix is appended to script names if entry_points is True, so that the
311 suffix is appended to script names if entry_points is True, so that the
311 Python 3 scripts get named "ipython3" etc.
312 Python 3 scripts get named "ipython3" etc.
312 """
313 """
313 if entry_points:
314 if entry_points:
314 console_scripts = [s % suffix for s in [
315 console_scripts = [s % suffix for s in [
315 'ipython%s = IPython.terminal.ipapp:launch_new_instance',
316 'ipython%s = IPython.terminal.ipapp:launch_new_instance',
316 'pycolor%s = IPython.utils.PyColorize:main',
317 'pycolor%s = IPython.utils.PyColorize:main',
317 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
318 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
318 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
319 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
319 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
320 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance',
320 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
321 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
321 'iptest%s = IPython.testing.iptest:main',
322 'iptest%s = IPython.testing.iptest:main',
322 'irunner%s = IPython.lib.irunner:main'
323 'irunner%s = IPython.lib.irunner:main'
323 ]]
324 ]]
324 gui_scripts = []
325 gui_scripts = []
325 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
326 scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts)
326 else:
327 else:
327 parallel_scripts = pjoin('IPython','parallel','scripts')
328 parallel_scripts = pjoin('IPython','parallel','scripts')
328 main_scripts = pjoin('IPython','scripts')
329 main_scripts = pjoin('IPython','scripts')
329 scripts = [
330 scripts = [
330 pjoin(parallel_scripts, 'ipengine'),
331 pjoin(parallel_scripts, 'ipengine'),
331 pjoin(parallel_scripts, 'ipcontroller'),
332 pjoin(parallel_scripts, 'ipcontroller'),
332 pjoin(parallel_scripts, 'ipcluster'),
333 pjoin(parallel_scripts, 'ipcluster'),
333 pjoin(parallel_scripts, 'iplogger'),
334 pjoin(parallel_scripts, 'iplogger'),
334 pjoin(main_scripts, 'ipython'),
335 pjoin(main_scripts, 'ipython'),
335 pjoin(main_scripts, 'pycolor'),
336 pjoin(main_scripts, 'pycolor'),
336 pjoin(main_scripts, 'irunner'),
337 pjoin(main_scripts, 'irunner'),
337 pjoin(main_scripts, 'iptest')
338 pjoin(main_scripts, 'iptest')
338 ]
339 ]
339 return scripts
340 return scripts
340
341
341 #---------------------------------------------------------------------------
342 #---------------------------------------------------------------------------
342 # Verify all dependencies
343 # Verify all dependencies
343 #---------------------------------------------------------------------------
344 #---------------------------------------------------------------------------
344
345
345 def check_for_dependencies():
346 def check_for_dependencies():
346 """Check for IPython's dependencies.
347 """Check for IPython's dependencies.
347
348
348 This function should NOT be called if running under setuptools!
349 This function should NOT be called if running under setuptools!
349 """
350 """
350 from setupext.setupext import (
351 from setupext.setupext import (
351 print_line, print_raw, print_status,
352 print_line, print_raw, print_status,
352 check_for_sphinx, check_for_pygments,
353 check_for_sphinx, check_for_pygments,
353 check_for_nose, check_for_pexpect,
354 check_for_nose, check_for_pexpect,
354 check_for_pyzmq, check_for_readline
355 check_for_pyzmq, check_for_readline
355 )
356 )
356 print_line()
357 print_line()
357 print_raw("BUILDING IPYTHON")
358 print_raw("BUILDING IPYTHON")
358 print_status('python', sys.version)
359 print_status('python', sys.version)
359 print_status('platform', sys.platform)
360 print_status('platform', sys.platform)
360 if sys.platform == 'win32':
361 if sys.platform == 'win32':
361 print_status('Windows version', sys.getwindowsversion())
362 print_status('Windows version', sys.getwindowsversion())
362
363
363 print_raw("")
364 print_raw("")
364 print_raw("OPTIONAL DEPENDENCIES")
365 print_raw("OPTIONAL DEPENDENCIES")
365
366
366 check_for_sphinx()
367 check_for_sphinx()
367 check_for_pygments()
368 check_for_pygments()
368 check_for_nose()
369 check_for_nose()
369 check_for_pexpect()
370 check_for_pexpect()
370 check_for_pyzmq()
371 check_for_pyzmq()
371 check_for_readline()
372 check_for_readline()
372
373
373 #---------------------------------------------------------------------------
374 #---------------------------------------------------------------------------
374 # VCS related
375 # VCS related
375 #---------------------------------------------------------------------------
376 #---------------------------------------------------------------------------
376
377
377 # utils.submodule has checks for submodule status
378 # utils.submodule has checks for submodule status
378 execfile(pjoin('IPython','utils','submodule.py'), globals())
379 execfile(pjoin('IPython','utils','submodule.py'), globals())
379
380
380 class UpdateSubmodules(Command):
381 class UpdateSubmodules(Command):
381 """Update git submodules
382 """Update git submodules
382
383
383 IPython's external javascript dependencies live in a separate repo.
384 IPython's external javascript dependencies live in a separate repo.
384 """
385 """
385 description = "Update git submodules"
386 description = "Update git submodules"
386 user_options = []
387 user_options = []
387
388
388 def initialize_options(self):
389 def initialize_options(self):
389 pass
390 pass
390
391
391 def finalize_options(self):
392 def finalize_options(self):
392 pass
393 pass
393
394
394 def run(self):
395 def run(self):
395 failure = False
396 failure = False
396 try:
397 try:
397 self.spawn('git submodule init'.split())
398 self.spawn('git submodule init'.split())
398 self.spawn('git submodule update --recursive'.split())
399 self.spawn('git submodule update --recursive'.split())
399 except Exception as e:
400 except Exception as e:
400 failure = e
401 failure = e
401 print(e)
402 print(e)
402
403
403 if not check_submodule_status(repo_root) == 'clean':
404 if not check_submodule_status(repo_root) == 'clean':
404 print("submodules could not be checked out")
405 print("submodules could not be checked out")
405 sys.exit(1)
406 sys.exit(1)
406
407
407
408
408 def git_prebuild(pkg_dir, build_cmd=build_py):
409 def git_prebuild(pkg_dir, build_cmd=build_py):
409 """Return extended build or sdist command class for recording commit
410 """Return extended build or sdist command class for recording commit
410
411
411 records git commit in IPython.utils._sysinfo.commit
412 records git commit in IPython.utils._sysinfo.commit
412
413
413 for use in IPython.utils.sysinfo.sys_info() calls after installation.
414 for use in IPython.utils.sysinfo.sys_info() calls after installation.
414
415
415 Also ensures that submodules exist prior to running
416 Also ensures that submodules exist prior to running
416 """
417 """
417
418
418 class MyBuildPy(build_cmd):
419 class MyBuildPy(build_cmd):
419 ''' Subclass to write commit data into installation tree '''
420 ''' Subclass to write commit data into installation tree '''
420 def run(self):
421 def run(self):
421 build_cmd.run(self)
422 build_cmd.run(self)
422 # this one will only fire for build commands
423 # this one will only fire for build commands
423 if hasattr(self, 'build_lib'):
424 if hasattr(self, 'build_lib'):
424 self._record_commit(self.build_lib)
425 self._record_commit(self.build_lib)
425
426
426 def make_release_tree(self, base_dir, files):
427 def make_release_tree(self, base_dir, files):
427 # this one will fire for sdist
428 # this one will fire for sdist
428 build_cmd.make_release_tree(self, base_dir, files)
429 build_cmd.make_release_tree(self, base_dir, files)
429 self._record_commit(base_dir)
430 self._record_commit(base_dir)
430
431
431 def _record_commit(self, base_dir):
432 def _record_commit(self, base_dir):
432 import subprocess
433 import subprocess
433 proc = subprocess.Popen('git rev-parse --short HEAD',
434 proc = subprocess.Popen('git rev-parse --short HEAD',
434 stdout=subprocess.PIPE,
435 stdout=subprocess.PIPE,
435 stderr=subprocess.PIPE,
436 stderr=subprocess.PIPE,
436 shell=True)
437 shell=True)
437 repo_commit, _ = proc.communicate()
438 repo_commit, _ = proc.communicate()
438 repo_commit = repo_commit.strip().decode("ascii")
439 repo_commit = repo_commit.strip().decode("ascii")
439
440
440 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
441 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
441 if os.path.isfile(out_pth) and not repo_commit:
442 if os.path.isfile(out_pth) and not repo_commit:
442 # nothing to write, don't clobber
443 # nothing to write, don't clobber
443 return
444 return
444
445
445 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
446 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
446
447
447 # remove to avoid overwriting original via hard link
448 # remove to avoid overwriting original via hard link
448 try:
449 try:
449 os.remove(out_pth)
450 os.remove(out_pth)
450 except (IOError, OSError):
451 except (IOError, OSError):
451 pass
452 pass
452 with open(out_pth, 'w') as out_file:
453 with open(out_pth, 'w') as out_file:
453 out_file.writelines([
454 out_file.writelines([
454 '# GENERATED BY setup.py\n',
455 '# GENERATED BY setup.py\n',
455 'commit = "%s"\n' % repo_commit,
456 'commit = "%s"\n' % repo_commit,
456 ])
457 ])
457 return require_submodules(MyBuildPy)
458 return require_submodules(MyBuildPy)
458
459
459
460
460 def require_submodules(command):
461 def require_submodules(command):
461 """decorator for instructing a command to check for submodules before running"""
462 """decorator for instructing a command to check for submodules before running"""
462 class DecoratedCommand(command):
463 class DecoratedCommand(command):
463 def run(self):
464 def run(self):
464 if not check_submodule_status(repo_root) == 'clean':
465 if not check_submodule_status(repo_root) == 'clean':
465 print("submodules missing! Run `setup.py submodule` and try again")
466 print("submodules missing! Run `setup.py submodule` and try again")
466 sys.exit(1)
467 sys.exit(1)
467 command.run(self)
468 command.run(self)
468 return DecoratedCommand
469 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now