Skip to content

Commit 09c3c35

Browse files
Merge connection info into existing connection file if it already exists (#1133)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 58e9d15 commit 09c3c35

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed

ipykernel/kernelapp.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
)
2525
from IPython.core.profiledir import ProfileDir
2626
from IPython.core.shellapp import InteractiveShellApp, shell_aliases, shell_flags
27-
from jupyter_client import write_connection_file
2827
from jupyter_client.connect import ConnectionFileMixin
2928
from jupyter_client.session import Session, session_aliases, session_flags
3029
from jupyter_core.paths import jupyter_runtime_dir
@@ -44,10 +43,11 @@
4443
from traitlets.utils.importstring import import_item
4544
from zmq.eventloop.zmqstream import ZMQStream
4645

47-
from .control import ControlThread
48-
from .heartbeat import Heartbeat
46+
from .connect import get_connection_info, write_connection_file
4947

5048
# local imports
49+
from .control import ControlThread
50+
from .heartbeat import Heartbeat
5151
from .iostream import IOPubThread
5252
from .ipkernel import IPythonKernel
5353
from .parentpoller import ParentPollerUnix, ParentPollerWindows
@@ -260,12 +260,7 @@ def _bind_socket(self, s, port):
260260
def write_connection_file(self):
261261
"""write connection info to JSON file"""
262262
cf = self.abs_connection_file
263-
if os.path.exists(cf):
264-
self.log.debug("Connection file %s already exists", cf)
265-
return
266-
self.log.debug("Writing connection file: %s", cf)
267-
write_connection_file(
268-
cf,
263+
connection_info = dict(
269264
ip=self.ip,
270265
key=self.session.key,
271266
transport=self.transport,
@@ -275,6 +270,19 @@ def write_connection_file(self):
275270
iopub_port=self.iopub_port,
276271
control_port=self.control_port,
277272
)
273+
if os.path.exists(cf):
274+
# If the file exists, merge our info into it. For example, if the
275+
# original file had port number 0, we update with the actual port
276+
# used.
277+
existing_connection_info = get_connection_info(cf, unpack=True)
278+
connection_info = dict(existing_connection_info, **connection_info)
279+
if connection_info == existing_connection_info:
280+
self.log.debug("Connection file %s with current information already exists", cf)
281+
return
282+
283+
self.log.debug("Writing connection file: %s", cf)
284+
285+
write_connection_file(cf, **connection_info)
278286

279287
def cleanup_connection_file(self):
280288
"""Clean up our connection file."""

ipykernel/tests/test_ipkernel_direct.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class user_mod:
2020
__dict__ = {}
2121

2222

23-
async def test_properities(ipkernel: IPythonKernel) -> None:
23+
async def test_properties(ipkernel: IPythonKernel) -> None:
2424
ipkernel.user_module = user_mod()
2525
ipkernel.user_ns = {}
2626

ipykernel/tests/test_kernelapp.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
import json
12
import os
23
import threading
34
import time
45
from unittest.mock import patch
56

67
import pytest
8+
from jupyter_core.paths import secure_write
9+
from traitlets.config.loader import Config
710

811
from ipykernel.kernelapp import IPKernelApp
912

1013
from .conftest import MockKernel
14+
from .utils import TemporaryWorkingDirectory
1115

1216
try:
1317
import trio
@@ -47,6 +51,73 @@ def trigger_stop():
4751
app.close()
4852

4953

54+
@pytest.mark.skipif(os.name == "nt", reason="permission errors on windows")
55+
def test_merge_connection_file():
56+
cfg = Config()
57+
with TemporaryWorkingDirectory() as d:
58+
cfg.ProfileDir.location = d
59+
cf = os.path.join(d, "kernel.json")
60+
initial_connection_info = {
61+
"ip": "*",
62+
"transport": "tcp",
63+
"shell_port": 0,
64+
"hb_port": 0,
65+
"iopub_port": 0,
66+
"stdin_port": 0,
67+
"control_port": 53555,
68+
"key": "abc123",
69+
"signature_scheme": "hmac-sha256",
70+
"kernel_name": "My Kernel",
71+
}
72+
# We cannot use connect.write_connection_file since
73+
# it replaces port number 0 with a random port
74+
# and we want IPKernelApp to do that replacement.
75+
with secure_write(cf) as f:
76+
json.dump(initial_connection_info, f)
77+
assert os.path.exists(cf)
78+
79+
app = IPKernelApp(config=cfg, connection_file=cf)
80+
81+
# Calling app.initialize() does not work in the test, so we call the relevant functions that initialize() calls
82+
# We must pass in an empty argv, otherwise the default is to try to parse the test runner's argv
83+
super(IPKernelApp, app).initialize(argv=[""])
84+
app.init_connection_file()
85+
app.init_sockets()
86+
app.init_heartbeat()
87+
app.write_connection_file()
88+
89+
# Initialize should have merged the actual connection info
90+
# with the connection info in the file
91+
assert cf == app.abs_connection_file
92+
assert os.path.exists(cf)
93+
94+
with open(cf) as f:
95+
new_connection_info = json.load(f)
96+
97+
# ports originally set as 0 have been replaced
98+
for port in ("shell", "hb", "iopub", "stdin"):
99+
key = f"{port}_port"
100+
# We initially had the port as 0
101+
assert initial_connection_info[key] == 0
102+
# the port is not 0 now
103+
assert new_connection_info[key] > 0
104+
# the port matches the port the kernel actually used
105+
assert new_connection_info[key] == getattr(app, key), f"{key}"
106+
del new_connection_info[key]
107+
del initial_connection_info[key]
108+
109+
# The wildcard ip address was also replaced
110+
assert new_connection_info["ip"] != "*"
111+
del new_connection_info["ip"]
112+
del initial_connection_info["ip"]
113+
114+
# everything else in the connection file is the same
115+
assert initial_connection_info == new_connection_info
116+
117+
app.close()
118+
os.remove(cf)
119+
120+
50121
@pytest.mark.skipif(trio is None, reason="requires trio")
51122
def test_trio_loop():
52123
app = IPKernelApp(trio_loop=True)

0 commit comments

Comments
 (0)