##// END OF EJS Templates
update completion_ and objection_info_request...
MinRK -
Show More
@@ -1,13 +1,10 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
7 3
8 4 //============================================================================
9 5 // Utilities
10 6 //============================================================================
7
11 8 IPython.namespace('IPython.utils');
12 9
13 10 IPython.utils = (function (IPython) {
@@ -430,7 +427,7 b' IPython.utils = (function (IPython) {'
430 427 var escape_html = function (text) {
431 428 // escape text to HTML
432 429 return $("<div/>").text(text).html();
433 }
430 };
434 431
435 432
436 433 var get_body_data = function(key) {
@@ -440,6 +437,17 b' IPython.utils = (function (IPython) {'
440 437 return decodeURIComponent($('body').data(key));
441 438 };
442 439
440 var absolute_cursor_pos = function (cm, cursor) {
441 // get the absolute cursor position from CodeMirror's col, ch
442 if (!cursor) {
443 cursor = cm.getCursor();
444 }
445 var cursor_pos = cursor.ch;
446 for (var i = 0; i < cursor.line; i++) {
447 cursor_pos += cm.getLine(i).length + 1;
448 }
449 return cursor_pos;
450 };
443 451
444 452 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
445 453 var browser = (function() {
@@ -465,13 +473,13 b' IPython.utils = (function (IPython) {'
465 473 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
466 474 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
467 475 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
468 return OSName
476 return OSName;
469 477 })();
470 478
471 479 var is_or_has = function (a, b) {
472 480 // Is b a child of a or a itself?
473 481 return a.has(b).length !==0 || a.is(b);
474 }
482 };
475 483
476 484 var is_focused = function (e) {
477 485 // Is element e, or one of its children focused?
@@ -486,7 +494,7 b' IPython.utils = (function (IPython) {'
486 494 } else {
487 495 return false;
488 496 }
489 }
497 };
490 498
491 499 var log_ajax_error = function (jqXHR, status, error) {
492 500 // log ajax failures with informative messages
@@ -515,6 +523,7 b' IPython.utils = (function (IPython) {'
515 523 splitext : splitext,
516 524 escape_html : escape_html,
517 525 always_new : always_new,
526 absolute_cursor_pos : absolute_cursor_pos,
518 527 browser : browser,
519 528 platform: platform,
520 529 is_or_has : is_or_has,
@@ -1,6 +1,10 b''
1 // function completer.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 // Completer
2 5 //
3 // completer should be a class that takes an cell instance
6 // Completer is be a class that takes a cell instance.
7
4 8 var IPython = (function (IPython) {
5 9 // that will prevent us from misspelling
6 10 "use strict";
@@ -145,11 +149,14 b' var IPython = (function (IPython) {'
145 149 // we fork here and directly call finish completing if kernel is busy
146 150 if (this.skip_kernel_completion) {
147 151 this.finish_completing({
148 'matches': [],
152 matches: [],
149 153 matched_text: ""
150 154 });
151 155 } else {
152 this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
156 var cursor_pos = IPython.utils.absolute_cursor_pos(this.editor, cur);
157 this.cell.kernel.complete(this.editor.getValue(), cursor_pos,
158 $.proxy(this.finish_completing, this)
159 );
153 160 }
154 161 };
155 162
@@ -1,9 +1,6 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
7 4 //============================================================================
8 5 // Tooltip
9 6 //============================================================================
@@ -105,8 +102,8 b' var IPython = (function (IPython) {'
105 102 this.tooltip.append(this.text);
106 103
107 104 // function that will be called if you press tab 1, 2, 3... times in a row
108 this.tabs_functions = [function (cell, text) {
109 that._request_tooltip(cell, text);
105 this.tabs_functions = [function (cell, text, cursor) {
106 that._request_tooltip(cell, text, cursor);
110 107 }, function () {
111 108 that.expand();
112 109 }, function () {
@@ -131,13 +128,10 b' var IPython = (function (IPython) {'
131 128 Tooltip.prototype.showInPager = function (cell) {
132 129 // reexecute last call in pager by appending ? to show back in pager
133 130 var that = this;
134 var callbacks = {'shell' : {
135 'payload' : {
136 'page' : $.proxy(cell._open_with_pager, cell)
137 }
138 }
139 };
140 cell.kernel.execute(that.name + '?', callbacks, {'silent': false, 'store_history': true});
131 var payload = {};
132 payload.text = that._reply.content.data['text/plain'];
133
134 $([IPython.events]).trigger('open_with_text.Pager', payload);
141 135 this.remove_and_cancel_tooltip();
142 136 };
143 137
@@ -222,10 +216,9 b' var IPython = (function (IPython) {'
222 216 return Tooltip.last_token_re.exec(line);
223 217 };
224 218
225 Tooltip.prototype._request_tooltip = function (cell, line) {
219 Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) {
226 220 var callbacks = $.proxy(this._show, this);
227 var oir_token = this.extract_oir_token(line);
228 var msg_id = cell.kernel.object_info(oir_token, callbacks);
221 var msg_id = cell.kernel.object_info(text, cursor_pos, callbacks);
229 222 };
230 223
231 224 // make an imediate completion request
@@ -236,10 +229,8 b' var IPython = (function (IPython) {'
236 229 this.cancel_pending();
237 230 var editor = cell.code_mirror;
238 231 var cursor = editor.getCursor();
239 var text = editor.getRange({
240 line: cursor.line,
241 ch: 0
242 }, cursor).trim();
232 var cursor_pos = IPython.utils.absolute_cursor_pos(editor, cursor);
233 var text = cell.get_text();
243 234
244 235 this._hide_if_no_docstring = hide_if_no_docstring;
245 236
@@ -260,16 +251,16 b' var IPython = (function (IPython) {'
260 251 this.reset_tabs_function (cell, text);
261 252 }
262 253
263 // don't do anything if line beggin with '(' or is empty
264 if (text === "" || text === "(") {
265 return;
266 }
254 // don't do anything if line begins with '(' or is empty
255 // if (text === "" || text === "(") {
256 // return;
257 // }
267 258
268 this.tabs_functions[this._consecutive_counter](cell, text);
259 this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos);
269 260
270 261 // then if we are at the end of list function, reset
271 262 if (this._consecutive_counter == this.tabs_functions.length) {
272 this.reset_tabs_function (cell, text);
263 this.reset_tabs_function (cell, text, cursor);
273 264 }
274 265
275 266 return;
@@ -302,6 +293,7 b' var IPython = (function (IPython) {'
302 293 Tooltip.prototype._show = function (reply) {
303 294 // move the bubble if it is not hidden
304 295 // otherwise fade it
296 this._reply = reply;
305 297 var content = reply.content;
306 298 if (!content.found) {
307 299 // object not found, nothing to show
@@ -339,42 +331,13 b' var IPython = (function (IPython) {'
339 331 'left': posarrowleft + 'px'
340 332 });
341 333
342 // build docstring
343 var defstring = content.call_def;
344 if (!defstring) {
345 defstring = content.init_definition;
346 }
347 if (!defstring) {
348 defstring = content.definition;
349 }
350
351 var docstring = content.call_docstring;
352 if (!docstring) {
353 docstring = content.init_docstring;
354 }
355 if (!docstring) {
356 docstring = content.docstring;
357 }
358
359 if (!docstring) {
360 // For reals this time, no docstring
361 if (this._hide_if_no_docstring) {
362 return;
363 } else {
364 docstring = "<empty docstring>";
365 }
366 }
367
368 334 this._hidden = false;
369 335 this.tooltip.fadeIn('fast');
370 336 this.text.children().remove();
371 337
338 // This should support rich data types, but only text/plain for now
372 339 // Any HTML within the docstring is escaped by the fixConsole() method.
373 var pre = $('<pre/>').html(utils.fixConsole(docstring));
374 if (defstring) {
375 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
376 this.text.append(defstring_html);
377 }
340 var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain']));
378 341 this.text.append(pre);
379 342 // keep scroll top to be sure to always see the first line
380 343 this.text.scrollTop(0);
@@ -263,7 +263,8 b' var IPython = (function (IPython) {'
263 263 /**
264 264 * Get info on an object
265 265 *
266 * @param objname {string}
266 * @param code {string}
267 * @param cursor_pos {integer}
267 268 * @param callback {function}
268 269 * @method object_info
269 270 *
@@ -271,20 +272,18 b' var IPython = (function (IPython) {'
271 272 * The callback will be passed the complete `object_info_reply` message documented
272 273 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
273 274 */
274 Kernel.prototype.object_info = function (objname, callback) {
275 Kernel.prototype.object_info = function (code, cursor_pos, callback) {
275 276 var callbacks;
276 277 if (callback) {
277 278 callbacks = { shell : { reply : callback } };
278 279 }
279 280
280 if (typeof(objname) !== null && objname !== null) {
281 281 var content = {
282 oname : objname.toString(),
282 code : code,
283 cursor_pos : cursor_pos,
283 284 detail_level : 0,
284 285 };
285 286 return this.send_shell_message("object_info_request", content, callbacks);
286 }
287 return;
288 287 };
289 288
290 289 /**
@@ -360,21 +359,19 b' var IPython = (function (IPython) {'
360 359 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
361 360 *
362 361 * @method complete
363 * @param line {integer}
362 * @param code {string}
364 363 * @param cursor_pos {integer}
365 364 * @param callback {function}
366 365 *
367 366 */
368 Kernel.prototype.complete = function (line, cursor_pos, callback) {
367 Kernel.prototype.complete = function (code, cursor_pos, callback) {
369 368 var callbacks;
370 369 if (callback) {
371 370 callbacks = { shell : { reply : callback } };
372 371 }
373 372 var content = {
374 text : '',
375 line : line,
376 block : null,
377 cursor_pos : cursor_pos
373 code : code,
374 cursor_pos : cursor_pos,
378 375 };
379 376 return this.send_shell_message("complete_request", content, callbacks);
380 377 };
@@ -270,38 +270,38 b' class ShellChannel(ZMQSocketChannel):'
270 270 self._queue_send(msg)
271 271 return msg['header']['msg_id']
272 272
273 def complete(self, text, line, cursor_pos, block=None):
273 def complete(self, code, cursor_pos=0, block=None):
274 274 """Tab complete text in the kernel's namespace.
275 275
276 276 Parameters
277 277 ----------
278 text : str
279 The text to complete.
280 line : str
281 The full line of text that is the surrounding context for the
282 text to complete.
283 cursor_pos : int
284 The position of the cursor in the line where the completion was
285 requested.
286 block : str, optional
287 The full block of code in which the completion is being requested.
278 code : str
279 The context in which completion is requested.
280 Can be anything between a variable name and an entire cell.
281 cursor_pos : int, optional
282 The position of the cursor in the block of code where the completion was requested.
288 283
289 284 Returns
290 285 -------
291 286 The msg_id of the message sent.
292 287 """
293 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
288 content = dict(code=code, cursor_pos=cursor_pos)
294 289 msg = self.session.msg('complete_request', content)
295 290 self._queue_send(msg)
296 291 return msg['header']['msg_id']
297 292
298 def object_info(self, oname, detail_level=0):
293 def object_info(self, code, cursor_pos=0, detail_level=0):
299 294 """Get metadata information about an object in the kernel's namespace.
300 295
296 It is up to the kernel to determine the appropriate object to inspect.
297
301 298 Parameters
302 299 ----------
303 oname : str
304 A string specifying the object name.
300 code : str
301 The context in which info is requested.
302 Can be anything between a variable name and an entire cell.
303 cursor_pos : int, optional
304 The position of the cursor in the block of code where the info was requested.
305 305 detail_level : int, optional
306 306 The level of detail for the introspection (0-2)
307 307
@@ -309,7 +309,9 b' class ShellChannel(ZMQSocketChannel):'
309 309 -------
310 310 The msg_id of the message sent.
311 311 """
312 content = dict(oname=oname, detail_level=detail_level)
312 content = dict(code=code, cursor_pos=cursor_pos,
313 detail_level=detail_level,
314 )
313 315 msg = self.session.msg('object_info_request', content)
314 316 self._queue_send(msg)
315 317 return msg['header']['msg_id']
@@ -94,14 +94,16 b' class InProcessShellChannel(InProcessChannel):'
94 94 self._dispatch_to_kernel(msg)
95 95 return msg['header']['msg_id']
96 96
97 def complete(self, text, line, cursor_pos, block=None):
98 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
97 def complete(self, code, cursor_pos=0):
98 content = dict(code=code, cursor_pos=cursor_pos)
99 99 msg = self.client.session.msg('complete_request', content)
100 100 self._dispatch_to_kernel(msg)
101 101 return msg['header']['msg_id']
102 102
103 def object_info(self, oname, detail_level=0):
104 content = dict(oname=oname, detail_level=detail_level)
103 def object_info(self, code, cursor_pos=0, detail_level=0):
104 content = dict(code=code, cursor_pos=cursor_pos,
105 detail_level=detail_level,
106 )
105 107 msg = self.client.session.msg('object_info_request', content)
106 108 self._dispatch_to_kernel(msg)
107 109 return msg['header']['msg_id']
@@ -1,19 +1,10 b''
1 #-------------------------------------------------------------------------------
2 # Copyright (C) 2012 The IPython Development Team
3 #
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING, distributed as part of this software.
6 #-------------------------------------------------------------------------------
1 # Copyright (c) IPython Development Team.
2 # Distributed under the terms of the Modified BSD License.
7 3
8 #-----------------------------------------------------------------------------
9 # Imports
10 #-----------------------------------------------------------------------------
11 4 from __future__ import print_function
12 5
13 # Standard library imports
14 6 import unittest
15 7
16 # Local imports
17 8 from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient
18 9 from IPython.kernel.inprocess.manager import InProcessKernelManager
19 10
@@ -71,7 +62,7 b' class InProcessKernelManagerTestCase(unittest.TestCase):'
71 62 kc = BlockingInProcessKernelClient(kernel=km.kernel)
72 63 kc.start_channels()
73 64 km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
74 kc.complete('my_ba', 'my_ba', 5)
65 kc.complete('my_ba', 5)
75 66 msg = kc.get_shell_msg()
76 67 self.assertEqual(msg['header']['msg_type'], 'complete_reply')
77 68 self.assertEqual(sorted(msg['content']['matches']),
@@ -87,9 +78,12 b' class InProcessKernelManagerTestCase(unittest.TestCase):'
87 78 km.kernel.shell.user_ns['foo'] = 1
88 79 kc.object_info('foo')
89 80 msg = kc.get_shell_msg()
90 self.assertEquals(msg['header']['msg_type'], 'object_info_reply')
91 self.assertEquals(msg['content']['name'], 'foo')
92 self.assertEquals(msg['content']['type_name'], 'int')
81 self.assertEqual(msg['header']['msg_type'], 'object_info_reply')
82 content = msg['content']
83 assert content['found']
84 self.assertEqual(content['name'], 'foo')
85 text = content['data']['text/plain']
86 self.assertIn('int', text)
93 87
94 88 def test_history(self):
95 89 """ Does requesting history from an in-process kernel work?
@@ -85,6 +85,17 b' class RHeader(Reference):'
85 85 username = Unicode()
86 86 version = Version('5.0')
87 87
88 mime_pat = re.compile(r'\w+/\w+')
89
90 class MimeBundle(Reference):
91 metadata = Dict()
92 data = Dict()
93 def _data_changed(self, name, old, new):
94 for k,v in iteritems(new):
95 assert mime_pat.match(k)
96 nt.assert_is_instance(v, string_types)
97
98 # shell replies
88 99
89 100 class ExecuteReply(Reference):
90 101 execution_count = Integer()
@@ -109,31 +120,9 b' class ExecuteReplyError(Reference):'
109 120 traceback = List(Unicode)
110 121
111 122
112 class OInfoReply(Reference):
123 class OInfoReply(MimeBundle):
113 124 name = Unicode()
114 125 found = Bool()
115 ismagic = Bool()
116 isalias = Bool()
117 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
118 type_name = Unicode()
119 string_form = Unicode()
120 base_class = Unicode()
121 length = Integer()
122 file = Unicode()
123 definition = Unicode()
124 argspec = Dict()
125 init_definition = Unicode()
126 docstring = Unicode()
127 init_docstring = Unicode()
128 class_docstring = Unicode()
129 call_def = Unicode()
130 call_docstring = Unicode()
131 source = Unicode()
132
133 def check(self, d):
134 super(OInfoReply, self).check(d)
135 if d['argspec'] is not None:
136 ArgSpec().check(d['argspec'])
137 126
138 127
139 128 class ArgSpec(Reference):
@@ -173,25 +162,12 b' class Stream(Reference):'
173 162 data = Unicode()
174 163
175 164
176 mime_pat = re.compile(r'\w+/\w+')
177
178 class DisplayData(Reference):
165 class DisplayData(MimeBundle):
179 166 source = Unicode()
180 metadata = Dict()
181 data = Dict()
182 def _data_changed(self, name, old, new):
183 for k,v in iteritems(new):
184 assert mime_pat.match(k)
185 nt.assert_is_instance(v, string_types)
186 167
187 168
188 class ExecuteResult(Reference):
169 class ExecuteResult(MimeBundle):
189 170 execution_count = Integer()
190 data = Dict()
191 def _data_changed(self, name, old, new):
192 for k,v in iteritems(new):
193 assert mime_pat.match(k)
194 nt.assert_is_instance(v, string_types)
195 171
196 172
197 173 references = {
@@ -334,8 +310,10 b' def test_oinfo_found():'
334 310 validate_message(reply, 'object_info_reply', msg_id)
335 311 content = reply['content']
336 312 assert content['found']
337 argspec = content['argspec']
338 nt.assert_is(argspec, None)
313 nt.assert_equal(content['name'], 'a')
314 text = content['data']['text/plain']
315 nt.assert_in('Type:', text)
316 nt.assert_in('Docstring:', text)
339 317
340 318
341 319 def test_oinfo_detail():
@@ -343,14 +321,15 b' def test_oinfo_detail():'
343 321
344 322 msg_id, reply = execute(code='ip=get_ipython()')
345 323
346 msg_id = KC.object_info('ip.object_inspect', detail_level=2)
324 msg_id = KC.object_info('ip.object_inspect', cursor_pos=10, detail_level=1)
347 325 reply = KC.get_shell_msg(timeout=TIMEOUT)
348 326 validate_message(reply, 'object_info_reply', msg_id)
349 327 content = reply['content']
350 328 assert content['found']
351 argspec = content['argspec']
352 nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec)
353 nt.assert_equal(argspec['defaults'], [0])
329 nt.assert_equal(content['name'], 'ip.object_inspect')
330 text = content['data']['text/plain']
331 nt.assert_in('Definition:', text)
332 nt.assert_in('Source:', text)
354 333
355 334
356 335 def test_oinfo_not_found():
@@ -368,7 +347,7 b' def test_complete():'
368 347
369 348 msg_id, reply = execute(code="alpha = albert = 5")
370 349
371 msg_id = KC.complete('al', 'al', 2)
350 msg_id = KC.complete('al', 2)
372 351 reply = KC.get_shell_msg(timeout=TIMEOUT)
373 352 validate_message(reply, 'complete_reply', msg_id)
374 353 matches = reply['content']['matches']
@@ -1,4 +1,3 b''
1 #!/usr/bin/env python
2 1 """An interactive kernel that talks to frontends over 0MQ."""
3 2
4 3 # Copyright (c) IPython Development Team.
@@ -28,6 +27,7 b' from IPython.core import release'
28 27 from IPython.utils import py3compat
29 28 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
30 29 from IPython.utils.jsonutil import json_clean
30 from IPython.utils.tokenutil import token_at_cursor
31 31 from IPython.utils.traitlets import (
32 32 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
33 33 Type, Bool,
@@ -243,6 +243,7 b' class Kernel(Configurable):'
243 243 else:
244 244 # ensure default_int_handler during handler call
245 245 sig = signal(SIGINT, default_int_handler)
246 self.log.debug("%s: %s", msg_type, msg)
246 247 try:
247 248 handler(stream, idents, msg)
248 249 except Exception:
@@ -489,7 +490,11 b' class Kernel(Configurable):'
489 490 self._publish_status(u'idle', parent)
490 491
491 492 def complete_request(self, stream, ident, parent):
492 txt, matches = self._complete(parent)
493 content = parent['content']
494 code = content['code']
495 cursor_pos = content['cursor_pos']
496
497 txt, matches = self.shell.complete('', code, cursor_pos)
493 498 matches = {'matches' : matches,
494 499 'matched_text' : txt,
495 500 'status' : 'ok'}
@@ -500,13 +505,25 b' class Kernel(Configurable):'
500 505
501 506 def object_info_request(self, stream, ident, parent):
502 507 content = parent['content']
503 object_info = self.shell.object_inspect(content['oname'],
504 detail_level = content.get('detail_level', 0)
508
509 name = token_at_cursor(content['code'], content['cursor_pos'])
510 info = self.shell.object_inspect(name)
511
512 reply_content = {'status' : 'ok'}
513 reply_content['data'] = data = {}
514 reply_content['metadata'] = {}
515 reply_content['name'] = name
516 reply_content['found'] = info['found']
517 if info['found']:
518 info_text = self.shell.object_inspect_text(
519 name,
520 detail_level=content.get('detail_level', 0),
505 521 )
522 reply_content['data']['text/plain'] = info_text
506 523 # Before we send this object over, we scrub it for JSON usage
507 oinfo = json_clean(object_info)
524 reply_content = json_clean(reply_content)
508 525 msg = self.session.send(stream, 'object_info_reply',
509 oinfo, parent, ident)
526 reply_content, parent, ident)
510 527 self.log.debug("%s", msg)
511 528
512 529 def history_request(self, stream, ident, parent):
@@ -822,19 +839,6 b' class Kernel(Configurable):'
822 839 raise EOFError
823 840 return value
824 841
825 def _complete(self, msg):
826 c = msg['content']
827 try:
828 cpos = int(c['cursor_pos'])
829 except:
830 # If we don't get something that we can convert to an integer, at
831 # least attempt the completion guessing the cursor is at the end of
832 # the text, if there's any, and otherwise of the line
833 cpos = len(c['text'])
834 if cpos==0:
835 cpos = len(c['line'])
836 return self.shell.complete(c['text'], c['line'], cpos)
837
838 842 def _at_shutdown(self):
839 843 """Actions taken at shutdown by the kernel, called by python's atexit.
840 844 """
@@ -128,7 +128,8 b' def test_embed_kernel_basic():'
128 128 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
129 129 content = msg['content']
130 130 nt.assert_true(content['found'])
131 nt.assert_equal(content['string_form'], u'10')
131 text = content['data']['text/plain']
132 nt.assert_in('10', text)
132 133
133 134 def test_embed_kernel_namespace():
134 135 """IPython.embed_kernel() inherits calling namespace"""
@@ -148,14 +149,16 b' def test_embed_kernel_namespace():'
148 149 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
149 150 content = msg['content']
150 151 nt.assert_true(content['found'])
151 nt.assert_equal(content['string_form'], u'5')
152 text = content['data']['text/plain']
153 nt.assert_in(u'5', text)
152 154
153 155 # oinfo b (str)
154 156 msg_id = client.object_info('b')
155 157 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
156 158 content = msg['content']
157 159 nt.assert_true(content['found'])
158 nt.assert_equal(content['string_form'], u'hi there')
160 text = content['data']['text/plain']
161 nt.assert_in(u'hi there', text)
159 162
160 163 # oinfo c (undefined)
161 164 msg_id = client.object_info('c')
@@ -184,7 +187,8 b' def test_embed_kernel_reentrant():'
184 187 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
185 188 content = msg['content']
186 189 nt.assert_true(content['found'])
187 nt.assert_equal(content['string_form'], unicode_type(i))
190 text = content['data']['text/plain']
191 nt.assert_in(unicode_type(i), text)
188 192
189 193 # exit from embed_kernel
190 194 client.execute("get_ipython().exit_now = True")
@@ -14,7 +14,8 b' def test_ipython_start_kernel_userns():'
14 14 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
15 15 content = msg['content']
16 16 assert content['found']
17 nt.assert_equal(content['string_form'], u'123')
17 text = content['data']['text/plain']
18 nt.assert_in(u'123', text)
18 19
19 20 # user_module should be an instance of DummyMod
20 21 msg_id = client.execute("usermod = get_ipython().user_module")
@@ -25,7 +26,8 b' def test_ipython_start_kernel_userns():'
25 26 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
26 27 content = msg['content']
27 28 assert content['found']
28 nt.assert_in('DummyMod', content['string_form'])
29 text = content['data']['text/plain']
30 nt.assert_in(u'DummyMod', text)
29 31
30 32 def test_ipython_start_kernel_no_userns():
31 33 # Issue #4188 - user_ns should be passed to shell as None, not {}
@@ -42,4 +44,5 b' def test_ipython_start_kernel_no_userns():'
42 44 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
43 45 content = msg['content']
44 46 assert content['found']
45 nt.assert_not_in('DummyMod', content['string_form'])
47 text = content['data']['text/plain']
48 nt.assert_not_in(u'DummyMod', text)
@@ -1585,6 +1585,15 b" class ConsoleWidget(MetaQObjectHasTraits('NewBase', (LoggingConfigurable, QtGui."
1585 1585 text = self._get_block_plain_text(cursor.block())
1586 1586 return text[len(prompt):]
1587 1587
1588 def _get_input_buffer_cursor_pos(self):
1589 """Return the cursor position within the input buffer."""
1590 cursor = self._control.textCursor()
1591 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
1592 input_buffer = cursor.selection().toPlainText()
1593
1594 # Don't count continuation prompts
1595 return len(input_buffer.replace('\n' + self._continuation_prompt, '\n'))
1596
1588 1597 def _get_input_buffer_cursor_prompt(self):
1589 1598 """ Returns the (plain text) prompt for line of the input buffer that
1590 1599 contains the cursor, or None if there is no such line.
@@ -727,17 +727,10 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
727 727 # Decide if it makes sense to show a call tip
728 728 if not self.enable_calltips:
729 729 return False
730 cursor = self._get_cursor()
731 cursor.movePosition(QtGui.QTextCursor.Left)
732 if cursor.document().characterAt(cursor.position()) != '(':
733 return False
734 context = self._get_context(cursor)
735 if not context:
736 return False
737
730 cursor_pos = self._get_input_buffer_cursor_pos()
731 code = self.input_buffer
738 732 # Send the metadata request to the kernel
739 name = '.'.join(context)
740 msg_id = self.kernel_client.object_info(name)
733 msg_id = self.kernel_client.object_info(code, cursor_pos)
741 734 pos = self._get_cursor().position()
742 735 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
743 736 return True
@@ -749,10 +742,9 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
749 742 if context:
750 743 # Send the completion request to the kernel
751 744 msg_id = self.kernel_client.complete(
752 '.'.join(context), # text
753 self._get_input_buffer_cursor_line(), # line
754 self._get_input_buffer_cursor_column(), # cursor_pos
755 self.input_buffer) # block
745 code=self.input_buffer,
746 cursor_pos=self._get_input_buffer_cursor_pos(),
747 )
756 748 pos = self._get_cursor().position()
757 749 info = self._CompletionRequest(msg_id, pos)
758 750 self._request_info['complete'] = info
@@ -327,24 +327,6 b' class IPythonWidget(FrontendWidget):'
327 327 # 'FrontendWidget' protected interface
328 328 #---------------------------------------------------------------------------
329 329
330 def _complete(self):
331 """ Reimplemented to support IPython's improved completion machinery.
332 """
333 # We let the kernel split the input line, so we *always* send an empty
334 # text field. Readline-based frontends do get a real text field which
335 # they can use.
336 text = ''
337
338 # Send the completion request to the kernel
339 msg_id = self.kernel_client.shell_channel.complete(
340 text, # text
341 self._get_input_buffer_cursor_line(), # line
342 self._get_input_buffer_cursor_column(), # cursor_pos
343 self.input_buffer) # block
344 pos = self._get_cursor().position()
345 info = self._CompletionRequest(msg_id, pos)
346 self._request_info['complete'] = info
347
348 330 def _process_execute_error(self, msg):
349 331 """ Reimplemented for IPython-style traceback formatting.
350 332 """
@@ -1,6 +1,9 b''
1 """Adapt readline completer interface to make ZMQ request.
2 """
3 1 # -*- coding: utf-8 -*-
2 """Adapt readline completer interface to make ZMQ request."""
3
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
6
4 7 try:
5 8 from queue import Empty # Py 3
6 9 except ImportError:
@@ -33,8 +36,10 b' class ZMQCompleter(IPCompleter):'
33 36
34 37 # send completion request to kernel
35 38 # Give the kernel up to 0.5s to respond
36 msg_id = self.client.shell_channel.complete(text=text, line=line,
37 cursor_pos=cursor_pos)
39 msg_id = self.client.shell_channel.complete(
40 code=line,
41 cursor_pos=cursor_pos,
42 )
38 43
39 44 msg = self.client.shell_channel.get_msg(timeout=self.timeout)
40 45 if msg['parent_header']['msg_id'] == msg_id:
@@ -6,11 +6,16 b' import nose.tools as nt'
6 6
7 7 from IPython.utils.tokenutil import token_at_cursor
8 8
9 def expect_token(expected, cell, column, line=0):
10 token = token_at_cursor(cell, column, line)
11
12 lines = cell.splitlines()
13 line_with_cursor = '%s|%s' % (lines[line][:column], lines[line][column:])
9 def expect_token(expected, cell, cursor_pos):
10 token = token_at_cursor(cell, cursor_pos)
11 offset = 0
12 for line in cell.splitlines():
13 if offset + len(line) >= cursor_pos:
14 break
15 else:
16 offset += len(line)
17 column = cursor_pos - offset
18 line_with_cursor = '%s|%s' % (line[:column], line[column:])
14 19 line
15 20 nt.assert_equal(token, expected,
16 21 "Excpected %r, got %r in: %s" % (
@@ -34,11 +39,13 b' def test_multiline():'
34 39 'b = hello("string", there)'
35 40 ])
36 41 expected = 'hello'
37 for i in range(4,9):
38 expect_token(expected, cell, i, 1)
42 start = cell.index(expected)
43 for i in range(start, start + len(expected)):
44 expect_token(expected, cell, i)
39 45 expected = 'there'
40 for i in range(21,27):
41 expect_token(expected, cell, i, 1)
46 start = cell.index(expected)
47 for i in range(start, start + len(expected)):
48 expect_token(expected, cell, i)
42 49
43 50 def test_attrs():
44 51 cell = "foo(a=obj.attr.subattr)"
@@ -23,7 +23,7 b' def generate_tokens(readline):'
23 23 # catch EOF error
24 24 return
25 25
26 def token_at_cursor(cell, column, line=0):
26 def token_at_cursor(cell, cursor_pos=0):
27 27 """Get the token at a given cursor
28 28
29 29 Used for introspection.
@@ -33,15 +33,13 b' def token_at_cursor(cell, column, line=0):'
33 33
34 34 cell : unicode
35 35 A block of Python code
36 column : int
37 The column of the cursor offset, where the token should be found
38 line : int, optional
39 The line where the token should be found (optional if cell is a single line)
36 cursor_pos : int
37 The location of the cursor in the block where the token should be found
40 38 """
41 39 cell = cast_unicode_py2(cell)
42 40 names = []
43 41 tokens = []
44 current_line = 0
42 offset = 0
45 43 for tup in generate_tokens(StringIO(cell).readline):
46 44
47 45 tok = Token(*tup)
@@ -49,7 +47,7 b' def token_at_cursor(cell, column, line=0):'
49 47 # token, text, start, end, line = tup
50 48 start_col = tok.start[1]
51 49 end_col = tok.end[1]
52 if line == current_line and start_col > column:
50 if offset + start_col > cursor_pos:
53 51 # current token starts after the cursor,
54 52 # don't consume it
55 53 break
@@ -64,13 +62,13 b' def token_at_cursor(cell, column, line=0):'
64 62 # don't inspect the lhs of an assignment
65 63 names.pop(-1)
66 64
67 if line == current_line and end_col > column:
65 if offset + end_col > cursor_pos:
68 66 # we found the cursor, stop reading
69 67 break
70 68
71 69 tokens.append(tok)
72 70 if tok.token == tokenize2.NEWLINE:
73 current_line += 1
71 offset += len(tok.line)
74 72
75 73 if names:
76 74 return names[-1]
General Comments 0
You need to be logged in to leave comments. Login now