Skip to content

Commit ece398f

Browse files
committed
Continue to support Kernel._parent_ident for backward compatibility (#1427)
1 parent acf027b commit ece398f

File tree

4 files changed

+82
-1
lines changed

4 files changed

+82
-1
lines changed

ipykernel/kernelbase.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import typing as t
1717
import uuid
1818
import warnings
19+
from collections.abc import Mapping
1920
from contextvars import ContextVar
2021
from datetime import datetime
2122
from functools import partial
@@ -61,6 +62,7 @@
6162

6263
from ._version import kernel_protocol_version
6364
from .iostream import OutStream
65+
from .utils import LazyDict
6466

6567
_AWAITABLE_MESSAGE: str = (
6668
"For consistency across implementations, it is recommended that `{func_name}`"
@@ -199,6 +201,9 @@ def _default_ident(self):
199201
_control_parent_ident: bytes = b""
200202
_shell_parent: ContextVar[dict[str, Any]]
201203
_shell_parent_ident: ContextVar[bytes]
204+
# Kept for backward-compatibility, accesses _control_parent_ident and _shell_parent_ident,
205+
# see https://github.com/jupyterlab/jupyterlab/issues/17785
206+
_parent_ident: Mapping[str, bytes]
202207

203208
@property
204209
def _parent_header(self):
@@ -313,6 +318,15 @@ def __init__(self, **kwargs):
313318
self._shell_parent_ident = ContextVar("shell_parent_ident")
314319
self._shell_parent_ident.set(b"")
315320

321+
# For backward compatibility so that _parent_ident["shell"] and _parent_ident["control"]
322+
# work as they used to for ipykernel >= 7
323+
self._parent_ident = LazyDict(
324+
{
325+
"control": lambda: self._control_parent_ident,
326+
"shell": lambda: self._shell_parent_ident.get(),
327+
}
328+
)
329+
316330
async def dispatch_control(self, msg):
317331
"""Dispatch a control request, ensuring only one message is processed at a time."""
318332
# Ensure only one control message is processed at a time

ipykernel/utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Utilities"""
2+
3+
import typing as t
4+
from collections.abc import Mapping
5+
6+
7+
class LazyDict(Mapping[str, t.Any]):
8+
"""Lazy evaluated read-only dictionary.
9+
10+
Initialised with a dictionary of key-value pairs where the values are either
11+
constants or callables. Callables are evaluated each time the respective item is
12+
read.
13+
"""
14+
15+
def __init__(self, dict):
16+
self._dict = dict
17+
18+
def __getitem__(self, key):
19+
item = self._dict.get(key)
20+
return item() if callable(item) else item
21+
22+
def __len__(self):
23+
return len(self._dict)
24+
25+
def __iter__(self):
26+
return iter(self._dict)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ path = "ipykernel/_version.py"
8888
features = ["docs"]
8989
[tool.hatch.envs.docs.scripts]
9090
build = "make -C docs html SPHINXOPTS='-W'"
91-
api = "sphinx-apidoc -o docs/api -f -E ipykernel tests ipykernel/datapub.py ipykernel/pickleutil.py ipykernel/serialize.py ipykernel/gui ipykernel/pylab"
91+
api = "sphinx-apidoc -o docs/api -f -E ipykernel tests ipykernel/datapub.py ipykernel/pickleutil.py ipykernel/serialize.py ipykernel/gui ipykernel/pylab ipykernel/utils.py"
9292

9393
[tool.hatch.envs.test]
9494
features = ["test"]

tests/test_kernel.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,3 +717,44 @@ def test_shutdown_subprocesses():
717717
child_newpg.terminate()
718718
except psutil.NoSuchProcess:
719719
pass
720+
721+
722+
def test_parent_header_and_ident():
723+
# Kernel._parent_ident is private but kept for backward compatibility,
724+
# see https://github.com/jupyterlab/jupyterlab/issues/17785
725+
with kernel() as kc:
726+
# get_parent('shell')
727+
msg_id, _ = execute(
728+
kc=kc,
729+
code="k=get_ipython().kernel; p=k.get_parent('shell'); print(p['header']['msg_id'], p['header']['session'])",
730+
)
731+
stdout, _ = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_id)
732+
check_msg_id, session = stdout.split()
733+
assert check_msg_id == msg_id
734+
assert check_msg_id.startswith(msg_id)
735+
736+
# _parent_ident['shell']
737+
msg_id, _ = execute(kc=kc, code="print(k._parent_ident['shell'])")
738+
stdout, _ = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_id)
739+
assert stdout == f"[b'{session}']\n"
740+
741+
# Send a control message
742+
msg = kc.session.msg("kernel_info_request")
743+
kc.control_channel.send(msg)
744+
control_msg_id = msg["header"]["msg_id"]
745+
assemble_output(kc.get_iopub_msg, parent_msg_id=control_msg_id)
746+
747+
# get_parent('control')
748+
msg_id, _ = execute(
749+
kc=kc,
750+
code="p=k.get_parent('control'); print(p['header']['msg_id'], p['header']['session'])",
751+
)
752+
stdout, _ = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_id)
753+
check_msg_id, session = stdout.split()
754+
assert check_msg_id == control_msg_id
755+
assert check_msg_id.startswith(control_msg_id)
756+
757+
# _parent_ident['control']
758+
msg_id, _ = execute(kc=kc, code="print(k._parent_ident['control'])")
759+
stdout, _ = assemble_output(kc.get_iopub_msg, parent_msg_id=msg_id)
760+
assert stdout == f"[b'{session}']\n"

0 commit comments

Comments
 (0)