2
2
# Copyright (c) Jupyter Development Team.
3
3
# Distributed under the terms of the Modified BSD License.
4
4
import asyncio
5
+ import json
5
6
import os
6
7
import socket
7
8
import typing as t
8
9
import uuid
9
10
from functools import wraps
11
+ from pathlib import Path
10
12
11
13
import zmq
12
14
from traitlets import Any , Bool , Dict , DottedObjectName , Instance , Unicode , default , observe
13
15
from traitlets .config .configurable import LoggingConfigurable
14
16
from traitlets .utils .importstring import import_item
15
17
18
+ from .connect import KernelConnectionInfo
16
19
from .kernelspec import NATIVE_KERNEL_NAME , KernelSpecManager
17
20
from .manager import KernelManager
18
- from .utils import ensure_async , run_sync
21
+ from .utils import ensure_async , run_sync , utcnow
19
22
20
23
21
24
class DuplicateKernelError (Exception ):
@@ -105,9 +108,14 @@ def _context_default(self) -> zmq.Context:
105
108
return zmq .Context ()
106
109
107
110
connection_dir = Unicode ("" )
111
+ external_connection_dir = Unicode (None , allow_none = True )
108
112
109
113
_kernels = Dict ()
110
114
115
+ def __init__ (self , * args , ** kwargs ):
116
+ super ().__init__ (* args , ** kwargs )
117
+ self .kernel_id_to_connection_file = {}
118
+
111
119
def __del__ (self ):
112
120
"""Handle garbage collection. Destroy context if applicable."""
113
121
if self ._created_context and self .context and not self .context .closed :
@@ -123,6 +131,51 @@ def __del__(self):
123
131
124
132
def list_kernel_ids (self ) -> t .List [str ]:
125
133
"""Return a list of the kernel ids of the active kernels."""
134
+ if self .external_connection_dir is not None :
135
+ external_connection_dir = Path (self .external_connection_dir )
136
+ if external_connection_dir .is_dir ():
137
+ connection_files = [p for p in external_connection_dir .iterdir () if p .is_file ()]
138
+
139
+ # remove kernels (whose connection file has disappeared) from our list
140
+ k = list (self .kernel_id_to_connection_file .keys ())
141
+ v = list (self .kernel_id_to_connection_file .values ())
142
+ for connection_file in list (self .kernel_id_to_connection_file .values ()):
143
+ if connection_file not in connection_files :
144
+ kernel_id = k [v .index (connection_file )]
145
+ del self .kernel_id_to_connection_file [kernel_id ]
146
+ del self ._kernels [kernel_id ]
147
+
148
+ # add kernels (whose connection file appeared) to our list
149
+ for connection_file in connection_files :
150
+ if connection_file in self .kernel_id_to_connection_file .values ():
151
+ continue
152
+ try :
153
+ connection_info : KernelConnectionInfo = json .loads (
154
+ connection_file .read_text ()
155
+ )
156
+ except Exception : # noqa: S112
157
+ continue
158
+ self .log .debug ("Loading connection file %s" , connection_file )
159
+ if not ("kernel_name" in connection_info and "key" in connection_info ):
160
+ continue
161
+ # it looks like a connection file
162
+ kernel_id = self .new_kernel_id ()
163
+ self .kernel_id_to_connection_file [kernel_id ] = connection_file
164
+ km = self .kernel_manager_factory (
165
+ parent = self ,
166
+ log = self .log ,
167
+ owns_kernel = False ,
168
+ )
169
+ km .load_connection_info (connection_info )
170
+ km .last_activity = utcnow ()
171
+ km .execution_state = "idle"
172
+ km .connections = 1
173
+ km .kernel_id = kernel_id
174
+ km .kernel_name = connection_info ["kernel_name" ]
175
+ km .ready .set_result (None )
176
+
177
+ self ._kernels [kernel_id ] = km
178
+
126
179
# Create a copy so we can iterate over kernels in operations
127
180
# that delete keys.
128
181
return list (self ._kernels .keys ())
0 commit comments