# 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