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