Show More
@@ -54,6 +54,9 b' class Adapter(object):' | |||||
54 | handler = getattr(self, header['msg_type'], None) |
|
54 | handler = getattr(self, header['msg_type'], None) | |
55 | if handler is None: |
|
55 | if handler is None: | |
56 | return msg |
|
56 | return msg | |
|
57 | # no transform for status=error | |||
|
58 | if msg['content'].get('status', None) in {'error', 'aborted'}: | |||
|
59 | return msg | |||
57 | return handler(msg) |
|
60 | return handler(msg) | |
58 |
|
61 | |||
59 | def _version_str_to_list(version): |
|
62 | def _version_str_to_list(version): | |
@@ -62,7 +65,7 b' def _version_str_to_list(version):' | |||||
62 | non-int segments are excluded |
|
65 | non-int segments are excluded | |
63 | """ |
|
66 | """ | |
64 | v = [] |
|
67 | v = [] | |
65 |
for part in |
|
68 | for part in version.split('.'): | |
66 | try: |
|
69 | try: | |
67 | v.append(int(part)) |
|
70 | v.append(int(part)) | |
68 | except ValueError: |
|
71 | except ValueError: | |
@@ -121,7 +124,7 b' class V5toV4(Adapter):' | |||||
121 | new_content = msg['content'] = {} |
|
124 | new_content = msg['content'] = {} | |
122 | new_content['text'] = '' |
|
125 | new_content['text'] = '' | |
123 | new_content['line'] = line |
|
126 | new_content['line'] = line | |
124 |
new_content['blo |
|
127 | new_content['block'] = None | |
125 | new_content['cursor_pos'] = cursor_pos |
|
128 | new_content['cursor_pos'] = cursor_pos | |
126 | return msg |
|
129 | return msg | |
127 |
|
130 | |||
@@ -138,10 +141,10 b' class V5toV4(Adapter):' | |||||
138 | content = msg['content'] |
|
141 | content = msg['content'] | |
139 | code = content['code'] |
|
142 | code = content['code'] | |
140 | cursor_pos = content['cursor_pos'] |
|
143 | cursor_pos = content['cursor_pos'] | |
141 |
line, |
|
144 | line, _ = code_to_line(code, cursor_pos) | |
142 |
|
145 | |||
143 | new_content = msg['content'] = {} |
|
146 | new_content = msg['content'] = {'status' : 'ok'} | |
144 | new_content['name'] = token_at_cursor(code, cursor_pos) |
|
147 | new_content['oname'] = token_at_cursor(code, cursor_pos) | |
145 | new_content['detail_level'] = content['detail_level'] |
|
148 | new_content['detail_level'] = content['detail_level'] | |
146 | return msg |
|
149 | return msg | |
147 |
|
150 | |||
@@ -155,6 +158,13 b' class V5toV4(Adapter):' | |||||
155 | def display_data(self, msg): |
|
158 | def display_data(self, msg): | |
156 | content = msg['content'] |
|
159 | content = msg['content'] | |
157 | content.setdefault("source", "display") |
|
160 | content.setdefault("source", "display") | |
|
161 | data = content['data'] | |||
|
162 | if 'application/json' in data: | |||
|
163 | try: | |||
|
164 | data['application/json'] = json.dumps(data['application/json']) | |||
|
165 | except Exception: | |||
|
166 | # warn? | |||
|
167 | pass | |||
158 | return msg |
|
168 | return msg | |
159 |
|
169 | |||
160 | # stdin channel |
|
170 | # stdin channel | |
@@ -219,25 +229,45 b' class V4toV5(Adapter):' | |||||
219 | def complete_reply(self, msg): |
|
229 | def complete_reply(self, msg): | |
220 | # TODO: complete_reply needs more context than we have |
|
230 | # TODO: complete_reply needs more context than we have | |
221 | # Maybe strip common prefix and give magic cursor_start = cursor_end = 0? |
|
231 | # Maybe strip common prefix and give magic cursor_start = cursor_end = 0? | |
222 |
content = msg['content'] |
|
232 | content = msg['content'] | |
223 | content['matches'] = [] |
|
233 | new_content = msg['content'] = {'status' : 'ok'} | |
224 | content['cursor_start'] = content['cursor_end'] = 0 |
|
234 | n = len(content['matched_text']) | |
225 | content['metadata'] = {} |
|
235 | new_content['matches'] = [ m[n:] for m in content['matches'] ] | |
|
236 | new_content['cursor_start'] = new_content['cursor_end'] = 0 | |||
|
237 | new_content['metadata'] = {} | |||
226 | return msg |
|
238 | return msg | |
227 |
|
239 | |||
228 | def inspect_request(self, msg): |
|
240 | def inspect_request(self, msg): | |
229 | content = msg['content'] |
|
241 | content = msg['content'] | |
230 | name = content['name'] |
|
242 | name = content['oname'] | |
231 |
|
243 | |||
232 | new_content = msg['content'] = {} |
|
244 | new_content = msg['content'] = {} | |
233 | new_content['code'] = name |
|
245 | new_content['code'] = name | |
234 |
new_content['cursor_pos'] = len(name) |
|
246 | new_content['cursor_pos'] = len(name) | |
235 | new_content['detail_level'] = content['detail_level'] |
|
247 | new_content['detail_level'] = content['detail_level'] | |
236 | return msg |
|
248 | return msg | |
237 |
|
249 | |||
238 | def inspect_reply(self, msg): |
|
250 | def inspect_reply(self, msg): | |
239 | """inspect_reply can't be easily backward compatible""" |
|
251 | """inspect_reply can't be easily backward compatible""" | |
240 | msg['content'] = {'found' : False, 'name' : 'unknown'} |
|
252 | content = msg['content'] | |
|
253 | new_content = msg['content'] = {'status' : 'ok'} | |||
|
254 | found = new_content['found'] = content['found'] | |||
|
255 | new_content['name'] = content['name'] | |||
|
256 | new_content['data'] = data = {} | |||
|
257 | new_content['metadata'] = {} | |||
|
258 | if found: | |||
|
259 | lines = [] | |||
|
260 | for key in ('call_def', 'init_definition', 'definition'): | |||
|
261 | if content.get(key, False): | |||
|
262 | lines.append(content[key]) | |||
|
263 | break | |||
|
264 | for key in ('call_docstring', 'init_docstring', 'docstring'): | |||
|
265 | if content.get(key, False): | |||
|
266 | lines.append(content[key]) | |||
|
267 | break | |||
|
268 | if not lines: | |||
|
269 | lines.append("<empty docstring>") | |||
|
270 | data['text/plain'] = '\n'.join(lines) | |||
241 | return msg |
|
271 | return msg | |
242 |
|
272 | |||
243 | # iopub channel |
|
273 | # iopub channel | |
@@ -247,7 +277,11 b' class V4toV5(Adapter):' | |||||
247 | content.pop("source", None) |
|
277 | content.pop("source", None) | |
248 | data = content['data'] |
|
278 | data = content['data'] | |
249 | if 'application/json' in data: |
|
279 | if 'application/json' in data: | |
250 | data['application/json'] = json.dumps(data['application/json']) |
|
280 | try: | |
|
281 | data['application/json'] = json.loads(data['application/json']) | |||
|
282 | except Exception: | |||
|
283 | # warn? | |||
|
284 | pass | |||
251 | return msg |
|
285 | return msg | |
252 |
|
286 | |||
253 | # stdin channel |
|
287 | # stdin channel |
@@ -4,7 +4,7 b'' | |||||
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import copy |
|
6 | import copy | |
7 |
|
7 | import json | ||
8 | from unittest import TestCase |
|
8 | from unittest import TestCase | |
9 | import nose.tools as nt |
|
9 | import nose.tools as nt | |
10 |
|
10 | |||
@@ -18,13 +18,12 b' def test_default_version():' | |||||
18 | msg['header'].pop('version') |
|
18 | msg['header'].pop('version') | |
19 | original = copy.deepcopy(msg) |
|
19 | original = copy.deepcopy(msg) | |
20 | adapted = adapt(original) |
|
20 | adapted = adapt(original) | |
21 |
nt.assert_equal(adapted['header'] |
|
21 | nt.assert_equal(adapted['header']['version'], V4toV5.version) | |
22 |
|
22 | |||
23 |
|
23 | |||
24 | class AdapterTest(TestCase): |
|
24 | class AdapterTest(TestCase): | |
25 |
|
25 | |||
26 | def setUp(self): |
|
26 | def setUp(self): | |
27 | print("hi") |
|
|||
28 | self.session = Session() |
|
27 | self.session = Session() | |
29 |
|
28 | |||
30 | def adapt(self, msg, version=None): |
|
29 | def adapt(self, msg, version=None): | |
@@ -85,25 +84,90 b' class V4toV5TestCase(AdapterTest):' | |||||
85 | v4c = v4['content'] |
|
84 | v4c = v4['content'] | |
86 | v5c = v5['content'] |
|
85 | v5c = v5['content'] | |
87 | self.assertEqual(v5c['user_expressions'], {'a' : 'apple', 'b': 'b'}) |
|
86 | self.assertEqual(v5c['user_expressions'], {'a' : 'apple', 'b': 'b'}) | |
88 |
|
|
87 | self.assertNotIn('user_variables', v5c) | |
89 | self.assertEqual(v5c['code'], v4c['code']) |
|
88 | self.assertEqual(v5c['code'], v4c['code']) | |
90 |
|
89 | |||
91 | def test_complete_request(self): |
|
90 | def test_complete_request(self): | |
92 | pass |
|
91 | msg = self.msg("complete_request", { | |
|
92 | 'text' : 'a.is', | |||
|
93 | 'line' : 'foo = a.is', | |||
|
94 | 'block' : None, | |||
|
95 | 'cursor_pos' : 10, | |||
|
96 | }) | |||
|
97 | v4, v5 = self.adapt(msg) | |||
|
98 | v4c = v4['content'] | |||
|
99 | v5c = v5['content'] | |||
|
100 | for key in ('text', 'line', 'block'): | |||
|
101 | self.assertNotIn(key, v5c) | |||
|
102 | self.assertEqual(v5c['cursor_pos'], v4c['cursor_pos']) | |||
|
103 | self.assertEqual(v5c['code'], v4c['line']) | |||
93 |
|
104 | |||
94 | def test_complete_reply(self): |
|
105 | def test_complete_reply(self): | |
95 | pass |
|
106 | msg = self.msg("complete_reply", { | |
|
107 | 'matched_text' : 'a.is', | |||
|
108 | 'matches' : ['a.isalnum', | |||
|
109 | 'a.isalpha', | |||
|
110 | 'a.isdigit', | |||
|
111 | 'a.islower', | |||
|
112 | ], | |||
|
113 | }) | |||
|
114 | v4, v5 = self.adapt(msg) | |||
|
115 | v4c = v4['content'] | |||
|
116 | v5c = v5['content'] | |||
|
117 | n = len(v4c['matched_text']) | |||
|
118 | expected_matches = [ m[n:] for m in v4c['matches'] ] | |||
|
119 | ||||
|
120 | self.assertEqual(v5c['matches'], expected_matches) | |||
|
121 | self.assertEqual(v5c['metadata'], {}) | |||
|
122 | self.assertEqual(v5c['cursor_start'], 0) | |||
|
123 | self.assertEqual(v5c['cursor_end'], 0) | |||
96 |
|
124 | |||
97 | def test_object_info_request(self): |
|
125 | def test_object_info_request(self): | |
98 | pass |
|
126 | msg = self.msg("object_info_request", { | |
|
127 | 'oname' : 'foo', | |||
|
128 | 'detail_level' : 1, | |||
|
129 | }) | |||
|
130 | v4, v5 = self.adapt(msg) | |||
|
131 | self.assertEqual(v5['header']['msg_type'], 'inspect_request') | |||
|
132 | v4c = v4['content'] | |||
|
133 | v5c = v5['content'] | |||
|
134 | self.assertEqual(v5c['code'], v4c['oname']) | |||
|
135 | self.assertEqual(v5c['cursor_pos'], len(v4c['oname'])) | |||
|
136 | self.assertEqual(v5c['detail_level'], v4c['detail_level']) | |||
99 |
|
137 | |||
100 | def test_object_info_reply(self): |
|
138 | def test_object_info_reply(self): | |
101 | pass |
|
139 | msg = self.msg("object_info_reply", { | |
|
140 | 'name' : 'foo', | |||
|
141 | 'found' : True, | |||
|
142 | 'status' : 'ok', | |||
|
143 | 'definition' : 'foo(a=5)', | |||
|
144 | 'docstring' : "the docstring", | |||
|
145 | }) | |||
|
146 | v4, v5 = self.adapt(msg) | |||
|
147 | self.assertEqual(v5['header']['msg_type'], 'inspect_reply') | |||
|
148 | v4c = v4['content'] | |||
|
149 | v5c = v5['content'] | |||
|
150 | self.assertEqual(sorted(v5c), [ 'data', 'found', 'metadata', 'name', 'status']) | |||
|
151 | text = v5c['data']['text/plain'] | |||
|
152 | self.assertEqual(text, '\n'.join([v4c['definition'], v4c['docstring']])) | |||
102 |
|
153 | |||
103 | # iopub channel |
|
154 | # iopub channel | |
104 |
|
155 | |||
105 | def test_display_data(self): |
|
156 | def test_display_data(self): | |
106 | pass |
|
157 | jsondata = dict(a=5) | |
|
158 | msg = self.msg("display_data", { | |||
|
159 | 'data' : { | |||
|
160 | 'text/plain' : 'some text', | |||
|
161 | 'application/json' : json.dumps(jsondata) | |||
|
162 | }, | |||
|
163 | 'metadata' : {'text/plain' : { 'key' : 'value' }}, | |||
|
164 | }) | |||
|
165 | v4, v5 = self.adapt(msg) | |||
|
166 | v4c = v4['content'] | |||
|
167 | v5c = v5['content'] | |||
|
168 | self.assertEqual(v5c['metadata'], v4c['metadata']) | |||
|
169 | self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain']) | |||
|
170 | self.assertEqual(v5c['data']['application/json'], jsondata) | |||
107 |
|
171 | |||
108 | # stdin channel |
|
172 | # stdin channel | |
109 |
|
173 | |||
@@ -162,21 +226,84 b' class V5toV4TestCase(AdapterTest):' | |||||
162 | self.assertEqual(v5c['code'], v4c['code']) |
|
226 | self.assertEqual(v5c['code'], v4c['code']) | |
163 |
|
227 | |||
164 | def test_complete_request(self): |
|
228 | def test_complete_request(self): | |
165 | pass |
|
229 | msg = self.msg("complete_request", { | |
|
230 | 'code' : 'def foo():\n' | |||
|
231 | ' a.is\n' | |||
|
232 | 'foo()', | |||
|
233 | 'cursor_pos': 19, | |||
|
234 | }) | |||
|
235 | v5, v4 = self.adapt(msg) | |||
|
236 | v4c = v4['content'] | |||
|
237 | v5c = v5['content'] | |||
|
238 | self.assertNotIn('code', v4c) | |||
|
239 | self.assertEqual(v4c['line'], v5c['code'].splitlines(True)[1]) | |||
|
240 | self.assertEqual(v4c['cursor_pos'], 8) | |||
|
241 | self.assertEqual(v4c['text'], '') | |||
|
242 | self.assertEqual(v4c['block'], None) | |||
166 |
|
243 | |||
167 | def test_complete_reply(self): |
|
244 | def test_complete_reply(self): | |
168 | pass |
|
245 | msg = self.msg("complete_reply", { | |
|
246 | 'cursor_start' : 10, | |||
|
247 | 'cursor_end' : 14, | |||
|
248 | 'matches' : ['a.isalnum', | |||
|
249 | 'a.isalpha', | |||
|
250 | 'a.isdigit', | |||
|
251 | 'a.islower', | |||
|
252 | ], | |||
|
253 | 'metadata' : {}, | |||
|
254 | }) | |||
|
255 | v5, v4 = self.adapt(msg) | |||
|
256 | v4c = v4['content'] | |||
|
257 | v5c = v5['content'] | |||
|
258 | self.assertEqual(v4c['matched_text'], 'a.is') | |||
|
259 | self.assertEqual(v4c['matches'], v5c['matches']) | |||
169 |
|
260 | |||
170 | def test_inspect_request(self): |
|
261 | def test_inspect_request(self): | |
171 | pass |
|
262 | msg = self.msg("inspect_request", { | |
|
263 | 'code' : 'def foo():\n' | |||
|
264 | ' apple\n' | |||
|
265 | 'bar()', | |||
|
266 | 'cursor_pos': 18, | |||
|
267 | 'detail_level' : 1, | |||
|
268 | }) | |||
|
269 | v5, v4 = self.adapt(msg) | |||
|
270 | self.assertEqual(v4['header']['msg_type'], 'object_info_request') | |||
|
271 | v4c = v4['content'] | |||
|
272 | v5c = v5['content'] | |||
|
273 | self.assertEqual(v4c['oname'], 'apple') | |||
|
274 | self.assertEqual(v5c['detail_level'], v4c['detail_level']) | |||
172 |
|
275 | |||
173 | def test_inspect_reply(self): |
|
276 | def test_inspect_reply(self): | |
174 | pass |
|
277 | msg = self.msg("inspect_reply", { | |
|
278 | 'name' : 'foo', | |||
|
279 | 'found' : True, | |||
|
280 | 'data' : {'text/plain' : 'some text'}, | |||
|
281 | 'metadata' : {}, | |||
|
282 | }) | |||
|
283 | v5, v4 = self.adapt(msg) | |||
|
284 | self.assertEqual(v4['header']['msg_type'], 'object_info_reply') | |||
|
285 | v4c = v4['content'] | |||
|
286 | v5c = v5['content'] | |||
|
287 | self.assertEqual(sorted(v4c), ['found', 'name']) | |||
|
288 | self.assertEqual(v4c['found'], False) | |||
175 |
|
289 | |||
176 | # iopub channel |
|
290 | # iopub channel | |
177 |
|
291 | |||
178 | def test_display_data(self): |
|
292 | def test_display_data(self): | |
179 | pass |
|
293 | jsondata = dict(a=5) | |
|
294 | msg = self.msg("display_data", { | |||
|
295 | 'data' : { | |||
|
296 | 'text/plain' : 'some text', | |||
|
297 | 'application/json' : jsondata, | |||
|
298 | }, | |||
|
299 | 'metadata' : {'text/plain' : { 'key' : 'value' }}, | |||
|
300 | }) | |||
|
301 | v5, v4 = self.adapt(msg) | |||
|
302 | v4c = v4['content'] | |||
|
303 | v5c = v5['content'] | |||
|
304 | self.assertEqual(v5c['metadata'], v4c['metadata']) | |||
|
305 | self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain']) | |||
|
306 | self.assertEqual(v4c['data']['application/json'], json.dumps(jsondata)) | |||
180 |
|
307 | |||
181 | # stdin channel |
|
308 | # stdin channel | |
182 |
|
309 |
General Comments 0
You need to be logged in to leave comments.
Login now