Skip to content

Commit

Permalink
Delete always skipped test, fix trio test, fix framelocal has not .cl…
Browse files Browse the repository at this point in the history
…ear() (#1322)

There are few chances we will ever use them again.

Also a small refactor and unskip of trio test using trio nursery.
  • Loading branch information
Carreau authored Feb 19, 2025
1 parent 486050f commit 28d096d
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 97 deletions.
3 changes: 2 additions & 1 deletion ipykernel/embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ def embed_kernel(module=None, local_ns=None, **kwargs):
if module is None:
module = caller_module
if local_ns is None:
local_ns = caller_locals
local_ns = dict(**caller_locals)

app.kernel.user_module = module
assert isinstance(local_ns, dict)
app.kernel.user_ns = local_ns
app.shell.set_completer_frame() # type:ignore[union-attr]
app.start()
Expand Down
15 changes: 13 additions & 2 deletions ipykernel/ipkernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@
from IPython.core import release
from IPython.utils.tokenutil import line_at_cursor, token_at_cursor
from jupyter_client.session import extract_header
from traitlets import Any, Bool, HasTraits, Instance, List, Type, default, observe, observe_compat
from traitlets import (
Any,
Bool,
Dict,
HasTraits,
Instance,
List,
Type,
default,
observe,
observe_compat,
)

from .comm.comm import BaseComm
from .comm.manager import CommManager
Expand Down Expand Up @@ -92,7 +103,7 @@ def _user_module_changed(self, change):
if self.shell is not None:
self.shell.user_module = change["new"]

user_ns = Instance("collections.abc.Mapping", allow_none=True)
user_ns = Dict(allow_none=True)

@default("user_ns")
def _default_user_ns(self):
Expand Down
23 changes: 15 additions & 8 deletions ipykernel/kernelapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ def init_pdb(self):
pdb.set_trace = debugger.set_trace

@catch_config_error
def initialize(self, argv=None):
def initialize(self, argv=None) -> None:
"""Initialize the application."""
self._init_asyncio_patch()
super().initialize(argv)
Expand Down Expand Up @@ -772,30 +772,37 @@ def initialize(self, argv=None):
sys.stdout.flush()
sys.stderr.flush()

def start(self) -> None:
"""Start the application."""
async def _start(self) -> None:
"""
Async version of start, when the loop is not controlled by IPykernel
For example to be used in test suite with @pytest.mark.trio
"""
if self.subapp is not None:
self.subapp.start()
return
if self.poller is not None:
self.poller.start()
await self.main()

def start(self) -> None:
"""Start the application."""
backend = "trio" if self.trio_loop else "asyncio"
run(self.main, backend=backend)
return
run(self._start, backend=backend)

async def _wait_to_enter_eventloop(self):
async def _wait_to_enter_eventloop(self) -> None:
await self.kernel._eventloop_set.wait()
await self.kernel.enter_eventloop()

async def main(self):
async def main(self) -> None:
async with create_task_group() as tg:
tg.start_soon(self._wait_to_enter_eventloop)
tg.start_soon(self.kernel.start)

if self.kernel.eventloop:
self.kernel._eventloop_set.set()

def stop(self):
def stop(self) -> None:
"""Stop the kernel, thread-safe."""
self.kernel.stop()

Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,13 @@ docs = [
"trio"
]
test = [
"pytest>=7.0,<9",
"pytest-cov",
"flaky",
"ipyparallel",
"pre-commit",
"pytest-cov",
"pytest-timeout",
"pytest>=7.0,<9",
"trio",
"pytest-asyncio>=0.23.5",
]
cov = [
"coverage[toml]",
Expand Down
12 changes: 6 additions & 6 deletions tests/test_embed_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ def connection_file_ready(connection_file):

@flaky(max_runs=3)
def test_embed_kernel_basic():
"""IPython.embed_kernel() is basically functional"""
"""ipykernel.embed.embed_kernel() is basically functional"""
cmd = "\n".join(
[
"from IPython import embed_kernel",
"from ipykernel.embed import embed_kernel",
"def go():",
" a=5",
' b="hi there"',
Expand Down Expand Up @@ -129,10 +129,10 @@ def test_embed_kernel_basic():

@flaky(max_runs=3)
def test_embed_kernel_namespace():
"""IPython.embed_kernel() inherits calling namespace"""
"""ipykernel.embed.embed_kernel() inherits calling namespace"""
cmd = "\n".join(
[
"from IPython import embed_kernel",
"from ipykernel.embed import embed_kernel",
"def go():",
" a=5",
' b="hi there"',
Expand Down Expand Up @@ -168,10 +168,10 @@ def test_embed_kernel_namespace():

@flaky(max_runs=3)
def test_embed_kernel_reentrant():
"""IPython.embed_kernel() can be called multiple times"""
"""ipykernel.embed.embed_kernel() can be called multiple times"""
cmd = "\n".join(
[
"from IPython import embed_kernel",
"from ipykernel.embed import embed_kernel",
"count = 0",
"def go():",
" global count",
Expand Down
38 changes: 0 additions & 38 deletions tests/test_ipkernel_direct.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Test IPythonKernel directly"""

import asyncio
import os

import pytest
Expand Down Expand Up @@ -152,49 +151,12 @@ async def test_direct_clear(ipkernel):
ipkernel.do_clear()


@pytest.mark.skip("ipykernel._cancel_on_sigint doesn't exist anymore")
async def test_cancel_on_sigint(ipkernel: IPythonKernel) -> None:
future: asyncio.Future = asyncio.Future()
# with ipkernel._cancel_on_sigint(future):
# pass
future.set_result(None)


async def test_dispatch_debugpy(ipkernel: IPythonKernel) -> None:
msg = ipkernel.session.msg("debug_request", {})
msg_list = ipkernel.session.serialize(msg)
await ipkernel.receive_debugpy_message(msg_list)


@pytest.mark.skip("Queues don't exist anymore")
async def test_start(ipkernel: IPythonKernel) -> None:
shell_future: asyncio.Future = asyncio.Future()

async def fake_dispatch_queue():
shell_future.set_result(None)

ipkernel.dispatch_queue = fake_dispatch_queue # type:ignore
ipkernel.start()
ipkernel.debugpy_stream = None
ipkernel.start()
await ipkernel.process_one(False)
await shell_future


@pytest.mark.skip("Queues don't exist anymore")
async def test_start_no_debugpy(ipkernel: IPythonKernel) -> None:
shell_future: asyncio.Future = asyncio.Future()

async def fake_dispatch_queue():
shell_future.set_result(None)

ipkernel.dispatch_queue = fake_dispatch_queue # type:ignore
ipkernel.debugpy_stream = None
ipkernel.start()

await shell_future


def test_create_comm():
assert isinstance(_create_comm(), BaseComm)

Expand Down
29 changes: 4 additions & 25 deletions tests/test_kernel_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

import asyncio
import os
import warnings

import pytest

Expand Down Expand Up @@ -108,19 +106,6 @@ async def test_direct_debug_request(kernel):
assert reply["header"]["msg_type"] == "debug_reply"


@pytest.mark.skip("Shell streams don't exist anymore")
async def test_deprecated_features(kernel):
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
header = kernel._parent_header
assert isinstance(header, dict)
shell_streams = kernel.shell_streams
assert len(shell_streams) == 1
assert shell_streams[0] == kernel.shell_stream
warnings.simplefilter("ignore", RuntimeWarning)
kernel.shell_streams = [kernel.shell_stream, kernel.shell_stream]


async def test_process_control(kernel):
from jupyter_client.session import DELIM

Expand All @@ -142,12 +127,6 @@ async def test_dispatch_shell(kernel):
await kernel.process_shell_message(msg)


@pytest.mark.skip("kernelbase.do_one_iteration doesn't exist anymore")
async def test_do_one_iteration(kernel):
kernel.msg_queue = asyncio.Queue()
await kernel.do_one_iteration()


async def test_publish_debug_event(kernel):
kernel._publish_debug_event({})

Expand All @@ -160,7 +139,7 @@ async def test_send_interrupt_children(kernel):
kernel._send_interrupt_children()


# TODO: this causes deadlock
# async def test_direct_usage_request(kernel):
# reply = await kernel.test_control_message("usage_request", {})
# assert reply['header']['msg_type'] == 'usage_reply'
@pytest.mark.skip(reason="this causes deadlock")
async def test_direct_usage_request(kernel):
reply = await kernel.test_control_message("usage_request", {})
assert reply["header"]["msg_type"] == "usage_reply"
24 changes: 10 additions & 14 deletions tests/test_kernelapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
from .conftest import MockKernel
from .utils import TemporaryWorkingDirectory

try:
import trio
except ImportError:
trio = None


@pytest.mark.skipif(os.name == "nt", reason="requires ipc")
def test_init_ipc_socket():
Expand Down Expand Up @@ -118,21 +113,22 @@ def test_merge_connection_file():
os.remove(cf)


# FIXME: @pytest.mark.skipif(trio is None, reason="requires trio")
@pytest.mark.skip()
def test_trio_loop():
@pytest.mark.skip("Something wrong with CI")
@pytest.mark.parametrize("anyio_backend", ["trio"])
async def test_trio_loop(anyio_backend):
import trio

app = IPKernelApp(trio_loop=True)

def trigger_stop():
time.sleep(1)
async def trigger_stop():
await trio.sleep(1)
app.stop()

thread = threading.Thread(target=trigger_stop)
thread.start()

app.kernel = MockKernel()
app.init_sockets()
app.start()
async with trio.open_nursery() as nursery:
nursery.start_soon(app._start)
nursery.start_soon(trigger_stop)
app.cleanup_connection_file()
app.kernel.destroy()
app.close()

0 comments on commit 28d096d

Please sign in to comment.