diff --git a/IPython/frontend/qt/console/frontend_widget.py b/IPython/frontend/qt/console/frontend_widget.py index a1a6dab..c6dad15 100644 --- a/IPython/frontend/qt/console/frontend_widget.py +++ b/IPython/frontend/qt/console/frontend_widget.py @@ -1,4 +1,5 @@ # Standard library imports +from collections import namedtuple import signal import sys @@ -90,6 +91,9 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): executed = QtCore.pyqtSignal(object) # Protected class variables. + _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos']) + _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos']) + _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind']) _input_splitter_class = InputSplitter #--------------------------------------------------------------------------- @@ -108,6 +112,7 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): self._input_splitter = self._input_splitter_class(input_mode='block') self._kernel_manager = None self._possible_kernel_restart = False + self._request_info = {} # Configure the ConsoleWidget. self.tab_width = 4 @@ -149,9 +154,10 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): # /end tmp code # FIXME - user_variables/expressions are not visible in API above us. - self.kernel_manager.xreq_channel.execute(source, hidden, - user_variables, - user_expressions) + msg_id = self.kernel_manager.xreq_channel.execute(source, hidden, + user_variables, + user_expressions) + self._request_info['execute'] = self._ExecutionRequest(msg_id, 'user') self._hidden = hidden def _prompt_started_hook(self): @@ -216,8 +222,9 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): """ Handle replies for tab completion. """ cursor = self._get_cursor() - if rep['parent_header']['msg_id'] == self._complete_id and \ - cursor.position() == self._complete_pos: + info = self._request_info.get('complete') + if info and info.id == rep['parent_header']['msg_id'] and \ + info.pos == cursor.position(): text = '.'.join(self._get_context()) cursor.movePosition(QtGui.QTextCursor.Left, n=len(text)) self._complete_with_items(cursor, rep['content']['matches']) @@ -225,7 +232,9 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): def _handle_execute_reply(self, msg): """ Handles replies for code execution. """ - if not self._hidden: + info = self._request_info.get('execute') + if info and info.id == msg['parent_header']['msg_id'] and \ + not self._hidden: # Make sure that all output from the SUB channel has been processed # before writing a new prompt. self.kernel_manager.sub_channel.flush() @@ -272,8 +281,9 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): """ Handle replies for call tips. """ cursor = self._get_cursor() - if rep['parent_header']['msg_id'] == self._call_tip_id and \ - cursor.position() == self._call_tip_pos: + info = self._request_info.get('call_tip') + if info and info.id == rep['parent_header']['msg_id'] and \ + info.pos == cursor.position(): doc = rep['content']['docstring'] if doc: self._call_tip_widget.show_docstring(doc) @@ -378,8 +388,9 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): # Send the metadata request to the kernel name = '.'.join(context) - self._call_tip_id = self.kernel_manager.xreq_channel.object_info(name) - self._call_tip_pos = self._get_cursor().position() + msg_id = self.kernel_manager.xreq_channel.object_info(name) + pos = self._get_cursor().position() + self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos) return True def _complete(self): @@ -388,12 +399,14 @@ class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): context = self._get_context() if context: # Send the completion request to the kernel - self._complete_id = self.kernel_manager.xreq_channel.complete( + msg_id = self.kernel_manager.xreq_channel.complete( '.'.join(context), # text self._get_input_buffer_cursor_line(), # line self._get_input_buffer_cursor_column(), # cursor_pos self.input_buffer) # block - self._complete_pos = self._get_cursor().position() + pos = self._get_cursor().position() + info = self._CompletionRequest(msg_id, pos) + self._request_info['complete'] = info def _get_banner(self): """ Gets a banner to display at the beginning of a session. diff --git a/IPython/frontend/qt/console/ipython_widget.py b/IPython/frontend/qt/console/ipython_widget.py index 1b3be7b..b889ae3 100644 --- a/IPython/frontend/qt/console/ipython_widget.py +++ b/IPython/frontend/qt/console/ipython_widget.py @@ -21,7 +21,6 @@ from PyQt4 import QtCore, QtGui # Local imports from IPython.core.inputsplitter import IPythonInputSplitter from IPython.core.usage import default_banner -from IPython.utils import io from IPython.utils.traitlets import Bool, Str from frontend_widget import FrontendWidget @@ -134,8 +133,9 @@ class IPythonWidget(FrontendWidget): """ Reimplemented to support IPython's improved completion machinery. """ cursor = self._get_cursor() - if rep['parent_header']['msg_id'] == self._complete_id and \ - cursor.position() == self._complete_pos: + info = self._request_info.get('complete') + if info and info.id == rep['parent_header']['msg_id'] and \ + info.pos == cursor.position(): matches = rep['content']['matches'] text = rep['content']['matched_text'] @@ -151,6 +151,17 @@ class IPythonWidget(FrontendWidget): cursor.movePosition(QtGui.QTextCursor.Left, n=len(text)) self._complete_with_items(cursor, matches) + def _handle_execute_reply(self, msg): + """ Reimplemented to support prompt requests. + """ + info = self._request_info.get('execute') + if info and info.id == msg['parent_header']['msg_id']: + if info.kind == 'prompt': + number = msg['content']['execution_count'] + 1 + self._show_interpreter_prompt(number) + else: + super(IPythonWidget, self)._handle_execute_reply(msg) + def _handle_history_reply(self, msg): """ Implemented to handle history replies, which are only supported by the IPython kernel. @@ -159,12 +170,6 @@ class IPythonWidget(FrontendWidget): items = [ history_dict[key] for key in sorted(history_dict.keys()) ] self._set_history(items) - def _handle_prompt_reply(self, msg): - """ Implemented to handle prompt number replies, which are only - supported by the IPython kernel. - """ - self._show_interpreter_prompt(msg['content']['execution_count']) - def _handle_pyout(self, msg): """ Reimplemented for IPython-style "display hook". """ @@ -204,12 +209,14 @@ class IPythonWidget(FrontendWidget): text = '' # Send the completion request to the kernel - self._complete_id = self.kernel_manager.xreq_channel.complete( + msg_id = self.kernel_manager.xreq_channel.complete( text, # text self._get_input_buffer_cursor_line(), # line self._get_input_buffer_cursor_column(), # cursor_pos self.input_buffer) # block - self._complete_pos = self._get_cursor().position() + pos = self._get_cursor().position() + info = self._CompletionRequest(msg_id, pos) + self._request_info['complete'] = info def _get_banner(self): """ Reimplemented to return IPython's default banner. @@ -254,10 +261,10 @@ class IPythonWidget(FrontendWidget): """ # If a number was not specified, make a prompt number request. if number is None: - # FIXME - fperez: this should be a silent code request - number = 1 - ##self.kernel_manager.xreq_channel.prompt() - ##return + msg_id = self.kernel_manager.xreq_channel.execute('', silent=True) + info = self._ExecutionRequest(msg_id, 'prompt') + self._request_info['execute'] = info + return # Show a new prompt and save information about it so that it can be # updated later if the prompt number turns out to be wrong. @@ -276,7 +283,6 @@ class IPythonWidget(FrontendWidget): """ # Update the old prompt number if necessary. content = msg['content'] - ##io.rprint('_show_interpreter_prompt_for_reply\n', content) # dbg previous_prompt_number = content['execution_count'] if self._previous_prompt_obj and \ self._previous_prompt_obj.number != previous_prompt_number: diff --git a/IPython/zmq/kernelmanager.py b/IPython/zmq/kernelmanager.py index 79f0844..ee5c3d1 100644 --- a/IPython/zmq/kernelmanager.py +++ b/IPython/zmq/kernelmanager.py @@ -206,7 +206,6 @@ class XReqSocketChannel(ZmqSocketChannel): If set, the kernel will execute the code as quietly possible. user_variables : list, optional - A list of variable names to pull from the user's namespace. They will come back as a dict with these names as keys and their :func:`repr` as values.