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