Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0014248

Browse files
committed
move _encode_binary to jsonutil.encode_images
And fix identification of PNG/JPEG data, which could be double-encoded when using a JSON library that decodes to str instead of unicode (simplejson 2.6, for instance). Changes are tested.
1 parent bb4488a commit 0014248

4 files changed

Lines changed: 57 additions & 18 deletions

File tree

IPython/utils/jsonutil.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import re
1515
import sys
1616
import types
17+
from base64 import encodestring
1718
from datetime import datetime
1819

1920
from IPython.utils import py3compat
@@ -89,6 +90,24 @@ def date_default(obj):
8990
raise TypeError("%r is not JSON serializable"%obj)
9091

9192

93+
# constants for identifying png/jpeg data
94+
PNG = b'\x89PNG\r\n\x1a\n'
95+
JPEG = b'\xff\xd8'
96+
97+
def encode_images(format_dict):
98+
"""b64-encodes images in a displaypub format dict
99+
100+
Perhaps this should be handled in json_clean itself?
101+
"""
102+
encoded = format_dict.copy()
103+
pngdata = format_dict.get('image/png')
104+
if isinstance(pngdata, bytes) and pngdata[:8] == PNG:
105+
encoded['image/png'] = encodestring(pngdata).decode('ascii')
106+
jpegdata = format_dict.get('image/jpeg')
107+
if isinstance(jpegdata, bytes) and jpegdata[:2] == JPEG:
108+
encoded['image/jpeg'] = encodestring(jpegdata).decode('ascii')
109+
return encoded
110+
92111

93112
def json_clean(obj):
94113
"""Clean an object to ensure it's safe to encode in JSON.

IPython/utils/tests/test_jsonutil.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
#-----------------------------------------------------------------------------
1313
# stdlib
1414
import json
15+
from base64 import decodestring
1516

1617
# third party
1718
import nose.tools as nt
1819

1920
# our own
20-
from ..jsonutil import json_clean
21+
from IPython.testing import decorators as dec
22+
from ..jsonutil import json_clean, encode_images
23+
from ..py3compat import unicode_to_str, str_to_bytes
2124

2225
#-----------------------------------------------------------------------------
2326
# Test functions
@@ -56,6 +59,35 @@ def test():
5659
json.loads(json.dumps(out))
5760

5861

62+
63+
@dec.parametric
64+
def test_encode_images():
65+
# invalid data, but the header and footer are from real files
66+
pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
67+
jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9'
68+
69+
fmt = {
70+
'image/png' : pngdata,
71+
'image/jpeg' : jpegdata,
72+
}
73+
encoded = encode_images(fmt)
74+
for key, value in fmt.iteritems():
75+
# encoded has unicode, want bytes
76+
decoded = decodestring(encoded[key].encode('ascii'))
77+
yield nt.assert_equal(decoded, value)
78+
encoded2 = encode_images(encoded)
79+
yield nt.assert_equal(encoded, encoded2)
80+
81+
b64_str = {}
82+
for key, encoded in encoded.iteritems():
83+
b64_str[key] = unicode_to_str(encoded)
84+
encoded3 = encode_images(b64_str)
85+
yield nt.assert_equal(encoded3, b64_str)
86+
for key, value in fmt.iteritems():
87+
# encoded3 has str, want bytes
88+
decoded = decodestring(str_to_bytes(encoded3[key]))
89+
yield nt.assert_equal(decoded, value)
90+
5991
def test_lambda():
6092
jc = json_clean(lambda : 1)
6193
assert isinstance(jc, str)

IPython/zmq/displayhook.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import __builtin__
22
import sys
3-
from base64 import encodestring
43

54
from IPython.core.displayhook import DisplayHook
5+
from IPython.utils.jsonutil import encode_images
66
from IPython.utils.traitlets import Instance, Dict
77
from session import extract_header, Session
88

@@ -30,18 +30,6 @@ def set_parent(self, parent):
3030
self.parent_header = extract_header(parent)
3131

3232

33-
def _encode_binary(format_dict):
34-
encoded = format_dict.copy()
35-
pngdata = format_dict.get('image/png')
36-
if isinstance(pngdata, bytes):
37-
encoded['image/png'] = encodestring(pngdata).decode('ascii')
38-
jpegdata = format_dict.get('image/jpeg')
39-
if isinstance(jpegdata, bytes):
40-
encoded['image/jpeg'] = encodestring(jpegdata).decode('ascii')
41-
42-
return encoded
43-
44-
4533
class ZMQShellDisplayHook(DisplayHook):
4634
"""A displayhook subclass that publishes data using ZeroMQ. This is intended
4735
to work with an InteractiveShell instance. It sends a dict of different
@@ -64,7 +52,7 @@ def write_output_prompt(self):
6452
self.msg['content']['execution_count'] = self.prompt_count
6553

6654
def write_format_data(self, format_dict):
67-
self.msg['content']['data'] = _encode_binary(format_dict)
55+
self.msg['content']['data'] = encode_images(format_dict)
6856

6957
def finish_displayhook(self):
7058
"""Finish up all displayhook activities."""

IPython/zmq/zmqshell.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@
3838
)
3939
from IPython.testing.skipdoctest import skip_doctest
4040
from IPython.utils import io
41-
from IPython.utils.jsonutil import json_clean
41+
from IPython.utils.jsonutil import json_clean, encode_images
4242
from IPython.utils.process import arg_split
4343
from IPython.utils import py3compat
4444
from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
4545
from IPython.utils.warn import warn, error
46-
from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
46+
from IPython.zmq.displayhook import ZMQShellDisplayHook
4747
from IPython.zmq.session import extract_header
4848
from session import Session
4949

@@ -75,7 +75,7 @@ def publish(self, source, data, metadata=None):
7575
self._validate_data(source, data, metadata)
7676
content = {}
7777
content['source'] = source
78-
content['data'] = _encode_binary(data)
78+
content['data'] = encode_images(data)
7979
content['metadata'] = metadata
8080
self.session.send(
8181
self.pub_socket, u'display_data', json_clean(content),

0 commit comments

Comments
 (0)