diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index a141900..3ecddda 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -34,7 +34,7 @@ from IPython.core import page from IPython.core import prefilter from IPython.core import shadowns from IPython.core import ultratb -from IPython.core.alias import AliasManager, AliasError +from IPython.core.alias import Alias, AliasManager from IPython.core.autocall import ExitAutocall from IPython.core.builtin_trap import BuiltinTrap from IPython.core.events import EventManager, available_events @@ -1502,6 +1502,7 @@ class InteractiveShell(SingletonConfigurable): found = True ospace = 'IPython internal' ismagic = True + isalias = isinstance(obj, Alias) # Last try: special-case some literals like '', [], {}, etc: if not found and oname_head in ["''",'""','[]','{}','()']: diff --git a/IPython/core/oinspect.py b/IPython/core/oinspect.py index 6e6b31b..e9d83e5 100644 --- a/IPython/core/oinspect.py +++ b/IPython/core/oinspect.py @@ -36,6 +36,7 @@ from IPython.utils import io from IPython.utils import openpy from IPython.utils import py3compat from IPython.utils.dir2 import safe_hasattr +from IPython.utils.path import compress_user from IPython.utils.text import indent from IPython.utils.wildcard import list_namespace from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable @@ -554,23 +555,6 @@ class Inspector: title = header((title+":").ljust(title_width)) out.append(cast_unicode(title) + cast_unicode(content)) return "\n".join(out) - - # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict) - pinfo_fields1 = [("Type", "type_name"), - ] - - pinfo_fields2 = [("String form", "string_form"), - ] - - pinfo_fields3 = [("Length", "length"), - ("File", "file"), - ("Definition", "definition"), - ] - - pinfo_fields_obj = [("Class docstring", "class_docstring"), - ("Init docstring", "init_docstring"), - ("Call def", "call_def"), - ("Call docstring", "call_docstring")] def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0): """Format an info dict as text""" @@ -582,41 +566,62 @@ class Inspector: field = info[key] if field is not None: displayfields.append((title, field.rstrip())) - - add_fields(self.pinfo_fields1) - - # Base class for old-style instances - if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']: - displayfields.append(("Base Class", info['base_class'].rstrip())) - - add_fields(self.pinfo_fields2) - - # Namespace - if info['namespace'] != 'Interactive': - displayfields.append(("Namespace", info['namespace'].rstrip())) - - add_fields(self.pinfo_fields3) - if info['isclass'] and info['init_definition']: - displayfields.append(("Init definition", - info['init_definition'].rstrip())) - - # Source or docstring, depending on detail level and whether - # source found. - if detail_level > 0 and info['source'] is not None: - displayfields.append(("Source", - self.format(cast_unicode(info['source'])))) - elif info['docstring'] is not None: - displayfields.append(("Docstring", info["docstring"])) - - # Constructor info for classes - if info['isclass']: - if info['init_docstring'] is not None: - displayfields.append(("Init docstring", - info['init_docstring'])) - - # Info for objects: + + if info['isalias']: + add_fields([('Repr', "string_form")]) + + elif info['ismagic']: + add_fields([("Docstring", "docstring"), + ("File", "file") + ]) + + elif info['isclass'] or is_simple_callable(obj): + # Functions, methods, classes + add_fields([("Signature", "definition"), + ("Init signature", "init_definition"), + ]) + if detail_level > 0 and info['source'] is not None: + add_fields([("Source", "source")]) + else: + add_fields([("Docstring", "docstring"), + ("Init docstring", "init_docstring"), + ]) + + add_fields([('File', 'file'), + ('Type', 'type_name'), + ]) + else: - add_fields(self.pinfo_fields_obj) + # General Python objects + add_fields([("Type", "type_name")]) + + # Base class for old-style instances + if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']: + displayfields.append(("Base Class", info['base_class'].rstrip())) + + add_fields([("String form", "string_form")]) + + # Namespace + if info['namespace'] != 'Interactive': + displayfields.append(("Namespace", info['namespace'].rstrip())) + + add_fields([("Length", "length"), + ("File", "file"), + ("Signature", "definition"), + ]) + + # Source or docstring, depending on detail level and whether + # source found. + if detail_level > 0 and info['source'] is not None: + displayfields.append(("Source", + self.format(cast_unicode(info['source'])))) + elif info['docstring'] is not None: + displayfields.append(("Docstring", info["docstring"])) + + add_fields([("Class docstring", "class_docstring"), + ("Init docstring", "init_docstring"), + ("Call signature", "call_def"), + ("Call docstring", "call_docstring")]) if displayfields: return self._format_fields(displayfields) @@ -737,7 +742,7 @@ class Inspector: binary_file = True elif fname.endswith(''): fname = 'Dynamically generated function. No source code available.' - out['file'] = fname + out['file'] = compress_user(fname) # Original source code for a callable, class or property. if detail_level: diff --git a/IPython/core/shellapp.py b/IPython/core/shellapp.py index c89fa46..7b07666 100644 --- a/IPython/core/shellapp.py +++ b/IPython/core/shellapp.py @@ -2,23 +2,10 @@ """ A mixin for :class:`~IPython.core.application.Application` classes that launch InteractiveShell instances, load extensions, etc. - -Authors -------- - -* Min Ragan-Kelley """ -#----------------------------------------------------------------------------- -# Copyright (C) 2008-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. from __future__ import absolute_import from __future__ import print_function @@ -152,10 +139,6 @@ class InteractiveShellApp(Configurable): extra_extension = Unicode('', config=True, help="dotted module name of an IPython extension to load." ) - def _extra_extension_changed(self, name, old, new): - if new: - # add to self.extensions - self.extensions.append(new) # Extensions that are always loaded (not configurable) default_extensions = List(Unicode, [u'storemagic'], config=False) @@ -269,6 +252,8 @@ class InteractiveShellApp(Configurable): try: self.log.debug("Loading IPython extensions...") extensions = self.default_extensions + self.extensions + if self.extra_extension: + extensions.append(self.extra_extension) for ext in extensions: try: self.log.info("Loading IPython extension: %s" % ext) diff --git a/IPython/core/tests/test_oinspect.py b/IPython/core/tests/test_oinspect.py index 73cb2d2..d01d0dd 100644 --- a/IPython/core/tests/test_oinspect.py +++ b/IPython/core/tests/test_oinspect.py @@ -28,6 +28,7 @@ from IPython.core.magic import (Magics, magics_class, line_magic, register_line_cell_magic) from IPython.external.decorator import decorator from IPython.testing.decorators import skipif +from IPython.utils.path import compress_user from IPython.utils import py3compat @@ -46,7 +47,7 @@ ip = get_ipython() # defined, if any code is inserted above, the following line will need to be # updated. Do NOT insert any whitespace between the next line and the function # definition below. -THIS_LINE_NUMBER = 49 # Put here the actual number of this line +THIS_LINE_NUMBER = 50 # Put here the actual number of this line def test_find_source_lines(): nt.assert_equal(oinspect.find_source_lines(test_find_source_lines), THIS_LINE_NUMBER+1) @@ -241,7 +242,7 @@ def test_info(): fname = fname[:-1] # case-insensitive comparison needed on some filesystems # e.g. Windows: - nt.assert_equal(i['file'].lower(), fname.lower()) + nt.assert_equal(i['file'].lower(), compress_user(fname.lower())) nt.assert_equal(i['definition'], None) nt.assert_equal(i['docstring'], Call.__doc__) nt.assert_equal(i['source'], None) diff --git a/IPython/html/static/services/kernels/comm.js b/IPython/html/static/services/kernels/comm.js index cffd66f..44af2a9 100644 --- a/IPython/html/static/services/kernels/comm.js +++ b/IPython/html/static/services/kernels/comm.js @@ -117,6 +117,7 @@ define([ // don't return a comm, so that further .then() functions // get an undefined comm input }); + return this.comms[content.comm_id]; }; CommManager.prototype.comm_msg = function(msg) { @@ -134,6 +135,7 @@ define([ } return comm; }); + return this.comms[content.comm_id]; }; //----------------------------------------------------------------------- diff --git a/IPython/html/static/services/kernels/kernel.js b/IPython/html/static/services/kernels/kernel.js index f414507..0c03ff2 100644 --- a/IPython/html/static/services/kernels/kernel.js +++ b/IPython/html/static/services/kernels/kernel.js @@ -41,6 +41,7 @@ define([ this.username = "username"; this.session_id = utils.uuid(); this._msg_callbacks = {}; + this._msg_queue = Promise.resolve(); this.info_reply = {}; // kernel_info_reply stored here after starting if (typeof(WebSocket) !== 'undefined') { @@ -854,19 +855,23 @@ define([ }; Kernel.prototype._handle_ws_message = function (e) { - serialize.deserialize(e.data, $.proxy(this._finish_ws_message, this)); + var that = this; + this._msg_queue = this._msg_queue.then(function() { + return serialize.deserialize(e.data); + }).then(function(msg) {return that._finish_ws_message(msg);}) + .catch(utils.reject("Couldn't process kernel message", true)); }; Kernel.prototype._finish_ws_message = function (msg) { switch (msg.channel) { case 'shell': - this._handle_shell_reply(msg); + return this._handle_shell_reply(msg); break; case 'iopub': - this._handle_iopub_message(msg); + return this._handle_iopub_message(msg); break; case 'stdin': - this._handle_input_request(msg); + return this._handle_input_request(msg); break; default: console.error("unrecognized message channel", msg.channel, msg); @@ -875,10 +880,12 @@ define([ Kernel.prototype._handle_shell_reply = function (reply) { this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply}); + var that = this; var content = reply.content; var metadata = reply.metadata; var parent_id = reply.parent_header.msg_id; var callbacks = this.get_callbacks_for_msg(parent_id); + var promise = Promise.resolve(); if (!callbacks || !callbacks.shell) { return; } @@ -888,17 +895,21 @@ define([ this._finish_shell(parent_id); if (shell_callbacks.reply !== undefined) { - shell_callbacks.reply(reply); + promise = promise.then(function() {return shell_callbacks.reply(reply)}); } if (content.payload && shell_callbacks.payload) { - this._handle_payloads(content.payload, shell_callbacks.payload, reply); + promise = promise.then(function() { + return that._handle_payloads(content.payload, shell_callbacks.payload, reply); + }); } + return promise; }; /** * @function _handle_payloads */ Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) { + var promise = []; var l = payloads.length; // Payloads are handled by triggering events because we don't want the Kernel // to depend on the Notebook or Pager classes. @@ -906,9 +917,10 @@ define([ var payload = payloads[i]; var callback = payload_callbacks[payload.source]; if (callback) { - callback(payload, msg); + promise.push(callback(payload, msg)); } } + return Promise.all(promise); }; /** @@ -1021,7 +1033,7 @@ define([ Kernel.prototype._handle_iopub_message = function (msg) { var handler = this.get_iopub_handler(msg.header.msg_type); if (handler !== undefined) { - handler(msg); + return handler(msg); } }; diff --git a/IPython/html/static/services/kernels/serialize.js b/IPython/html/static/services/kernels/serialize.js index 4da4c9a..9fb8d28 100644 --- a/IPython/html/static/services/kernels/serialize.js +++ b/IPython/html/static/services/kernels/serialize.js @@ -30,7 +30,7 @@ define([ return msg; }; - var _deserialize_binary = function(data, callback) { + var _deserialize_binary = function(data) { /** * deserialize the binary message format * callback will be called with a message whose buffers attribute @@ -39,28 +39,31 @@ define([ if (data instanceof Blob) { // data is Blob, have to deserialize from ArrayBuffer in reader callback var reader = new FileReader(); - reader.onload = function () { - var msg = _deserialize_array_buffer(this.result); - callback(msg); - }; + var promise = new Promise(function(resolve, reject) { + reader.onload = function () { + var msg = _deserialize_array_buffer(this.result); + resolve(msg); + }; + }); reader.readAsArrayBuffer(data); + return promise; } else { // data is ArrayBuffer, can deserialize directly var msg = _deserialize_array_buffer(data); - callback(msg); + return msg; } }; - var deserialize = function (data, callback) { + var deserialize = function (data) { /** - * deserialize a message and pass the unpacked message object to callback + * deserialize a message and return a promise for the unpacked message */ if (typeof data === "string") { // text JSON message - callback(JSON.parse(data)); + return Promise.resolve(JSON.parse(data)); } else { // binary message - _deserialize_binary(data, callback); + return Promise.resolve(_deserialize_binary(data)); } }; @@ -117,4 +120,4 @@ define([ serialize: serialize }; return exports; -}); \ No newline at end of file +}); diff --git a/IPython/html/static/style/ipython.min.css b/IPython/html/static/style/ipython.min.css index 3e160b5..38af689 100644 --- a/IPython/html/static/style/ipython.min.css +++ b/IPython/html/static/style/ipython.min.css @@ -1575,8 +1575,6 @@ h6:hover .anchor-link { } .widget-radio-box label { margin-top: 0px; -} -.widget-radio { margin-left: 20px; } /*# sourceMappingURL=ipython.min.css.map */ \ No newline at end of file diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index feface6..8f4c7c1 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -10358,8 +10358,6 @@ h6:hover .anchor-link { } .widget-radio-box label { margin-top: 0px; -} -.widget-radio { margin-left: 20px; } /*! diff --git a/IPython/html/static/widgets/js/widget_bool.js b/IPython/html/static/widgets/js/widget_bool.js index 0234054..dc107ec 100644 --- a/IPython/html/static/widgets/js/widget_bool.js +++ b/IPython/html/static/widgets/js/widget_bool.js @@ -59,10 +59,9 @@ define([ this.$checkbox.prop('checked', this.model.get('value')); if (options === undefined || options.updated_view != this) { - var disabled = this.model.get('disabled'); - this.$checkbox.prop('disabled', disabled); + this.$checkbox.prop("disabled", this.model.get("disabled")); - var description = this.model.get('description'); + var description = this.model.get("description"); if (description.trim().length === 0) { this.$label.hide(); } else { @@ -113,7 +112,7 @@ define([ /** * Update the contents of this view * - * Called when the model is changed. The model may have been + * Called when the model is changed. The model may have been * changed by another view or by a state update from the back-end. */ if (this.model.get('value')) { @@ -123,16 +122,16 @@ define([ } if (options === undefined || options.updated_view != this) { - - var disabled = this.model.get('disabled'); - this.$el.prop('disabled', disabled); - - var description = this.model.get('description'); + this.$el.prop("disabled", this.model.get("disabled")); this.$el.attr("title", this.model.get("tooltip")); - if (description.trim().length === 0) { + + var description = this.model.get("description"); + var icon = this.model.get("icon"); + if (description.trim().length === 0 && icon.trim().length ===0) { this.$el.html(" "); // Preserve button height } else { this.$el.text(description); + $('').prependTo(this.$el).addClass(icon); } } return ToggleButtonView.__super__.update.apply(this); diff --git a/IPython/html/static/widgets/js/widget_button.js b/IPython/html/static/widgets/js/widget_button.js index c476581..a2e6bd4 100644 --- a/IPython/html/static/widgets/js/widget_button.js +++ b/IPython/html/static/widgets/js/widget_button.js @@ -27,21 +27,19 @@ define([ /** * Update the contents of this view * - * Called when the model is changed. The model may have been + * Called when the model is changed. The model may have been * changed by another view or by a state update from the back-end. */ - var description = this.model.get('description'); + this.$el.prop("disabled", this.model.get("disabled")); this.$el.attr("title", this.model.get("tooltip")); - if (description.length === 0) { + + var description = this.model.get("description"); + var icon = this.model.get("icon"); + if (description.trim().length === 0 && icon.trim().length ===0) { this.$el.html(" "); // Preserve button height } else { this.$el.text(description); - } - - if (this.model.get('disabled')) { - this.$el.attr('disabled','disabled'); - } else { - this.$el.removeAttr('disabled'); + $('').prependTo(this.$el).addClass(icon); } return ButtonView.__super__.update.apply(this); diff --git a/IPython/html/static/widgets/js/widget_selection.js b/IPython/html/static/widgets/js/widget_selection.js index c487834..1eb7550 100644 --- a/IPython/html/static/widgets/js/widget_selection.js +++ b/IPython/html/static/widgets/js/widget_selection.js @@ -307,17 +307,21 @@ define([ if (options === undefined || options.updated_view != this) { // Add missing items to the DOM. var items = this.model.get('_options_labels'); + var icons = this.model.get('icons'); + var previous_icons = this.model.previous('icons') || []; var disabled = this.model.get('disabled'); var that = this; var item_html; _.each(items, function(item, index) { - if (item.trim().length === 0) { + if (item.trim().length === 0 && (!icons[index] || + icons[index].trim().length === 0)) { item_html = " "; } else { item_html = utils.escape_html(item); } var item_query = '[data-value="' + encodeURIComponent(item) + '"]'; var $item_element = that.$buttongroup.find(item_query); + var $icon_element = $item_element.find('.fa'); if (!$item_element.length) { $item_element = $('