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