2022-05-23 00:16:32 +04:00

157 lines
4.9 KiB
Python

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import asyncio
import sys
import unittest
from contextlib import contextmanager
from io import StringIO
import pytest
import tornado
from IPython.utils.io import capture_output
from jupyter_client.session import Session
from ipykernel.inprocess.blocking import BlockingInProcessKernelClient
from ipykernel.inprocess.ipkernel import InProcessKernel
from ipykernel.inprocess.manager import InProcessKernelManager
from ipykernel.tests.utils import assemble_output
orig_msg = Session.msg
def _init_asyncio_patch():
"""set default asyncio policy to be compatible with tornado
Tornado 6 (at least) is not compatible with the default
asyncio implementation on Windows
Pick the older SelectorEventLoopPolicy on Windows
if the known-incompatible default policy is in use.
do this as early as possible to make it a low priority and overrideable
ref: https://github.com/tornadoweb/tornado/issues/2608
FIXME: if/when tornado supports the defaults in asyncio,
remove and bump tornado requirement for py38
"""
if (
sys.platform.startswith("win")
and sys.version_info >= (3, 8)
and tornado.version_info < (6, 1)
):
import asyncio
try:
from asyncio import (
WindowsProactorEventLoopPolicy,
WindowsSelectorEventLoopPolicy,
)
except ImportError:
pass
# not affected
else:
if type(asyncio.get_event_loop_policy()) is WindowsProactorEventLoopPolicy:
# WindowsProactorEventLoopPolicy is not compatible with tornado 6
# fallback to the pre-3.8 default of Selector
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
def _inject_cell_id(_self, *args, **kwargs):
"""
This patch jupyter_client.session:Session.msg to add a cell_id to the return message metadata
"""
assert isinstance(_self, Session)
res = orig_msg(_self, *args, **kwargs)
assert "cellId" not in res["metadata"]
res["metadata"]["cellId"] = "test_cell_id"
return res
@contextmanager
def patch_cell_id():
try:
Session.msg = _inject_cell_id
yield
finally:
Session.msg = orig_msg
class InProcessKernelTestCase(unittest.TestCase):
def setUp(self):
_init_asyncio_patch()
self.km = InProcessKernelManager()
self.km.start_kernel()
self.kc = self.km.client()
self.kc.start_channels()
self.kc.wait_for_ready()
def test_with_cell_id(self):
with patch_cell_id():
self.kc.execute("1+1")
def test_pylab(self):
"""Does %pylab work in the in-process kernel?"""
_ = pytest.importorskip("matplotlib", reason="This test requires matplotlib")
kc = self.kc
kc.execute("%pylab")
out, err = assemble_output(kc.get_iopub_msg)
self.assertIn("matplotlib", out)
def test_raw_input(self):
"""Does the in-process kernel handle raw_input correctly?"""
io = StringIO("foobar\n")
sys_stdin = sys.stdin
sys.stdin = io
try:
self.kc.execute("x = input()")
finally:
sys.stdin = sys_stdin
assert self.km.kernel.shell.user_ns.get("x") == "foobar"
@pytest.mark.skipif("__pypy__" in sys.builtin_module_names, reason="fails on pypy")
def test_stdout(self):
"""Does the in-process kernel correctly capture IO?"""
kernel = InProcessKernel()
with capture_output() as io:
kernel.shell.run_cell('print("foo")')
assert io.stdout == "foo\n"
kc = BlockingInProcessKernelClient(kernel=kernel, session=kernel.session)
kernel.frontends.append(kc)
kc.execute('print("bar")')
out, err = assemble_output(kc.get_iopub_msg)
assert out == "bar\n"
@pytest.mark.skip(reason="Currently don't capture during test as pytest does its own capturing")
def test_capfd(self):
"""Does correctly capture fd"""
kernel = InProcessKernel()
with capture_output() as io:
kernel.shell.run_cell('print("foo")')
assert io.stdout == "foo\n"
kc = BlockingInProcessKernelClient(kernel=kernel, session=kernel.session)
kernel.frontends.append(kc)
kc.execute("import os")
kc.execute('os.system("echo capfd")')
out, err = assemble_output(kc.iopub_channel)
assert out == "capfd\n"
def test_getpass_stream(self):
"Tests that kernel getpass accept the stream parameter"
kernel = InProcessKernel()
kernel._allow_stdin = True
kernel._input_request = lambda *args, **kwargs: None
kernel.getpass(stream="non empty")
def test_do_execute(self):
kernel = InProcessKernel()
asyncio.run(kernel.do_execute("a=1", True))
assert kernel.shell.user_ns["a"] == 1