Show More
@@ -14,6 +14,7 b'' | |||||
14 | import re |
|
14 | import re | |
15 | import sys |
|
15 | import sys | |
16 | import types |
|
16 | import types | |
|
17 | from base64 import encodestring | |||
17 | from datetime import datetime |
|
18 | from datetime import datetime | |
18 |
|
19 | |||
19 | from IPython.utils import py3compat |
|
20 | from IPython.utils import py3compat | |
@@ -89,6 +90,39 b' def date_default(obj):' | |||||
89 | raise TypeError("%r is not JSON serializable"%obj) |
|
90 | raise TypeError("%r is not JSON serializable"%obj) | |
90 |
|
91 | |||
91 |
|
92 | |||
|
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 | Parameters | |||
|
103 | ---------- | |||
|
104 | ||||
|
105 | format_dict : dict | |||
|
106 | A dictionary of display data keyed by mime-type | |||
|
107 | ||||
|
108 | Returns | |||
|
109 | ------- | |||
|
110 | ||||
|
111 | format_dict : dict | |||
|
112 | A copy of the same dictionary, | |||
|
113 | but binary image data ('image/png' or 'image/jpeg') | |||
|
114 | is base64-encoded. | |||
|
115 | ||||
|
116 | """ | |||
|
117 | encoded = format_dict.copy() | |||
|
118 | pngdata = format_dict.get('image/png') | |||
|
119 | if isinstance(pngdata, bytes) and pngdata[:8] == PNG: | |||
|
120 | encoded['image/png'] = encodestring(pngdata).decode('ascii') | |||
|
121 | jpegdata = format_dict.get('image/jpeg') | |||
|
122 | if isinstance(jpegdata, bytes) and jpegdata[:2] == JPEG: | |||
|
123 | encoded['image/jpeg'] = encodestring(jpegdata).decode('ascii') | |||
|
124 | return encoded | |||
|
125 | ||||
92 |
|
126 | |||
93 | def json_clean(obj): |
|
127 | def json_clean(obj): | |
94 | """Clean an object to ensure it's safe to encode in JSON. |
|
128 | """Clean an object to ensure it's safe to encode in JSON. |
@@ -12,12 +12,15 b'' | |||||
12 | #----------------------------------------------------------------------------- |
|
12 | #----------------------------------------------------------------------------- | |
13 | # stdlib |
|
13 | # stdlib | |
14 | import json |
|
14 | import json | |
|
15 | from base64 import decodestring | |||
15 |
|
16 | |||
16 | # third party |
|
17 | # third party | |
17 | import nose.tools as nt |
|
18 | import nose.tools as nt | |
18 |
|
19 | |||
19 | # our own |
|
20 | # 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 | |||
21 |
|
24 | |||
22 | #----------------------------------------------------------------------------- |
|
25 | #----------------------------------------------------------------------------- | |
23 | # Test functions |
|
26 | # Test functions | |
@@ -56,6 +59,35 b' def test():' | |||||
56 | json.loads(json.dumps(out)) |
|
59 | json.loads(json.dumps(out)) | |
57 |
|
60 | |||
58 |
|
61 | |||
|
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 | ||||
59 | def test_lambda(): |
|
91 | def test_lambda(): | |
60 | jc = json_clean(lambda : 1) |
|
92 | jc = json_clean(lambda : 1) | |
61 | assert isinstance(jc, str) |
|
93 | assert isinstance(jc, str) |
@@ -1,8 +1,8 b'' | |||||
1 | import __builtin__ |
|
1 | import __builtin__ | |
2 | import sys |
|
2 | import sys | |
3 | from base64 import encodestring |
|
|||
4 |
|
3 | |||
5 | from IPython.core.displayhook import DisplayHook |
|
4 | from IPython.core.displayhook import DisplayHook | |
|
5 | from IPython.utils.jsonutil import encode_images | |||
6 | from IPython.utils.traitlets import Instance, Dict |
|
6 | from IPython.utils.traitlets import Instance, Dict | |
7 | from session import extract_header, Session |
|
7 | from session import extract_header, Session | |
8 |
|
8 | |||
@@ -30,18 +30,6 b' class ZMQDisplayHook(object):' | |||||
30 | self.parent_header = extract_header(parent) |
|
30 | self.parent_header = extract_header(parent) | |
31 |
|
31 | |||
32 |
|
32 | |||
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 |
|
||||
45 | class ZMQShellDisplayHook(DisplayHook): |
|
33 | class ZMQShellDisplayHook(DisplayHook): | |
46 | """A displayhook subclass that publishes data using ZeroMQ. This is intended |
|
34 | """A displayhook subclass that publishes data using ZeroMQ. This is intended | |
47 | to work with an InteractiveShell instance. It sends a dict of different |
|
35 | to work with an InteractiveShell instance. It sends a dict of different | |
@@ -64,7 +52,7 b' class ZMQShellDisplayHook(DisplayHook):' | |||||
64 | self.msg['content']['execution_count'] = self.prompt_count |
|
52 | self.msg['content']['execution_count'] = self.prompt_count | |
65 |
|
53 | |||
66 | def write_format_data(self, format_dict): |
|
54 | def write_format_data(self, format_dict): | |
67 |
self.msg['content']['data'] = |
|
55 | self.msg['content']['data'] = encode_images(format_dict) | |
68 |
|
56 | |||
69 | def finish_displayhook(self): |
|
57 | def finish_displayhook(self): | |
70 | """Finish up all displayhook activities.""" |
|
58 | """Finish up all displayhook activities.""" |
@@ -38,12 +38,12 b' from IPython.lib.kernel import (' | |||||
38 | ) |
|
38 | ) | |
39 | from IPython.testing.skipdoctest import skip_doctest |
|
39 | from IPython.testing.skipdoctest import skip_doctest | |
40 | from IPython.utils import io |
|
40 | from IPython.utils import io | |
41 | from IPython.utils.jsonutil import json_clean |
|
41 | from IPython.utils.jsonutil import json_clean, encode_images | |
42 | from IPython.utils.process import arg_split |
|
42 | from IPython.utils.process import arg_split | |
43 | from IPython.utils import py3compat |
|
43 | from IPython.utils import py3compat | |
44 | from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes |
|
44 | from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes | |
45 | from IPython.utils.warn import warn, error |
|
45 | from IPython.utils.warn import warn, error | |
46 |
from IPython.zmq.displayhook import ZMQShellDisplayHook |
|
46 | from IPython.zmq.displayhook import ZMQShellDisplayHook | |
47 | from IPython.zmq.session import extract_header |
|
47 | from IPython.zmq.session import extract_header | |
48 | from session import Session |
|
48 | from session import Session | |
49 |
|
49 | |||
@@ -75,7 +75,7 b' class ZMQDisplayPublisher(DisplayPublisher):' | |||||
75 | self._validate_data(source, data, metadata) |
|
75 | self._validate_data(source, data, metadata) | |
76 | content = {} |
|
76 | content = {} | |
77 | content['source'] = source |
|
77 | content['source'] = source | |
78 |
content['data'] = |
|
78 | content['data'] = encode_images(data) | |
79 | content['metadata'] = metadata |
|
79 | content['metadata'] = metadata | |
80 | self.session.send( |
|
80 | self.session.send( | |
81 | self.pub_socket, u'display_data', json_clean(content), |
|
81 | self.pub_socket, u'display_data', json_clean(content), |
General Comments 0
You need to be logged in to leave comments.
Login now