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 |
// |
|
|
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 |
|
|
|
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 |
|
|
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, |
|
|
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 beg |
|
|
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( |
|
|
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 |
|
|
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 ( |
|
|
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 |
|
|
282 | oname : objname.toString(), | |
|
282 | code : code, | |
|
283 | cursor_pos : cursor_pos, | |
|
283 | 284 |
|
|
284 | 285 |
|
|
285 | 286 |
|
|
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 |
|
|
362 | * @param code {string} | |
|
364 | 363 | * @param cursor_pos {integer} |
|
365 | 364 | * @param callback {function} |
|
366 | 365 | * |
|
367 | 366 | */ |
|
368 |
Kernel.prototype.complete = function ( |
|
|
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 |
|
|
|
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, |
|
|
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 |
|
|
|
279 |
The |
|
|
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( |
|
|
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, |
|
|
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 |
|
|
|
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, |
|
|
98 |
content = dict( |
|
|
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, |
|
|
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', |
|
|
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.assertEqual |
|
|
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( |
|
|
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( |
|
|
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= |
|
|
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', |
|
|
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 |
|
|
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 |
|
|
|
524 | reply_content = json_clean(reply_content) | |
|
508 | 525 | msg = self.session.send(stream, 'object_info_reply', |
|
509 |
|
|
|
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_ |
|
|
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 |
|
|
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( |
|
|
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, c |
|
|
10 |
token = token_at_cursor(cell, c |
|
|
11 | ||
|
12 |
|
|
|
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, c |
|
|
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 |
c |
|
|
37 |
The co |
|
|
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