jupyter / jupyter_kernel_test

A tool for testing Jupyter kernels

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Version 0.3: code_page_something not optional?

martinRenou opened this issue · comments

Using the last release of jupyter_kernel_test (0.3), I found that not testing code_page_something results in an error.

The following example works fine:

import unittest
import jupyter_kernel_test


class PythonTests(jupyter_kernel_test.KernelTests):

    kernel_name = "python3"
    language_name = "python"

    code_page_something = "?print"

    def test_python_stdout(self):
        reply, output_msgs = self.execute_helper(code='print(3)')
        self.assertEqual(output_msgs[0]['msg_type'], 'stream')
        self.assertEqual(output_msgs[0]['content']['name'], 'stdout')
        self.assertEqual(output_msgs[0]['content']['text'], '3\n')

if __name__ == '__main__':
    unittest.main()

But this one doesn't work:

import unittest
import jupyter_kernel_test


class PythonTests(jupyter_kernel_test.KernelTests):

    kernel_name = "python3"
    language_name = "python"

    def test_python_stdout(self):
        reply, output_msgs = self.execute_helper(code='print(3)')
        self.assertEqual(output_msgs[0]['msg_type'], 'stream')
        self.assertEqual(output_msgs[0]['content']['name'], 'stdout')
        self.assertEqual(output_msgs[0]['content']['text'], '3\n')

if __name__ == '__main__':
    unittest.main()

And results in this error:

test_xeus_python_kernel.py:13: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../../miniconda3/envs/dev/lib/python3.6/site-packages/jupyter_kernel_test/__init__.py:67: in execute_helper
    validate_message(busy_msg, 'status', msg_id)
../../../../miniconda3/envs/dev/lib/python3.6/site-packages/jupyter_kernel_test/messagespec.py:172: in validate_message
    nt.assert_equal(msg['parent_header']['msg_id'], parent)
E   AssertionError: 'd80cd30e-c108ad4a7797aaf0152d3e30' != 'cdcc4f0b-f60040a52145d517bdad7f23'
E   - d80cd30e-c108ad4a7797aaf0152d3e30
E   + cdcc4f0b-f60040a52145d517bdad7f23

After printing messages, I guess that the kernel_info_reply is not used in the second case, and instead of receiving the execute_reply it first receives the kernel_info_reply, resulting in this error.

This has been tested with three different kernel implementations (python3, xeus-cling, xeus-python)

Yup, it's definitely meant to be optional. Can you try on master? There have been some major changes since 0.3.

I have a weird error on master: jupyter_kernel_mgmt.kernelspec.NoSuchKernel: No such kernel named
Kernel name is apparently an empty string. I'm using jupyter_kernel_mgmt 0.3.0.

Hmm, strange. Can you show the full traceback? I've been changing jupyter_kernel_mgmt quite a bit recently as I tried to adapt nbconvert and notebook to use it; it's possible the code in this repo needs some changes to match.

Sure:

_____________________________________________________________________________ ERROR at setup of KernelTests.test_kernel_info ______________________________________________________________________________

self = <jupyter_kernel_mgmt.kernelspec.KernelSpecManager object at 0x7f623d984d30>, kernel_name = ''

    def get_kernel_spec(self, kernel_name):
        """Returns a :class:`KernelSpec` instance for the given kernel_name.
    
        Raises :exc:`NoSuchKernel` if the given kernel name is not found.
        """
        d = self.find_kernel_specs()
        try:
>           resource_dir = d[kernel_name.lower()]
E           KeyError: ''

../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_mgmt/kernelspec.py:160: KeyError

During handling of the above exception, another exception occurred:

cls = <class 'jupyter_kernel_test.KernelTests'>

    @classmethod
    def setUpClass(cls):
>       conn_info, km = cls.launch_kernel()

../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_test/__init__.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_test/__init__.py:27: in launch_kernel
    return kf.launch(name=name)
../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_mgmt/discovery.py:173: in launch
    return provider.launch(kernel_id, cwd)
