##// END OF EJS Templates
Fix code_to_line when called with empty code...
Thomas Kluyver -
Show More
@@ -1,354 +1,356 b''
1 """Adapters for IPython msg spec versions."""
1 """Adapters for IPython msg spec versions."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import json
6 import json
7
7
8 from IPython.core.release import kernel_protocol_version_info
8 from IPython.core.release import kernel_protocol_version_info
9 from IPython.utils.tokenutil import token_at_cursor
9 from IPython.utils.tokenutil import token_at_cursor
10
10
11
11
12 def code_to_line(code, cursor_pos):
12 def code_to_line(code, cursor_pos):
13 """Turn a multiline code block and cursor position into a single line
13 """Turn a multiline code block and cursor position into a single line
14 and new cursor position.
14 and new cursor position.
15
15
16 For adapting ``complete_`` and ``object_info_request``.
16 For adapting ``complete_`` and ``object_info_request``.
17 """
17 """
18 if not code:
19 return "", 0
18 for line in code.splitlines(True):
20 for line in code.splitlines(True):
19 n = len(line)
21 n = len(line)
20 if cursor_pos > n:
22 if cursor_pos > n:
21 cursor_pos -= n
23 cursor_pos -= n
22 else:
24 else:
23 break
25 break
24 return line, cursor_pos
26 return line, cursor_pos
25
27
26
28
27 class Adapter(object):
29 class Adapter(object):
28 """Base class for adapting messages
30 """Base class for adapting messages
29
31
30 Override message_type(msg) methods to create adapters.
32 Override message_type(msg) methods to create adapters.
31 """
33 """
32
34
33 msg_type_map = {}
35 msg_type_map = {}
34
36
35 def update_header(self, msg):
37 def update_header(self, msg):
36 return msg
38 return msg
37
39
38 def update_metadata(self, msg):
40 def update_metadata(self, msg):
39 return msg
41 return msg
40
42
41 def update_msg_type(self, msg):
43 def update_msg_type(self, msg):
42 header = msg['header']
44 header = msg['header']
43 msg_type = header['msg_type']
45 msg_type = header['msg_type']
44 if msg_type in self.msg_type_map:
46 if msg_type in self.msg_type_map:
45 msg['msg_type'] = header['msg_type'] = self.msg_type_map[msg_type]
47 msg['msg_type'] = header['msg_type'] = self.msg_type_map[msg_type]
46 return msg
48 return msg
47
49
48 def handle_reply_status_error(self, msg):
50 def handle_reply_status_error(self, msg):
49 """This will be called *instead of* the regular handler
51 """This will be called *instead of* the regular handler
50
52
51 on any reply with status != ok
53 on any reply with status != ok
52 """
54 """
53 return msg
55 return msg
54
56
55 def __call__(self, msg):
57 def __call__(self, msg):
56 msg = self.update_header(msg)
58 msg = self.update_header(msg)
57 msg = self.update_metadata(msg)
59 msg = self.update_metadata(msg)
58 msg = self.update_msg_type(msg)
60 msg = self.update_msg_type(msg)
59 header = msg['header']
61 header = msg['header']
60
62
61 handler = getattr(self, header['msg_type'], None)
63 handler = getattr(self, header['msg_type'], None)
62 if handler is None:
64 if handler is None:
63 return msg
65 return msg
64
66
65 # handle status=error replies separately (no change, at present)
67 # handle status=error replies separately (no change, at present)
66 if msg['content'].get('status', None) in {'error', 'aborted'}:
68 if msg['content'].get('status', None) in {'error', 'aborted'}:
67 return self.handle_reply_status_error(msg)
69 return self.handle_reply_status_error(msg)
68 return handler(msg)
70 return handler(msg)
69
71
70 def _version_str_to_list(version):
72 def _version_str_to_list(version):
71 """convert a version string to a list of ints
73 """convert a version string to a list of ints
72
74
73 non-int segments are excluded
75 non-int segments are excluded
74 """
76 """
75 v = []
77 v = []
76 for part in version.split('.'):
78 for part in version.split('.'):
77 try:
79 try:
78 v.append(int(part))
80 v.append(int(part))
79 except ValueError:
81 except ValueError:
80 pass
82 pass
81 return v
83 return v
82
84
83 class V5toV4(Adapter):
85 class V5toV4(Adapter):
84 """Adapt msg protocol v5 to v4"""
86 """Adapt msg protocol v5 to v4"""
85
87
86 version = '4.1'
88 version = '4.1'
87
89
88 msg_type_map = {
90 msg_type_map = {
89 'execute_result' : 'pyout',
91 'execute_result' : 'pyout',
90 'execute_input' : 'pyin',
92 'execute_input' : 'pyin',
91 'error' : 'pyerr',
93 'error' : 'pyerr',
92 'inspect_request' : 'object_info_request',
94 'inspect_request' : 'object_info_request',
93 'inspect_reply' : 'object_info_reply',
95 'inspect_reply' : 'object_info_reply',
94 }
96 }
95
97
96 def update_header(self, msg):
98 def update_header(self, msg):
97 msg['header'].pop('version', None)
99 msg['header'].pop('version', None)
98 return msg
100 return msg
99
101
100 # shell channel
102 # shell channel
101
103
102 def kernel_info_reply(self, msg):
104 def kernel_info_reply(self, msg):
103 content = msg['content']
105 content = msg['content']
104 content.pop('banner', None)
106 content.pop('banner', None)
105 for key in ('language_version', 'protocol_version'):
107 for key in ('language_version', 'protocol_version'):
106 if key in content:
108 if key in content:
107 content[key] = _version_str_to_list(content[key])
109 content[key] = _version_str_to_list(content[key])
108 if content.pop('implementation', '') == 'ipython' \
110 if content.pop('implementation', '') == 'ipython' \
109 and 'implementation_version' in content:
111 and 'implementation_version' in content:
110 content['ipython_version'] = content.pop('implmentation_version')
112 content['ipython_version'] = content.pop('implmentation_version')
111 content.pop('implementation_version', None)
113 content.pop('implementation_version', None)
112 content.setdefault("implmentation", content['language'])
114 content.setdefault("implmentation", content['language'])
113 return msg
115 return msg
114
116
115 def execute_request(self, msg):
117 def execute_request(self, msg):
116 content = msg['content']
118 content = msg['content']
117 content.setdefault('user_variables', [])
119 content.setdefault('user_variables', [])
118 return msg
120 return msg
119
121
120 def execute_reply(self, msg):
122 def execute_reply(self, msg):
121 content = msg['content']
123 content = msg['content']
122 content.setdefault('user_variables', {})
124 content.setdefault('user_variables', {})
123 # TODO: handle payloads
125 # TODO: handle payloads
124 return msg
126 return msg
125
127
126 def complete_request(self, msg):
128 def complete_request(self, msg):
127 content = msg['content']
129 content = msg['content']
128 code = content['code']
130 code = content['code']
129 cursor_pos = content['cursor_pos']
131 cursor_pos = content['cursor_pos']
130 line, cursor_pos = code_to_line(code, cursor_pos)
132 line, cursor_pos = code_to_line(code, cursor_pos)
131
133
132 new_content = msg['content'] = {}
134 new_content = msg['content'] = {}
133 new_content['text'] = ''
135 new_content['text'] = ''
134 new_content['line'] = line
136 new_content['line'] = line
135 new_content['block'] = None
137 new_content['block'] = None
136 new_content['cursor_pos'] = cursor_pos
138 new_content['cursor_pos'] = cursor_pos
137 return msg
139 return msg
138
140
139 def complete_reply(self, msg):
141 def complete_reply(self, msg):
140 content = msg['content']
142 content = msg['content']
141 cursor_start = content.pop('cursor_start')
143 cursor_start = content.pop('cursor_start')
142 cursor_end = content.pop('cursor_end')
144 cursor_end = content.pop('cursor_end')
143 match_len = cursor_end - cursor_start
145 match_len = cursor_end - cursor_start
144 content['matched_text'] = content['matches'][0][:match_len]
146 content['matched_text'] = content['matches'][0][:match_len]
145 content.pop('metadata', None)
147 content.pop('metadata', None)
146 return msg
148 return msg
147
149
148 def object_info_request(self, msg):
150 def object_info_request(self, msg):
149 content = msg['content']
151 content = msg['content']
150 code = content['code']
152 code = content['code']
151 cursor_pos = content['cursor_pos']
153 cursor_pos = content['cursor_pos']
152 line, _ = code_to_line(code, cursor_pos)
154 line, _ = code_to_line(code, cursor_pos)
153
155
154 new_content = msg['content'] = {}
156 new_content = msg['content'] = {}
155 new_content['oname'] = token_at_cursor(code, cursor_pos)
157 new_content['oname'] = token_at_cursor(code, cursor_pos)
156 new_content['detail_level'] = content['detail_level']
158 new_content['detail_level'] = content['detail_level']
157 return msg
159 return msg
158
160
159 def object_info_reply(self, msg):
161 def object_info_reply(self, msg):
160 """inspect_reply can't be easily backward compatible"""
162 """inspect_reply can't be easily backward compatible"""
161 msg['content'] = {'found' : False, 'name' : 'unknown'}
163 msg['content'] = {'found' : False, 'name' : 'unknown'}
162 return msg
164 return msg
163
165
164 # iopub channel
166 # iopub channel
165
167
166 def stream(self, msg):
168 def stream(self, msg):
167 content = msg['content']
169 content = msg['content']
168 content['data'] = content.pop('text')
170 content['data'] = content.pop('text')
169 return msg
171 return msg
170
172
171 def display_data(self, msg):
173 def display_data(self, msg):
172 content = msg['content']
174 content = msg['content']
173 content.setdefault("source", "display")
175 content.setdefault("source", "display")
174 data = content['data']
176 data = content['data']
175 if 'application/json' in data:
177 if 'application/json' in data:
176 try:
178 try:
177 data['application/json'] = json.dumps(data['application/json'])
179 data['application/json'] = json.dumps(data['application/json'])
178 except Exception:
180 except Exception:
179 # warn?
181 # warn?
180 pass
182 pass
181 return msg
183 return msg
182
184
183 # stdin channel
185 # stdin channel
184
186
185 def input_request(self, msg):
187 def input_request(self, msg):
186 msg['content'].pop('password', None)
188 msg['content'].pop('password', None)
187 return msg
189 return msg
188
190
189
191
190 class V4toV5(Adapter):
192 class V4toV5(Adapter):
191 """Convert msg spec V4 to V5"""
193 """Convert msg spec V4 to V5"""
192 version = '5.0'
194 version = '5.0'
193
195
194 # invert message renames above
196 # invert message renames above
195 msg_type_map = {v:k for k,v in V5toV4.msg_type_map.items()}
197 msg_type_map = {v:k for k,v in V5toV4.msg_type_map.items()}
196
198
197 def update_header(self, msg):
199 def update_header(self, msg):
198 msg['header']['version'] = self.version
200 msg['header']['version'] = self.version
199 return msg
201 return msg
200
202
201 # shell channel
203 # shell channel
202
204
203 def kernel_info_reply(self, msg):
205 def kernel_info_reply(self, msg):
204 content = msg['content']
206 content = msg['content']
205 for key in ('language_version', 'protocol_version', 'ipython_version'):
207 for key in ('language_version', 'protocol_version', 'ipython_version'):
206 if key in content:
208 if key in content:
207 content[key] = ".".join(map(str, content[key]))
209 content[key] = ".".join(map(str, content[key]))
208
210
209 if content['language'].startswith('python') and 'ipython_version' in content:
211 if content['language'].startswith('python') and 'ipython_version' in content:
210 content['implementation'] = 'ipython'
212 content['implementation'] = 'ipython'
211 content['implementation_version'] = content.pop('ipython_version')
213 content['implementation_version'] = content.pop('ipython_version')
212
214
213 content['banner'] = ''
215 content['banner'] = ''
214 return msg
216 return msg
215
217
216 def execute_request(self, msg):
218 def execute_request(self, msg):
217 content = msg['content']
219 content = msg['content']
218 user_variables = content.pop('user_variables', [])
220 user_variables = content.pop('user_variables', [])
219 user_expressions = content.setdefault('user_expressions', {})
221 user_expressions = content.setdefault('user_expressions', {})
220 for v in user_variables:
222 for v in user_variables:
221 user_expressions[v] = v
223 user_expressions[v] = v
222 return msg
224 return msg
223
225
224 def execute_reply(self, msg):
226 def execute_reply(self, msg):
225 content = msg['content']
227 content = msg['content']
226 user_expressions = content.setdefault('user_expressions', {})
228 user_expressions = content.setdefault('user_expressions', {})
227 user_variables = content.pop('user_variables', {})
229 user_variables = content.pop('user_variables', {})
228 if user_variables:
230 if user_variables:
229 user_expressions.update(user_variables)
231 user_expressions.update(user_variables)
230
232
231 # Pager payloads became a mime bundle
233 # Pager payloads became a mime bundle
232 for payload in content.get('payload', []):
234 for payload in content.get('payload', []):
233 if payload.get('source', None) == 'page' and ('text' in payload):
235 if payload.get('source', None) == 'page' and ('text' in payload):
234 if 'data' not in payload:
236 if 'data' not in payload:
235 payload['data'] = {}
237 payload['data'] = {}
236 payload['data']['text/plain'] = payload.pop('text')
238 payload['data']['text/plain'] = payload.pop('text')
237
239
238 return msg
240 return msg
239
241
240 def complete_request(self, msg):
242 def complete_request(self, msg):
241 old_content = msg['content']
243 old_content = msg['content']
242
244
243 new_content = msg['content'] = {}
245 new_content = msg['content'] = {}
244 new_content['code'] = old_content['line']
246 new_content['code'] = old_content['line']
245 new_content['cursor_pos'] = old_content['cursor_pos']
247 new_content['cursor_pos'] = old_content['cursor_pos']
246 return msg
248 return msg
247
249
248 def complete_reply(self, msg):
250 def complete_reply(self, msg):
249 # complete_reply needs more context than we have to get cursor_start and end.
251 # complete_reply needs more context than we have to get cursor_start and end.
250 # use special value of `-1` to indicate to frontend that it should be at
252 # use special value of `-1` to indicate to frontend that it should be at
251 # the current cursor position.
253 # the current cursor position.
252 content = msg['content']
254 content = msg['content']
253 new_content = msg['content'] = {'status' : 'ok'}
255 new_content = msg['content'] = {'status' : 'ok'}
254 new_content['matches'] = content['matches']
256 new_content['matches'] = content['matches']
255 new_content['cursor_start'] = -len(content['matched_text'])
257 new_content['cursor_start'] = -len(content['matched_text'])
256 new_content['cursor_end'] = None
258 new_content['cursor_end'] = None
257 new_content['metadata'] = {}
259 new_content['metadata'] = {}
258 return msg
260 return msg
259
261
260 def inspect_request(self, msg):
262 def inspect_request(self, msg):
261 content = msg['content']
263 content = msg['content']
262 name = content['oname']
264 name = content['oname']
263
265
264 new_content = msg['content'] = {}
266 new_content = msg['content'] = {}
265 new_content['code'] = name
267 new_content['code'] = name
266 new_content['cursor_pos'] = len(name)
268 new_content['cursor_pos'] = len(name)
267 new_content['detail_level'] = content['detail_level']
269 new_content['detail_level'] = content['detail_level']
268 return msg
270 return msg
269
271
270 def inspect_reply(self, msg):
272 def inspect_reply(self, msg):
271 """inspect_reply can't be easily backward compatible"""
273 """inspect_reply can't be easily backward compatible"""
272 content = msg['content']
274 content = msg['content']
273 new_content = msg['content'] = {'status' : 'ok'}
275 new_content = msg['content'] = {'status' : 'ok'}
274 found = new_content['found'] = content['found']
276 found = new_content['found'] = content['found']
275 new_content['name'] = content['name']
277 new_content['name'] = content['name']
276 new_content['data'] = data = {}
278 new_content['data'] = data = {}
277 new_content['metadata'] = {}
279 new_content['metadata'] = {}
278 if found:
280 if found:
279 lines = []
281 lines = []
280 for key in ('call_def', 'init_definition', 'definition'):
282 for key in ('call_def', 'init_definition', 'definition'):
281 if content.get(key, False):
283 if content.get(key, False):
282 lines.append(content[key])
284 lines.append(content[key])
283 break
285 break
284 for key in ('call_docstring', 'init_docstring', 'docstring'):
286 for key in ('call_docstring', 'init_docstring', 'docstring'):
285 if content.get(key, False):
287 if content.get(key, False):
286 lines.append(content[key])
288 lines.append(content[key])
287 break
289 break
288 if not lines:
290 if not lines:
289 lines.append("<empty docstring>")
291 lines.append("<empty docstring>")
290 data['text/plain'] = '\n'.join(lines)
292 data['text/plain'] = '\n'.join(lines)
291 return msg
293 return msg
292
294
293 # iopub channel
295 # iopub channel
294
296
295 def stream(self, msg):
297 def stream(self, msg):
296 content = msg['content']
298 content = msg['content']
297 content['text'] = content.pop('data')
299 content['text'] = content.pop('data')
298 return msg
300 return msg
299
301
300 def display_data(self, msg):
302 def display_data(self, msg):
301 content = msg['content']
303 content = msg['content']
302 content.pop("source", None)
304 content.pop("source", None)
303 data = content['data']
305 data = content['data']
304 if 'application/json' in data:
306 if 'application/json' in data:
305 try:
307 try:
306 data['application/json'] = json.loads(data['application/json'])
308 data['application/json'] = json.loads(data['application/json'])
307 except Exception:
309 except Exception:
308 # warn?
310 # warn?
309 pass
311 pass
310 return msg
312 return msg
311
313
312 # stdin channel
314 # stdin channel
313
315
314 def input_request(self, msg):
316 def input_request(self, msg):
315 msg['content'].setdefault('password', False)
317 msg['content'].setdefault('password', False)
316 return msg
318 return msg
317
319
318
320
319
321
320 def adapt(msg, to_version=kernel_protocol_version_info[0]):
322 def adapt(msg, to_version=kernel_protocol_version_info[0]):
321 """Adapt a single message to a target version
323 """Adapt a single message to a target version
322
324
323 Parameters
325 Parameters
324 ----------
326 ----------
325
327
326 msg : dict
328 msg : dict
327 An IPython message.
329 An IPython message.
328 to_version : int, optional
330 to_version : int, optional
329 The target major version.
331 The target major version.
330 If unspecified, adapt to the current version for IPython.
332 If unspecified, adapt to the current version for IPython.
331
333
332 Returns
334 Returns
333 -------
335 -------
334
336
335 msg : dict
337 msg : dict
336 An IPython message appropriate in the new version.
338 An IPython message appropriate in the new version.
337 """
339 """
338 header = msg['header']
340 header = msg['header']
339 if 'version' in header:
341 if 'version' in header:
340 from_version = int(header['version'].split('.')[0])
342 from_version = int(header['version'].split('.')[0])
341 else:
343 else:
342 # assume last version before adding the key to the header
344 # assume last version before adding the key to the header
343 from_version = 4
345 from_version = 4
344 adapter = adapters.get((from_version, to_version), None)
346 adapter = adapters.get((from_version, to_version), None)
345 if adapter is None:
347 if adapter is None:
346 return msg
348 return msg
347 return adapter(msg)
349 return adapter(msg)
348
350
349
351
350 # one adapter per major version from,to
352 # one adapter per major version from,to
351 adapters = {
353 adapters = {
352 (5,4) : V5toV4(),
354 (5,4) : V5toV4(),
353 (4,5) : V4toV5(),
355 (4,5) : V4toV5(),
354 }
356 }
@@ -1,330 +1,334 b''
1 """Tests for adapting IPython msg spec versions"""
1 """Tests for adapting IPython msg spec versions"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
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 import json
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
11 from IPython.kernel.adapter import adapt, V4toV5, V5toV4
11 from IPython.kernel.adapter import adapt, V4toV5, V5toV4, code_to_line
12 from IPython.kernel.zmq.session import Session
12 from IPython.kernel.zmq.session import Session
13
13
14
14
15 def test_default_version():
15 def test_default_version():
16 s = Session()
16 s = Session()
17 msg = s.msg("msg_type")
17 msg = s.msg("msg_type")
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']['version'], V4toV5.version)
21 nt.assert_equal(adapted['header']['version'], V4toV5.version)
22
22
23 def test_code_to_line_no_code():
24 line, pos = code_to_line("", 0)
25 nt.assert_equal(line, "")
26 nt.assert_equal(pos, 0)
23
27
24 class AdapterTest(TestCase):
28 class AdapterTest(TestCase):
25
29
26 def setUp(self):
30 def setUp(self):
27 self.session = Session()
31 self.session = Session()
28
32
29 def adapt(self, msg, version=None):
33 def adapt(self, msg, version=None):
30 original = copy.deepcopy(msg)
34 original = copy.deepcopy(msg)
31 adapted = adapt(msg, version or self.to_version)
35 adapted = adapt(msg, version or self.to_version)
32 return original, adapted
36 return original, adapted
33
37
34 def check_header(self, msg):
38 def check_header(self, msg):
35 pass
39 pass
36
40
37
41
38 class V4toV5TestCase(AdapterTest):
42 class V4toV5TestCase(AdapterTest):
39 from_version = 4
43 from_version = 4
40 to_version = 5
44 to_version = 5
41
45
42 def msg(self, msg_type, content):
46 def msg(self, msg_type, content):
43 """Create a v4 msg (same as v5, minus version header)"""
47 """Create a v4 msg (same as v5, minus version header)"""
44 msg = self.session.msg(msg_type, content)
48 msg = self.session.msg(msg_type, content)
45 msg['header'].pop('version')
49 msg['header'].pop('version')
46 return msg
50 return msg
47
51
48 def test_same_version(self):
52 def test_same_version(self):
49 msg = self.msg("execute_result",
53 msg = self.msg("execute_result",
50 content={'status' : 'ok'}
54 content={'status' : 'ok'}
51 )
55 )
52 original, adapted = self.adapt(msg, self.from_version)
56 original, adapted = self.adapt(msg, self.from_version)
53
57
54 self.assertEqual(original, adapted)
58 self.assertEqual(original, adapted)
55
59
56 def test_no_adapt(self):
60 def test_no_adapt(self):
57 msg = self.msg("input_reply", {'value' : 'some text'})
61 msg = self.msg("input_reply", {'value' : 'some text'})
58 v4, v5 = self.adapt(msg)
62 v4, v5 = self.adapt(msg)
59 self.assertEqual(v5['header']['version'], V4toV5.version)
63 self.assertEqual(v5['header']['version'], V4toV5.version)
60 v5['header'].pop('version')
64 v5['header'].pop('version')
61 self.assertEqual(v4, v5)
65 self.assertEqual(v4, v5)
62
66
63 def test_rename_type(self):
67 def test_rename_type(self):
64 for v5_type, v4_type in [
68 for v5_type, v4_type in [
65 ('execute_result', 'pyout'),
69 ('execute_result', 'pyout'),
66 ('execute_input', 'pyin'),
70 ('execute_input', 'pyin'),
67 ('error', 'pyerr'),
71 ('error', 'pyerr'),
68 ]:
72 ]:
69 msg = self.msg(v4_type, {'key' : 'value'})
73 msg = self.msg(v4_type, {'key' : 'value'})
70 v4, v5 = self.adapt(msg)
74 v4, v5 = self.adapt(msg)
71 self.assertEqual(v5['header']['version'], V4toV5.version)
75 self.assertEqual(v5['header']['version'], V4toV5.version)
72 self.assertEqual(v5['header']['msg_type'], v5_type)
76 self.assertEqual(v5['header']['msg_type'], v5_type)
73 self.assertEqual(v4['content'], v5['content'])
77 self.assertEqual(v4['content'], v5['content'])
74
78
75 def test_execute_request(self):
79 def test_execute_request(self):
76 msg = self.msg("execute_request", {
80 msg = self.msg("execute_request", {
77 'code' : 'a=5',
81 'code' : 'a=5',
78 'silent' : False,
82 'silent' : False,
79 'user_expressions' : {'a' : 'apple'},
83 'user_expressions' : {'a' : 'apple'},
80 'user_variables' : ['b'],
84 'user_variables' : ['b'],
81 })
85 })
82 v4, v5 = self.adapt(msg)
86 v4, v5 = self.adapt(msg)
83 self.assertEqual(v4['header']['msg_type'], v5['header']['msg_type'])
87 self.assertEqual(v4['header']['msg_type'], v5['header']['msg_type'])
84 v4c = v4['content']
88 v4c = v4['content']
85 v5c = v5['content']
89 v5c = v5['content']
86 self.assertEqual(v5c['user_expressions'], {'a' : 'apple', 'b': 'b'})
90 self.assertEqual(v5c['user_expressions'], {'a' : 'apple', 'b': 'b'})
87 self.assertNotIn('user_variables', v5c)
91 self.assertNotIn('user_variables', v5c)
88 self.assertEqual(v5c['code'], v4c['code'])
92 self.assertEqual(v5c['code'], v4c['code'])
89
93
90 def test_execute_reply(self):
94 def test_execute_reply(self):
91 msg = self.msg("execute_reply", {
95 msg = self.msg("execute_reply", {
92 'status': 'ok',
96 'status': 'ok',
93 'execution_count': 7,
97 'execution_count': 7,
94 'user_variables': {'a': 1},
98 'user_variables': {'a': 1},
95 'user_expressions': {'a+a': 2},
99 'user_expressions': {'a+a': 2},
96 'payload': [{'source':'page', 'text':'blah'}]
100 'payload': [{'source':'page', 'text':'blah'}]
97 })
101 })
98 v4, v5 = self.adapt(msg)
102 v4, v5 = self.adapt(msg)
99 v5c = v5['content']
103 v5c = v5['content']
100 self.assertNotIn('user_variables', v5c)
104 self.assertNotIn('user_variables', v5c)
101 self.assertEqual(v5c['user_expressions'], {'a': 1, 'a+a': 2})
105 self.assertEqual(v5c['user_expressions'], {'a': 1, 'a+a': 2})
102 self.assertEqual(v5c['payload'], [{'source': 'page',
106 self.assertEqual(v5c['payload'], [{'source': 'page',
103 'data': {'text/plain': 'blah'}}
107 'data': {'text/plain': 'blah'}}
104 ])
108 ])
105
109
106 def test_complete_request(self):
110 def test_complete_request(self):
107 msg = self.msg("complete_request", {
111 msg = self.msg("complete_request", {
108 'text' : 'a.is',
112 'text' : 'a.is',
109 'line' : 'foo = a.is',
113 'line' : 'foo = a.is',
110 'block' : None,
114 'block' : None,
111 'cursor_pos' : 10,
115 'cursor_pos' : 10,
112 })
116 })
113 v4, v5 = self.adapt(msg)
117 v4, v5 = self.adapt(msg)
114 v4c = v4['content']
118 v4c = v4['content']
115 v5c = v5['content']
119 v5c = v5['content']
116 for key in ('text', 'line', 'block'):
120 for key in ('text', 'line', 'block'):
117 self.assertNotIn(key, v5c)
121 self.assertNotIn(key, v5c)
118 self.assertEqual(v5c['cursor_pos'], v4c['cursor_pos'])
122 self.assertEqual(v5c['cursor_pos'], v4c['cursor_pos'])
119 self.assertEqual(v5c['code'], v4c['line'])
123 self.assertEqual(v5c['code'], v4c['line'])
120
124
121 def test_complete_reply(self):
125 def test_complete_reply(self):
122 msg = self.msg("complete_reply", {
126 msg = self.msg("complete_reply", {
123 'matched_text' : 'a.is',
127 'matched_text' : 'a.is',
124 'matches' : ['a.isalnum',
128 'matches' : ['a.isalnum',
125 'a.isalpha',
129 'a.isalpha',
126 'a.isdigit',
130 'a.isdigit',
127 'a.islower',
131 'a.islower',
128 ],
132 ],
129 })
133 })
130 v4, v5 = self.adapt(msg)
134 v4, v5 = self.adapt(msg)
131 v4c = v4['content']
135 v4c = v4['content']
132 v5c = v5['content']
136 v5c = v5['content']
133
137
134 self.assertEqual(v5c['matches'], v4c['matches'])
138 self.assertEqual(v5c['matches'], v4c['matches'])
135 self.assertEqual(v5c['metadata'], {})
139 self.assertEqual(v5c['metadata'], {})
136 self.assertEqual(v5c['cursor_start'], -4)
140 self.assertEqual(v5c['cursor_start'], -4)
137 self.assertEqual(v5c['cursor_end'], None)
141 self.assertEqual(v5c['cursor_end'], None)
138
142
139 def test_object_info_request(self):
143 def test_object_info_request(self):
140 msg = self.msg("object_info_request", {
144 msg = self.msg("object_info_request", {
141 'oname' : 'foo',
145 'oname' : 'foo',
142 'detail_level' : 1,
146 'detail_level' : 1,
143 })
147 })
144 v4, v5 = self.adapt(msg)
148 v4, v5 = self.adapt(msg)
145 self.assertEqual(v5['header']['msg_type'], 'inspect_request')
149 self.assertEqual(v5['header']['msg_type'], 'inspect_request')
146 v4c = v4['content']
150 v4c = v4['content']
147 v5c = v5['content']
151 v5c = v5['content']
148 self.assertEqual(v5c['code'], v4c['oname'])
152 self.assertEqual(v5c['code'], v4c['oname'])
149 self.assertEqual(v5c['cursor_pos'], len(v4c['oname']))
153 self.assertEqual(v5c['cursor_pos'], len(v4c['oname']))
150 self.assertEqual(v5c['detail_level'], v4c['detail_level'])
154 self.assertEqual(v5c['detail_level'], v4c['detail_level'])
151
155
152 def test_object_info_reply(self):
156 def test_object_info_reply(self):
153 msg = self.msg("object_info_reply", {
157 msg = self.msg("object_info_reply", {
154 'name' : 'foo',
158 'name' : 'foo',
155 'found' : True,
159 'found' : True,
156 'status' : 'ok',
160 'status' : 'ok',
157 'definition' : 'foo(a=5)',
161 'definition' : 'foo(a=5)',
158 'docstring' : "the docstring",
162 'docstring' : "the docstring",
159 })
163 })
160 v4, v5 = self.adapt(msg)
164 v4, v5 = self.adapt(msg)
161 self.assertEqual(v5['header']['msg_type'], 'inspect_reply')
165 self.assertEqual(v5['header']['msg_type'], 'inspect_reply')
162 v4c = v4['content']
166 v4c = v4['content']
163 v5c = v5['content']
167 v5c = v5['content']
164 self.assertEqual(sorted(v5c), [ 'data', 'found', 'metadata', 'name', 'status'])
168 self.assertEqual(sorted(v5c), [ 'data', 'found', 'metadata', 'name', 'status'])
165 text = v5c['data']['text/plain']
169 text = v5c['data']['text/plain']
166 self.assertEqual(text, '\n'.join([v4c['definition'], v4c['docstring']]))
170 self.assertEqual(text, '\n'.join([v4c['definition'], v4c['docstring']]))
167
171
168 # iopub channel
172 # iopub channel
169
173
170 def test_display_data(self):
174 def test_display_data(self):
171 jsondata = dict(a=5)
175 jsondata = dict(a=5)
172 msg = self.msg("display_data", {
176 msg = self.msg("display_data", {
173 'data' : {
177 'data' : {
174 'text/plain' : 'some text',
178 'text/plain' : 'some text',
175 'application/json' : json.dumps(jsondata)
179 'application/json' : json.dumps(jsondata)
176 },
180 },
177 'metadata' : {'text/plain' : { 'key' : 'value' }},
181 'metadata' : {'text/plain' : { 'key' : 'value' }},
178 })
182 })
179 v4, v5 = self.adapt(msg)
183 v4, v5 = self.adapt(msg)
180 v4c = v4['content']
184 v4c = v4['content']
181 v5c = v5['content']
185 v5c = v5['content']
182 self.assertEqual(v5c['metadata'], v4c['metadata'])
186 self.assertEqual(v5c['metadata'], v4c['metadata'])
183 self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain'])
187 self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain'])
184 self.assertEqual(v5c['data']['application/json'], jsondata)
188 self.assertEqual(v5c['data']['application/json'], jsondata)
185
189
186 # stdin channel
190 # stdin channel
187
191
188 def test_input_request(self):
192 def test_input_request(self):
189 msg = self.msg('input_request', {'prompt': "$>"})
193 msg = self.msg('input_request', {'prompt': "$>"})
190 v4, v5 = self.adapt(msg)
194 v4, v5 = self.adapt(msg)
191 self.assertEqual(v5['content']['prompt'], v4['content']['prompt'])
195 self.assertEqual(v5['content']['prompt'], v4['content']['prompt'])
192 self.assertFalse(v5['content']['password'])
196 self.assertFalse(v5['content']['password'])
193
197
194
198
195 class V5toV4TestCase(AdapterTest):
199 class V5toV4TestCase(AdapterTest):
196 from_version = 5
200 from_version = 5
197 to_version = 4
201 to_version = 4
198
202
199 def msg(self, msg_type, content):
203 def msg(self, msg_type, content):
200 return self.session.msg(msg_type, content)
204 return self.session.msg(msg_type, content)
201
205
202 def test_same_version(self):
206 def test_same_version(self):
203 msg = self.msg("execute_result",
207 msg = self.msg("execute_result",
204 content={'status' : 'ok'}
208 content={'status' : 'ok'}
205 )
209 )
206 original, adapted = self.adapt(msg, self.from_version)
210 original, adapted = self.adapt(msg, self.from_version)
207
211
208 self.assertEqual(original, adapted)
212 self.assertEqual(original, adapted)
209
213
210 def test_no_adapt(self):
214 def test_no_adapt(self):
211 msg = self.msg("input_reply", {'value' : 'some text'})
215 msg = self.msg("input_reply", {'value' : 'some text'})
212 v5, v4 = self.adapt(msg)
216 v5, v4 = self.adapt(msg)
213 self.assertNotIn('version', v4['header'])
217 self.assertNotIn('version', v4['header'])
214 v5['header'].pop('version')
218 v5['header'].pop('version')
215 self.assertEqual(v4, v5)
219 self.assertEqual(v4, v5)
216
220
217 def test_rename_type(self):
221 def test_rename_type(self):
218 for v5_type, v4_type in [
222 for v5_type, v4_type in [
219 ('execute_result', 'pyout'),
223 ('execute_result', 'pyout'),
220 ('execute_input', 'pyin'),
224 ('execute_input', 'pyin'),
221 ('error', 'pyerr'),
225 ('error', 'pyerr'),
222 ]:
226 ]:
223 msg = self.msg(v5_type, {'key' : 'value'})
227 msg = self.msg(v5_type, {'key' : 'value'})
224 v5, v4 = self.adapt(msg)
228 v5, v4 = self.adapt(msg)
225 self.assertEqual(v4['header']['msg_type'], v4_type)
229 self.assertEqual(v4['header']['msg_type'], v4_type)
226 nt.assert_not_in('version', v4['header'])
230 nt.assert_not_in('version', v4['header'])
227 self.assertEqual(v4['content'], v5['content'])
231 self.assertEqual(v4['content'], v5['content'])
228
232
229 def test_execute_request(self):
233 def test_execute_request(self):
230 msg = self.msg("execute_request", {
234 msg = self.msg("execute_request", {
231 'code' : 'a=5',
235 'code' : 'a=5',
232 'silent' : False,
236 'silent' : False,
233 'user_expressions' : {'a' : 'apple'},
237 'user_expressions' : {'a' : 'apple'},
234 })
238 })
235 v5, v4 = self.adapt(msg)
239 v5, v4 = self.adapt(msg)
236 self.assertEqual(v4['header']['msg_type'], v5['header']['msg_type'])
240 self.assertEqual(v4['header']['msg_type'], v5['header']['msg_type'])
237 v4c = v4['content']
241 v4c = v4['content']
238 v5c = v5['content']
242 v5c = v5['content']
239 self.assertEqual(v4c['user_variables'], [])
243 self.assertEqual(v4c['user_variables'], [])
240 self.assertEqual(v5c['code'], v4c['code'])
244 self.assertEqual(v5c['code'], v4c['code'])
241
245
242 def test_complete_request(self):
246 def test_complete_request(self):
243 msg = self.msg("complete_request", {
247 msg = self.msg("complete_request", {
244 'code' : 'def foo():\n'
248 'code' : 'def foo():\n'
245 ' a.is\n'
249 ' a.is\n'
246 'foo()',
250 'foo()',
247 'cursor_pos': 19,
251 'cursor_pos': 19,
248 })
252 })
249 v5, v4 = self.adapt(msg)
253 v5, v4 = self.adapt(msg)
250 v4c = v4['content']
254 v4c = v4['content']
251 v5c = v5['content']
255 v5c = v5['content']
252 self.assertNotIn('code', v4c)
256 self.assertNotIn('code', v4c)
253 self.assertEqual(v4c['line'], v5c['code'].splitlines(True)[1])
257 self.assertEqual(v4c['line'], v5c['code'].splitlines(True)[1])
254 self.assertEqual(v4c['cursor_pos'], 8)
258 self.assertEqual(v4c['cursor_pos'], 8)
255 self.assertEqual(v4c['text'], '')
259 self.assertEqual(v4c['text'], '')
256 self.assertEqual(v4c['block'], None)
260 self.assertEqual(v4c['block'], None)
257
261
258 def test_complete_reply(self):
262 def test_complete_reply(self):
259 msg = self.msg("complete_reply", {
263 msg = self.msg("complete_reply", {
260 'cursor_start' : 10,
264 'cursor_start' : 10,
261 'cursor_end' : 14,
265 'cursor_end' : 14,
262 'matches' : ['a.isalnum',
266 'matches' : ['a.isalnum',
263 'a.isalpha',
267 'a.isalpha',
264 'a.isdigit',
268 'a.isdigit',
265 'a.islower',
269 'a.islower',
266 ],
270 ],
267 'metadata' : {},
271 'metadata' : {},
268 })
272 })
269 v5, v4 = self.adapt(msg)
273 v5, v4 = self.adapt(msg)
270 v4c = v4['content']
274 v4c = v4['content']
271 v5c = v5['content']
275 v5c = v5['content']
272 self.assertEqual(v4c['matched_text'], 'a.is')
276 self.assertEqual(v4c['matched_text'], 'a.is')
273 self.assertEqual(v4c['matches'], v5c['matches'])
277 self.assertEqual(v4c['matches'], v5c['matches'])
274
278
275 def test_inspect_request(self):
279 def test_inspect_request(self):
276 msg = self.msg("inspect_request", {
280 msg = self.msg("inspect_request", {
277 'code' : 'def foo():\n'
281 'code' : 'def foo():\n'
278 ' apple\n'
282 ' apple\n'
279 'bar()',
283 'bar()',
280 'cursor_pos': 18,
284 'cursor_pos': 18,
281 'detail_level' : 1,
285 'detail_level' : 1,
282 })
286 })
283 v5, v4 = self.adapt(msg)
287 v5, v4 = self.adapt(msg)
284 self.assertEqual(v4['header']['msg_type'], 'object_info_request')
288 self.assertEqual(v4['header']['msg_type'], 'object_info_request')
285 v4c = v4['content']
289 v4c = v4['content']
286 v5c = v5['content']
290 v5c = v5['content']
287 self.assertEqual(v4c['oname'], 'apple')
291 self.assertEqual(v4c['oname'], 'apple')
288 self.assertEqual(v5c['detail_level'], v4c['detail_level'])
292 self.assertEqual(v5c['detail_level'], v4c['detail_level'])
289
293
290 def test_inspect_reply(self):
294 def test_inspect_reply(self):
291 msg = self.msg("inspect_reply", {
295 msg = self.msg("inspect_reply", {
292 'name' : 'foo',
296 'name' : 'foo',
293 'found' : True,
297 'found' : True,
294 'data' : {'text/plain' : 'some text'},
298 'data' : {'text/plain' : 'some text'},
295 'metadata' : {},
299 'metadata' : {},
296 })
300 })
297 v5, v4 = self.adapt(msg)
301 v5, v4 = self.adapt(msg)
298 self.assertEqual(v4['header']['msg_type'], 'object_info_reply')
302 self.assertEqual(v4['header']['msg_type'], 'object_info_reply')
299 v4c = v4['content']
303 v4c = v4['content']
300 v5c = v5['content']
304 v5c = v5['content']
301 self.assertEqual(sorted(v4c), ['found', 'name'])
305 self.assertEqual(sorted(v4c), ['found', 'name'])
302 self.assertEqual(v4c['found'], False)
306 self.assertEqual(v4c['found'], False)
303
307
304 # iopub channel
308 # iopub channel
305
309
306 def test_display_data(self):
310 def test_display_data(self):
307 jsondata = dict(a=5)
311 jsondata = dict(a=5)
308 msg = self.msg("display_data", {
312 msg = self.msg("display_data", {
309 'data' : {
313 'data' : {
310 'text/plain' : 'some text',
314 'text/plain' : 'some text',
311 'application/json' : jsondata,
315 'application/json' : jsondata,
312 },
316 },
313 'metadata' : {'text/plain' : { 'key' : 'value' }},
317 'metadata' : {'text/plain' : { 'key' : 'value' }},
314 })
318 })
315 v5, v4 = self.adapt(msg)
319 v5, v4 = self.adapt(msg)
316 v4c = v4['content']
320 v4c = v4['content']
317 v5c = v5['content']
321 v5c = v5['content']
318 self.assertEqual(v5c['metadata'], v4c['metadata'])
322 self.assertEqual(v5c['metadata'], v4c['metadata'])
319 self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain'])
323 self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain'])
320 self.assertEqual(v4c['data']['application/json'], json.dumps(jsondata))
324 self.assertEqual(v4c['data']['application/json'], json.dumps(jsondata))
321
325
322 # stdin channel
326 # stdin channel
323
327
324 def test_input_request(self):
328 def test_input_request(self):
325 msg = self.msg('input_request', {'prompt': "$>", 'password' : True})
329 msg = self.msg('input_request', {'prompt': "$>", 'password' : True})
326 v5, v4 = self.adapt(msg)
330 v5, v4 = self.adapt(msg)
327 self.assertEqual(v5['content']['prompt'], v4['content']['prompt'])
331 self.assertEqual(v5['content']['prompt'], v4['content']['prompt'])
328 self.assertNotIn('password', v4['content'])
332 self.assertNotIn('password', v4['content'])
329
333
330
334
General Comments 0
You need to be logged in to leave comments. Login now