##// END OF EJS Templates
add IPython.kernel.adapter...
MinRK -
Show More
@@ -0,0 +1,295 b''
1 """Adapters for IPython msg spec versions."""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
6 import json
7
8 from IPython.core.release import kernel_protocol_version, kernel_protocol_version_info
9 from IPython.utils.tokenutil import token_at_cursor
10
11
12 def code_to_line(code, cursor_pos):
13 """Turn a multiline code block and cursor position into a single line
14 and new cursor position.
15
16 For adapting complete_ and object_info_requests.
17 """
18 for line in code.splitlines(True):
19 n = len(line)
20 if cursor_pos > n:
21 cursor_pos -= n
22 else:
23 break
24 return line, cursor_pos
25
26
27 class Adapter(object):
28 """Base class for adapting messages
29
30 Override message_type(msg) methods to create adapters.
31 """
32
33 msg_type_map = {}
34
35 def update_header(self, msg):
36 return msg
37
38 def update_metadata(self, msg):
39 return msg
40
41 def update_msg_type(self, msg):
42 header = msg['header']
43 msg_type = header['msg_type']
44 if msg_type in self.msg_type_map:
45 msg['msg_type'] = header['msg_type'] = self.msg_type_map[msg_type]
46 return msg
47
48 def __call__(self, msg):
49 msg = self.update_header(msg)
50 msg = self.update_metadata(msg)
51 msg = self.update_msg_type(msg)
52 header = msg['header']
53
54 handler = getattr(self, header['msg_type'], None)
55 if handler is None:
56 return msg
57 return handler(msg)
58
59 def _version_str_to_list(version):
60 """convert a version string to a list of ints
61
62 non-int segments are excluded
63 """
64 v = []
65 for part in content[key].split('.'):
66 try:
67 v.append(int(part))
68 except ValueError:
69 pass
70 return v
71
72 class V5toV4(Adapter):
73 """Adapt msg protocol v5 to v4"""
74
75 version = '4.1'
76
77 msg_type_map = {
78 'execute_result' : 'pyout',
79 'execute_input' : 'pyin',
80 'error' : 'pyerr',
81 'inspect_request' : 'object_info_request',
82 'inspect_reply' : 'object_info_reply',
83 }
84
85 def update_header(self, msg):
86 msg['header'].pop('version', None)
87 return msg
88
89 # shell channel
90
91 def kernel_info_reply(self, msg):
92 content = msg['content']
93 content.pop('banner', None)
94 for key in ('language_version', 'protocol_version'):
95 if key in content:
96 content[key] = _version_str_to_list(content[key])
97 if content.pop('implementation', '') == 'ipython' \
98 and 'implementation_version' in content:
99 content['ipython_version'] = content.pop('implmentation_version')
100 content.pop('implementation_version', None)
101 content.setdefault("implmentation", content['language'])
102 return msg
103
104 def execute_request(self, msg):
105 content = msg['content']
106 content.setdefault('user_variables', [])
107 return msg
108
109 def execute_reply(self, msg):
110 content = msg['content']
111 content.setdefault('user_variables', {})
112 # TODO: handle payloads
113 return msg
114
115 def complete_request(self, msg):
116 content = msg['content']
117 code = content['code']
118 cursor_pos = content['cursor_pos']
119 line, cursor_pos = code_to_line(code, cursor_pos)
120
121 new_content = msg['content'] = {}
122 new_content['text'] = ''
123 new_content['line'] = line
124 new_content['blob'] = None
125 new_content['cursor_pos'] = cursor_pos
126 return msg
127
128 def complete_reply(self, msg):
129 content = msg['content']
130 cursor_start = content.pop('cursor_start')
131 cursor_end = content.pop('cursor_end')
132 match_len = cursor_end - cursor_start
133 content['matched_text'] = content['matches'][0][:match_len]
134 content.pop('metadata', None)
135 return msg
136
137 def object_info_request(self, msg):
138 content = msg['content']
139 code = content['code']
140 cursor_pos = content['cursor_pos']
141 line, cursor_pos = code_to_line(code, cursor_pos)
142
143 new_content = msg['content'] = {}
144 new_content['name'] = token_at_cursor(code, cursor_pos)
145 new_content['detail_level'] = content['detail_level']
146 return msg
147
148 def object_info_reply(self, msg):
149 """inspect_reply can't be easily backward compatible"""
150 msg['content'] = {'found' : False, 'name' : 'unknown'}
151 return msg
152
153 # iopub channel
154
155 def display_data(self, msg):
156 content = msg['content']
157 content.setdefault("source", "display")
158 return msg
159
160 # stdin channel
161
162 def input_request(self, msg):
163 msg['content'].pop('password', None)
164 return msg
165
166 def _tuple_to_str(version):
167 return ".".join(map(str, version))
168
169 class V4toV5(Adapter):
170 """Convert msg spec V4 to V5"""
171 version = kernel_protocol_version
172
173 # invert message renames above
174 msg_type_map = {v:k for k,v in V5toV4.msg_type_map.items()}
175
176 def update_header(self, msg):
177 msg['header']['version'] = self.version
178 return msg
179
180 # shell channel
181
182 def kernel_info_reply(self, msg):
183 content = msg['content']
184 for key in ('language_version', 'protocol_version', 'ipython_version'):
185 if key in content:
186 content[key] = ".".join(map(str, content[key]))
187
188 if content['language'].startswith('python') and 'ipython_version' in content:
189 content['implementation'] = 'ipython'
190 content['implementation_version'] = content.pop('ipython_version')
191
192 content['banner'] = ''
193 return msg
194
195 def execute_request(self, msg):
196 content = msg['content']
197 user_variables = content.pop('user_variables', [])
198 user_expressions = content.setdefault('user_expressions', {})
199 for v in user_variables:
200 user_expressions[v] = v
201 return msg
202
203 def execute_reply(self, msg):
204 content = msg['content']
205 user_expressions = content.setdefault('user_expressions', {})
206 user_variables = content.pop('user_variables', {})
207 if user_variables:
208 user_expressions.update(user_variables)
209 return msg
210
211 def complete_request(self, msg):
212 old_content = msg['content']
213
214 new_content = msg['content'] = {}
215 new_content['code'] = old_content['line']
216 new_content['cursor_pos'] = old_content['cursor_pos']
217 return msg
218
219 def complete_reply(self, msg):
220 # TODO: complete_reply needs more context than we have
221 # Maybe strip common prefix and give magic cursor_start = cursor_end = 0?
222 content = msg['content'] = {}
223 content['matches'] = []
224 content['cursor_start'] = content['cursor_end'] = 0
225 content['metadata'] = {}
226 return msg
227
228 def inspect_request(self, msg):
229 content = msg['content']
230 name = content['name']
231
232 new_content = msg['content'] = {}
233 new_content['code'] = name
234 new_content['cursor_pos'] = len(name) - 1
235 new_content['detail_level'] = content['detail_level']
236 return msg
237
238 def inspect_reply(self, msg):
239 """inspect_reply can't be easily backward compatible"""
240 msg['content'] = {'found' : False, 'name' : 'unknown'}
241 return msg
242
243 # iopub channel
244
245 def display_data(self, msg):
246 content = msg['content']
247 content.pop("source", None)
248 data = content['data']
249 if 'application/json' in data:
250 data['application/json'] = json.dumps(data['application/json'])
251 return msg
252
253 # stdin channel
254
255 def input_request(self, msg):
256 msg['content'].setdefault('password', False)
257 return msg
258
259
260
261 def adapt(msg, to_version=kernel_protocol_version_info[0]):
262 """Adapt a single message to a target version
263
264 Parameters
265 ----------
266
267 msg : dict
268 An IPython message.
269 to_version : int, optional
270 The target major version.
271 If unspecified, adapt to the current version for IPython.
272
273 Returns
274 -------
275
276 msg : dict
277 An IPython message appropriate in the new version.
278 """
279 header = msg['header']
280 if 'version' in header:
281 from_version = int(header['version'].split('.')[0])
282 else:
283 # assume last version before adding the key to the header
284 from_version = 4
285 adapter = adapters.get((from_version, to_version), None)
286 if adapter is None:
287 return msg
288 return adapter(msg)
289
290
291 # one adapter per major version from,to
292 adapters = {
293 (5,4) : V5toV4(),
294 (4,5) : V4toV5(),
295 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now