../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_mgmt/discovery.py:71: in launch
    spec = self.ksm.get_kernel_spec(name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <jupyter_kernel_mgmt.kernelspec.KernelSpecManager object at 0x7f623d984d30>, kernel_name = ''

    def get_kernel_spec(self, kernel_name):
        """Returns a :class:`KernelSpec` instance for the given kernel_name.
    
        Raises :exc:`NoSuchKernel` if the given kernel name is not found.
        """
        d = self.find_kernel_specs()
        try:
            resource_dir = d[kernel_name.lower()]
        except KeyError:
>           raise NoSuchKernel(kernel_name)
E           jupyter_kernel_mgmt.kernelspec.NoSuchKernel: No such kernel named

../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_mgmt/kernelspec.py:162: NoSuchKernel

That looks like cls.kernel_name is not set. Is it possible there's a typo? jupyter_kernel_test should probably check for that and throw a more informative error.

I'm using this test:

import unittest
from jupyter_kernel_test import KernelTests


class PythonTests(KernelTests):

    kernel_name = "python3"
    language_name = "python"

    code_page_something = "?print"

    def test_python_stdout(self):
        reply, output_msgs = self.execute_helper(code='print(3)')
        self.assertEqual(output_msgs[0]['msg_type'], 'stream')
        self.assertEqual(output_msgs[0]['content']['name'], 'stdout')
        self.assertEqual(output_msgs[0]['content']['text'], '3\n')

if __name__ == '__main__':
    unittest.main()

It's really strange.

Ah, I think I remember this. If you do from jupyter_kernel_test import KernelTests , then it tries to run the base class as tests as well as your subclass. Maybe we should tweak the name to avoid that.

Nice catch! I can open an other specific issue for this one.
Ok so now it fails differently:

_____________________________________________________________________________________ PythonTests.test_python_stdout ______________________________________________________________________________________

self = <test_kernel.PythonTests testMethod=test_python_stdout>

    def test_python_stdout(self):
>       reply, output_msgs = self.execute_helper(code='print(3)')

test_kernel.py:11: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_test/__init__.py:68: in execute_helper
    self.kc.loop_client.add_handler('iopub', output_msgs.append)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <jupyter_kernel_mgmt.client.IOLoopKernelClient object at 0x7f30b050acc0>, handler = 'iopub', channels = <built-in method append of list object at 0x7f30aa56fa88>

    def add_handler(self, handler, channels):
        """Add a callback for received messages on one or more channels.
    
        Parameters
        ----------
    
        handler : function
          Will be called for each message received with the message dictionary
          as a single argument.
        channels : set or str
          Channel names: 'shell', 'iopub', 'stdin' or 'control'
        """
        if isinstance(channels, str):
            channels = {channels}
>       invalid_channels = channels - self.handler_channels
E       TypeError: unsupported operand type(s) for -: 'builtin_function_or_method' and 'frozenset'

../../../../miniconda3/envs/test/lib/python3.7/site-packages/jupyter_kernel_mgmt/client.py:134: TypeError

I'm sorry I'm just reporting, not really helping... I'm on other stuff at the same time

Actually the issue has already been reported: #7

Yup, that was me changing APIs around. The parameters for add_handler need to be switched around.

The error from add_handler should be fixed now in master (e7844c7).

Thanks! It works! I'm not seeing the issue anymore. There was still one thing, I guess the README is not valid anymore:

def test_mykernel_stderr(self):
        reply, output_msgs = self.execute_helper(code='print_err "oops"')
        self.assertEqual(output_msgs[0]['msg_type'], 'stream')
        self.assertEqual(output_msgs[0]['content']['name'], 'stderr')
        self.assertEqual(output_msgs[0]['content']['text'], 'oops\n')

should be replaced by

def test_mykernel_stderr(self):
        reply, output_msgs = self.execute_helper(code='print_err "oops"')
        # self.assertEqual(output_msgs[0]['msg_type'], 'stream')
        self.assertEqual(output_msgs[0].content['name'], 'stderr')
        self.assertEqual(output_msgs[0].content['text'], 'oops\n')

I'm note sure how to adapt the msg_type line to the new version of master

Thank you for your help! Do you plan to make a release?

I might wait a while for a release, because the new kernel APIs are still in flux (hence these problems ;-). I'm working on integrating them into notebook and nbconvert as well, but it's not settled yet.

The msg_type bit should become .header['msg_type']. The old API copied it to the top-level for convenience, whereas the new one doesn't.

Do you want to make a PR to update the README? You might also want to add a note that master is different to 0.3, because it might be a while before it's ready for a new release. We can always make a branch off before the new APIs were landed (in 41850aa) if it becomes necessary.

Sure, I'll do that :)