Skip to content

Failure to initialize default stdout and stderr in the InProcessKernel #724

@rayosborn

Description

@rayosborn

I use the in-process kernel in an Jupyter shell embedded in a PyQt GUI. It is based on the QtConsole RichJupyterWidget. Here is a slimmed-down version of the code to set it up.

from qtconsole.inprocess import QtInProcessKernelManager
from qtconsole.rich_jupyter_widget import RichJupyterWidget

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, app, tree, settings, config):
        """ Create a MainWindow for the application.
        super(MainWindow, self).__init__()
        mainwindow = QtWidgets.QWidget()
        self.console = RichJupyterWidget(config=self.config)
        self.console.kernel_manager = QtInProcessKernelManager(config=self.config)
        self.console.kernel_manager.start_kernel()
        self.console.kernel_manager.kernel.gui = 'qt'
        self.console.kernel_client = self.console.kernel_manager.client()
        self.console.kernel_client.start_channels()
        self.console.show()

Calls to the QtConsole FrontendWidget function _silent_exec_callback trigger an Empty exception in the Queue module.

2021-07-12 10:53:24,760 - ERROR - Exception in GUI event loop
---------------------------------------------------------------------------
Empty                                     Traceback (most recent call last)
~/Documents/Computing/Repositories/nexpy/src/nexpy/gui/mainwindow.py in update_all_magic_menu(self)
   2037 
   2038         """
-> 2039         self.console._silent_exec_callback('get_ipython().magic("lsmagic")',
   2040                 self.populate_all_magic_menu)
   2041 

~/opt/miniconda3/envs/py38/lib/python3.8/site-packages/qtconsole/frontend_widget.py in _silent_exec_callback(self, expr, callback)
    417         # not the unique request originated from here (can use msg id ?)
    418         local_uuid = str(uuid.uuid1())
--> 419         msg_id = self.kernel_client.execute('',
    420             silent=True, user_expressions={ local_uuid:expr })
    421         self._callback_dict[local_uuid] = callback

~/Documents/Computing/Repositories/ipykernel/ipykernel/inprocess/client.py in execute(self, code, silent, store_history, user_expressions, allow_stdin)
    108                        allow_stdin=allow_stdin)
    109         msg = self.session.msg('execute_request', content)
--> 110         self._dispatch_to_kernel(msg)
    111         return msg['header']['msg_id']
    112 

~/Documents/Computing/Repositories/ipykernel/ipykernel/inprocess/client.py in _dispatch_to_kernel(self, msg)
    178         loop = asyncio.get_event_loop()
    179         loop.run_until_complete(kernel.dispatch_shell(msg_parts))
--> 180         idents, reply_msg = self.session.recv(stream, copy=False)
    181         self.shell_channel.call_handlers_later(reply_msg)
    182 

~/opt/miniconda3/envs/py38/lib/python3.8/site-packages/jupyter_client/session.py in recv(self, socket, mode, content, copy)
    807             socket = socket.socket
    808         try:
--> 809             msg_list = socket.recv_multipart(mode, copy=copy)
    810         except zmq.ZMQError as e:
    811             if e.errno == zmq.EAGAIN:

~/Documents/Computing/Repositories/ipykernel/ipykernel/inprocess/socket.py in recv_multipart(self, flags, copy, track)
     30 
     31     def recv_multipart(self, flags=0, copy=True, track=False):
---> 32         return self.queue.get_nowait()
     33 
     34     def send_multipart(self, msg_parts, flags=0, copy=True, track=False):

~/opt/miniconda3/envs/py38/lib/python3.8/queue.py in get_nowait(self)
    196         raise the Empty exception.
    197         '''
--> 198         return self.get(block=False)
    199 
    200     # Override these methods to implement other queue organizations

~/opt/miniconda3/envs/py38/lib/python3.8/queue.py in get(self, block, timeout)
    165             if not block:
    166                 if not self._qsize():
--> 167                     raise Empty
    168             elif timeout is None:
    169                 while not self._qsize():

Empty: 

I haven't been able to diagnose the issue, but the problem appears to be in the following lines in ipykernel InProcessKernelClient function, _dispatch_to_kernel in the following lines:

        stream = kernel.shell_stream
        self.session.send(stream, msg)
        msg_parts = stream.recv_multipart()
        loop = asyncio.get_event_loop()
        loop.run_until_complete(kernel.dispatch_shell(msg_parts))
        idents, reply_msg = self.session.recv(stream, copy=False)
        self.shell_channel.call_handlers_later(reply_msg)

If I monitor the DummySocket send_multipart and recv_multipart functions, there is a send and receive pair triggered by lines 2 and 3 above, followed by another send and receive pair triggered by lines 5 and 6, during a couple of successful calls to _dispatch_to_kernel when starting the channels. However, the next call, which has silent=True fails because the kernel.dispatch_shell function doesn't send a message in line 5 so the queue is empty in line 6. I don't know if the silent option is the key difference. It might be a problem with using a different handler in dispatch_shell.

I am happy to try and extract whatever information is helpful from my debugger, but I don't know enough about the message passing machinery to know what to look for.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions