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