From a03020c537a48be28af2c20e8eb7a5c8b0991e5b 2017-08-18 12:47:17 From: Min RK Date: 2017-08-18 12:47:17 Subject: [PATCH] implement GIF support without registering a new formatter we shouldn't need a new formatter ever again now that we have `_repr_mimebundle_` --- diff --git a/IPython/core/display.py b/IPython/core/display.py index 1333bff..daf5e80 100644 --- a/IPython/core/display.py +++ b/IPython/core/display.py @@ -23,7 +23,7 @@ from IPython.utils.py3compat import cast_unicode from IPython.testing.skipdoctest import skip_doctest __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown', -'display_svg', 'display_png', 'display_jpeg', 'display_gif', 'display_latex', 'display_json', +'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json', 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject', 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'GeoJSON', 'Javascript', 'Image', 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close', @@ -86,18 +86,7 @@ def publish_display_data(data, metadata=None, source=None, *, transient=None, ** See the ``display_data`` message in the messaging documentation for more details about this message type. - The following MIME types are currently implemented: - - * text/plain - * text/html - * text/markdown - * text/latex - * application/json - * application/javascript - * image/png - * image/jpeg - * image/gif - * image/svg+xml + Keys of data and metadata can be any mime-type. Parameters ---------- @@ -257,11 +246,11 @@ def display(*objs, include=None, exclude=None, metadata=None, transient=None, di - `_repr_json_`: return a JSONable dict - `_repr_jpeg_`: return raw JPEG data - `_repr_png_`: return raw PNG data - - `_repr_gif_`: return raw GIF data - `_repr_svg_`: return raw SVG data as a string - `_repr_latex_`: return LaTeX commands in a string surrounded by "$". - `_repr_mimebundle_`: return a full mimebundle containing the mapping - from all mimetypes to data + from all mimetypes to data. + Use this for any mime-type not listed above. When you are directly writing your own classes, you can adapt them for display in IPython by following the above approach. But in practice, you @@ -496,22 +485,6 @@ def display_jpeg(*objs, **kwargs): Metadata to be associated with the specific mimetype output. """ _display_mimetype('image/jpeg', objs, **kwargs) - -def display_gif(*objs, **kwargs): - """Display the GIF representation of an object. - - Parameters - ---------- - objs : tuple of objects - The Python objects to display, or if raw=True raw gif data to - display. - raw : bool - Are the data objects raw data or Python objects that need to be - formatted before display? [default: False] - metadata : dict (optional) - Metadata to be associated with the specific mimetype output. - """ - _display_mimetype('image/gif', objs, **kwargs) def display_latex(*objs, **kwargs): @@ -984,11 +957,12 @@ def _jpegxy(data): idx += 2 return struct.unpack('>HH', data[iSOF+5:iSOF+9]) - + def _gifxy(data): """read the (width, height) from a GIF header""" return struct.unpack('jpeg format = self._FMT_JPEG - + self.format = format.lower() self.embed = embed if embed is not None else (url is None) if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS: raise ValueError("Cannot embed the '%s' image format" % (self.format)) + if self.embed: + self._mimetype = self._MIMETYPES.get(self.format) + self.width = width self.height = height self.retina = retina @@ -1119,6 +1101,7 @@ class Image(DisplayObject): if retina: self._retina_shape() + def _retina_shape(self): """load pixel-doubled width and height from image data""" if not self.embed: @@ -1158,7 +1141,21 @@ class Image(DisplayObject): klass=klass, ) - def _data_and_metadata(self): + def _repr_mimebundle_(self, include=None, exclude=None): + """Return the image as a mimebundle + + Any new mimetype support should be implemented here. + """ + if self.embed: + mimetype = self._mimetype + data, metadata = self._data_and_metadata(always_both=True) + if metadata: + metadata = {mimetype: metadata} + return {mimetype: data}, metadata + else: + return {'text/html': self._repr_html_()} + + def _data_and_metadata(self, always_both=False): """shortcut for returning metadata with shape information, if defined""" md = {} if self.metadata: @@ -1169,7 +1166,7 @@ class Image(DisplayObject): md['height'] = self.height if self.unconfined: md['unconfined'] = self.unconfined - if md: + if md or always_both: return self.data, md else: return self.data @@ -1179,16 +1176,13 @@ class Image(DisplayObject): return self._data_and_metadata() def _repr_jpeg_(self): - if self.embed and (self.format == self._FMT_JPEG or self.format == u'jpg'): - return self._data_and_metadata() - - def _repr_gif_(self): - if self.embed and self.format == self._FMT_GIF: + if self.embed and self.format == self._FMT_JPEG: return self._data_and_metadata() def _find_ext(self, s): return s.split('.')[-1].lower() + class Video(DisplayObject): def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None): @@ -1287,15 +1281,6 @@ class Video(DisplayObject): # TODO pass - def _repr_png_(self): - # TODO - pass - def _repr_jpeg_(self): - # TODO - pass - def _repr_gif_(self): - # TODO - pass def clear_output(wait=False): """Clear the output of the current cell receiving output. diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index 12294db..83f5d11 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -74,7 +74,6 @@ class DisplayFormatter(Configurable): MarkdownFormatter, SVGFormatter, PNGFormatter, - GIFFormatter, PDFFormatter, JPEGFormatter, LatexFormatter, @@ -780,24 +779,6 @@ class JPEGFormatter(BaseFormatter): _return_type = (bytes, str) -class GIFFormatter(BaseFormatter): - """A PNG formatter. - - To define the callables that compute the GIF representation of your - objects, define a :meth:`_repr_gif_` method or use the :meth:`for_type` - or :meth:`for_type_by_name` methods to register functions that handle - this. - - The return value of this formatter should be raw GIF data, *not* - base64 encoded. - """ - format_type = Unicode('image/gif') - - print_method = ObjectName('_repr_gif_') - - _return_type = (bytes, str) - - class LatexFormatter(BaseFormatter): """A LaTeX formatter. @@ -977,10 +958,7 @@ class MimeBundleFormatter(BaseFormatter): method = get_real_method(obj, self.print_method) if method is not None: - d = {} - d['include'] = include - d['exclude'] = exclude - return method(**d) + return method(include=include, exclude=exclude) return None else: return None @@ -992,7 +970,6 @@ FormatterABC.register(HTMLFormatter) FormatterABC.register(MarkdownFormatter) FormatterABC.register(SVGFormatter) FormatterABC.register(PNGFormatter) -FormatterABC.register(GIFFormatter) FormatterABC.register(PDFFormatter) FormatterABC.register(JPEGFormatter) FormatterABC.register(LatexFormatter) @@ -1007,19 +984,6 @@ def format_display_data(obj, include=None, exclude=None): By default all format types will be computed. - The following MIME types are currently implemented: - - * text/plain - * text/html - * text/markdown - * text/latex - * application/json - * application/javascript - * application/pdf - * image/png - * image/jpeg - * image/svg+xml - Parameters ---------- obj : object diff --git a/IPython/core/tests/test_display.py b/IPython/core/tests/test_display.py index 5e3b37f..7357ba5 100644 --- a/IPython/core/tests/test_display.py +++ b/IPython/core/tests/test_display.py @@ -32,6 +32,15 @@ def test_image_size(): nt.assert_equal(u'' % (thisurl), img._repr_html_()) +def test_image_mimes(): + fmt = get_ipython().display_formatter.format + for format in display.Image._ACCEPTABLE_EMBEDDINGS: + mime = display.Image._MIMETYPES[format] + img = display.Image(b'garbage', format=format) + data, metadata = fmt(img) + nt.assert_equal(sorted(data), sorted([mime, 'text/plain'])) + + def test_geojson(): gj = display.GeoJSON(data={ @@ -361,3 +370,4 @@ def test_display_handle(): }, 'update': True, }) + diff --git a/IPython/utils/capture.py b/IPython/utils/capture.py index 629d2be..7ce527a 100644 --- a/IPython/utils/capture.py +++ b/IPython/utils/capture.py @@ -30,7 +30,10 @@ class RichOutput(object): return data, self.metadata[mime] else: return data - + + def _repr_mimebundle_(self, include=None, exclude=None): + return self.data, self.metadata + def _repr_html_(self): return self._repr_mime_("text/html") @@ -48,9 +51,6 @@ class RichOutput(object): def _repr_jpeg_(self): return self._repr_mime_("image/jpeg") - - def _repr_gif_(self): - return self._repr_mime_("image/gif") def _repr_svg_(self): return self._repr_mime_("image/svg+xml")