0
# Kernel Provisioning
1
2
Pluggable kernel provisioning system for starting kernels in different environments. Supports local subprocess provisioning and extensible remote provisioning through a factory pattern and abstract base class system.
3
4
## Capabilities
5
6
### Kernel Provisioner Base
7
8
The `KernelProvisionerBase` abstract class defines the interface for kernel provisioners, enabling pluggable kernel launch strategies for different environments.
9
10
```python { .api }
11
class KernelProvisionerBase:
12
"""Abstract base class for kernel provisioners."""
13
14
@property
15
def has_process(self):
16
"""
17
Whether this provisioner manages a process.
18
19
Returns:
20
bool: True if provisioner manages a local process
21
"""
22
23
async def poll(self):
24
"""
25
Check if the kernel process is running.
26
27
Returns:
28
int | None: Process exit code or None if still running
29
"""
30
31
async def wait(self):
32
"""
33
Wait for the kernel process to terminate.
34
35
Returns:
36
int | None: Process exit code
37
"""
38
39
async def send_signal(self, signum):
40
"""
41
Send a signal to the kernel process.
42
43
Parameters:
44
- signum (int): Signal number to send
45
46
Returns:
47
None
48
"""
49
50
async def kill(self, restart=False):
51
"""
52
Kill the kernel process.
53
54
Parameters:
55
- restart (bool): Whether this is part of a restart
56
57
Returns:
58
None
59
"""
60
61
async def terminate(self, restart=False):
62
"""
63
Terminate the kernel process gracefully.
64
65
Parameters:
66
- restart (bool): Whether this is part of a restart
67
68
Returns:
69
None
70
"""
71
72
async def launch_kernel(self, cmd, **kwargs):
73
"""
74
Launch the kernel process.
75
76
Parameters:
77
- cmd (list): Command line arguments to execute
78
- **kwargs: Additional launch parameters
79
80
Returns:
81
dict: Connection information for the launched kernel
82
"""
83
84
# Optional override methods
85
86
async def cleanup(self, restart=False):
87
"""
88
Cleanup resources after kernel shutdown.
89
90
Parameters:
91
- restart (bool): Whether this is part of a restart
92
93
Returns:
94
None
95
"""
96
97
async def shutdown_requested(self, restart=False):
98
"""
99
Handle shutdown request from kernel manager.
100
101
Parameters:
102
- restart (bool): Whether this is part of a restart
103
104
Returns:
105
None
106
"""
107
108
async def pre_launch(self, **kwargs):
109
"""
110
Perform setup before kernel launch.
111
112
Parameters:
113
- **kwargs: Launch parameters
114
115
Returns:
116
dict: Updated launch parameters
117
"""
118
119
async def post_launch(self, **kwargs):
120
"""
121
Perform setup after kernel launch.
122
123
Parameters:
124
- **kwargs: Launch results
125
126
Returns:
127
None
128
"""
129
130
async def get_provisioner_info(self):
131
"""
132
Get current provisioner state information.
133
134
Returns:
135
dict: Provisioner state data
136
"""
137
138
async def load_provisioner_info(self, provisioner_info):
139
"""
140
Load provisioner state information.
141
142
Parameters:
143
- provisioner_info (dict): Previously saved state data
144
145
Returns:
146
None
147
"""
148
149
# Utility methods
150
151
def get_shutdown_wait_time(self, recommended=5.0):
152
"""
153
Get timeout for shutdown operations.
154
155
Parameters:
156
- recommended (float): Recommended timeout in seconds
157
158
Returns:
159
float: Timeout value to use
160
"""
161
162
def get_stable_start_time(self, recommended=10.0):
163
"""
164
Get timeout for kernel startup stability.
165
166
Parameters:
167
- recommended (float): Recommended timeout in seconds
168
169
Returns:
170
float: Timeout value to use
171
"""
172
```
173
174
### Local Provisioner
175
176
The `LocalProvisioner` provides a concrete implementation for running kernels as local subprocesses.
177
178
```python { .api }
179
class LocalProvisioner(KernelProvisionerBase):
180
"""Provisions kernels locally using subprocess."""
181
182
@property
183
def has_process(self):
184
"""
185
Always returns True for local provisioner.
186
187
Returns:
188
bool: True
189
"""
190
191
async def poll(self):
192
"""
193
Poll the local subprocess status.
194
195
Returns:
196
int | None: Process exit code or None if running
197
"""
198
199
async def wait(self):
200
"""
201
Wait for local subprocess to terminate.
202
203
Returns:
204
int | None: Process exit code
205
"""
206
207
async def send_signal(self, signum):
208
"""
209
Send signal to local subprocess.
210
211
Parameters:
212
- signum (int): Signal number
213
214
Returns:
215
None
216
"""
217
218
async def kill(self, restart=False):
219
"""
220
Kill local subprocess.
221
222
Parameters:
223
- restart (bool): Whether part of restart
224
225
Returns:
226
None
227
"""
228
229
async def terminate(self, restart=False):
230
"""
231
Terminate local subprocess gracefully.
232
233
Parameters:
234
- restart (bool): Whether part of restart
235
236
Returns:
237
None
238
"""
239
240
async def launch_kernel(self, cmd, **kwargs):
241
"""
242
Launch kernel as local subprocess.
243
244
Parameters:
245
- cmd (list): Command to execute
246
- **kwargs: Additional subprocess parameters
247
248
Returns:
249
dict: Connection information
250
"""
251
252
async def pre_launch(self, **kwargs):
253
"""
254
Setup local environment before launch.
255
256
Parameters:
257
- **kwargs: Launch parameters
258
259
Returns:
260
dict: Updated launch parameters
261
"""
262
263
async def cleanup(self, restart=False):
264
"""
265
Cleanup local resources.
266
267
Parameters:
268
- restart (bool): Whether part of restart
269
270
Returns:
271
None
272
"""
273
```
274
275
### Provisioner Factory
276
277
The `KernelProvisionerFactory` manages the creation of provisioner instances and discovery of available provisioners.
278
279
```python { .api }
280
class KernelProvisionerFactory:
281
"""Factory for creating kernel provisioner instances."""
282
283
def is_provisioner_available(self, kernel_spec):
284
"""
285
Check if a provisioner is available for the kernel spec.
286
287
Parameters:
288
- kernel_spec (KernelSpec): Kernel specification
289
290
Returns:
291
bool: True if provisioner is available
292
"""
293
294
def create_provisioner_instance(self, kernel_id, kernel_spec, parent):
295
"""
296
Create a provisioner instance for the kernel.
297
298
Parameters:
299
- kernel_id (str): Unique kernel identifier
300
- kernel_spec (KernelSpec): Kernel specification
301
- parent: Parent configurable object
302
303
Returns:
304
KernelProvisionerBase: Provisioner instance
305
"""
306
307
def get_provisioner_entries(self):
308
"""
309
Get all available provisioner entry points.
310
311
Returns:
312
dict: Mapping of provisioner names to entry points
313
"""
314
```
315
316
### Kernel Launcher
317
318
Utility function for launching kernel processes directly.
319
320
```python { .api }
321
def launch_kernel(cmd, stdin=None, stdout=None, stderr=None,
322
env=None, independent=False, cwd=None, **kw):
323
"""
324
Launch a kernel process.
325
326
Parameters:
327
- cmd (list): Command line arguments to execute
328
- stdin (int): Standard input file descriptor
329
- stdout (int): Standard output file descriptor
330
- stderr (int): Standard error file descriptor
331
- env (dict): Environment variables
332
- independent (bool): Whether kernel survives parent death
333
- cwd (str): Working directory for kernel process
334
- **kw: Additional Popen arguments
335
336
Returns:
337
subprocess.Popen: Process object for the kernel
338
"""
339
```
340
341
## Usage Examples
342
343
### Using Default Local Provisioner
344
345
```python
346
from jupyter_client import KernelManager
347
348
# KernelManager uses LocalProvisioner by default
349
km = KernelManager(kernel_name='python3')
350
km.start_kernel()
351
352
# The provisioner is accessible
353
provisioner = km.provisioner
354
print(f"Provisioner type: {type(provisioner)}")
355
print(f"Has process: {provisioner.has_process}")
356
357
# Check if kernel is running
358
if await provisioner.poll() is None:
359
print("Kernel is running")
360
361
km.shutdown_kernel()
362
```
363
364
### Creating Custom Provisioner
365
366
```python
367
from jupyter_client.provisioning import KernelProvisionerBase
368
import asyncio
369
import subprocess
370
371
class CustomProvisioner(KernelProvisionerBase):
372
"""Custom provisioner example."""
373
374
def __init__(self, **kwargs):
375
super().__init__(**kwargs)
376
self.process = None
377
378
@property
379
def has_process(self):
380
return True
381
382
async def launch_kernel(self, cmd, **kwargs):
383
"""Launch kernel with custom logic."""
384
# Custom environment setup
385
custom_env = kwargs.get('env', {}).copy()
386
custom_env['CUSTOM_VAR'] = 'custom_value'
387
388
# Launch process
389
self.process = subprocess.Popen(
390
cmd,
391
env=custom_env,
392
cwd=kwargs.get('cwd'),
393
stdout=subprocess.PIPE,
394
stderr=subprocess.PIPE
395
)
396
397
# Return connection info (would be real in practice)
398
return {
399
'shell_port': 50001,
400
'iopub_port': 50002,
401
'stdin_port': 50003,
402
'hb_port': 50004,
403
'control_port': 50005,
404
'ip': '127.0.0.1',
405
'transport': 'tcp'
406
}
407
408
async def poll(self):
409
if self.process:
410
return self.process.poll()
411
return None
412
413
async def kill(self, restart=False):
414
if self.process:
415
self.process.kill()
416
417
async def terminate(self, restart=False):
418
if self.process:
419
self.process.terminate()
420
421
async def send_signal(self, signum):
422
if self.process:
423
self.process.send_signal(signum)
424
425
async def wait(self):
426
if self.process:
427
return self.process.wait()
428
return None
429
430
# Use custom provisioner
431
from jupyter_client import KernelManager
432
km = KernelManager()
433
km.provisioner = CustomProvisioner(parent=km)
434
```
435
436
### Working with Provisioner Factory
437
438
```python
439
from jupyter_client.provisioning import KernelProvisionerFactory
440
from jupyter_client import get_kernel_spec
441
442
# Get factory instance
443
factory = KernelProvisionerFactory.instance()
444
445
# Get available provisioners
446
provisioners = factory.get_provisioner_entries()
447
print(f"Available provisioners: {list(provisioners.keys())}")
448
449
# Check if provisioner is available for a kernel spec
450
kernel_spec = get_kernel_spec('python3')
451
if factory.is_provisioner_available(kernel_spec):
452
print("Provisioner available for python3")
453
454
# Create provisioner instance
455
provisioner = factory.create_provisioner_instance(
456
kernel_id='test-kernel',
457
kernel_spec=kernel_spec,
458
parent=None
459
)
460
print(f"Created provisioner: {type(provisioner)}")
461
```
462
463
### Async Provisioner Operations
464
465
```python
466
import asyncio
467
from jupyter_client import AsyncKernelManager
468
469
async def manage_kernel_with_provisioner():
470
km = AsyncKernelManager()
471
await km.start_kernel()
472
473
provisioner = km.provisioner
474
475
# Check provisioner state
476
if provisioner.has_process:
477
status = await provisioner.poll()
478
if status is None:
479
print("Kernel process is running")
480
481
# Get provisioner info
482
info = await provisioner.get_provisioner_info()
483
print(f"Provisioner info: {info}")
484
485
# Graceful shutdown
486
await provisioner.terminate()
487
await provisioner.wait()
488
489
# Cleanup
490
await provisioner.cleanup()
491
492
await km.shutdown_kernel()
493
494
# Run async example
495
asyncio.run(manage_kernel_with_provisioner())
496
```
497
498
### Direct Kernel Launch
499
500
```python
501
from jupyter_client.launcher import launch_kernel
502
import tempfile
503
import os
504
505
# Prepare kernel command
506
cmd = ['python', '-m', 'ipykernel_launcher', '-f', 'connection.json']
507
508
# Create temporary directory for kernel
509
with tempfile.TemporaryDirectory() as temp_dir:
510
conn_file = os.path.join(temp_dir, 'connection.json')
511
512
# Write connection file
513
from jupyter_client import write_connection_file
514
write_connection_file(conn_file)
515
516
# Launch kernel directly
517
process = launch_kernel(
518
cmd,
519
cwd=temp_dir,
520
independent=True
521
)
522
523
print(f"Launched kernel with PID: {process.pid}")
524
525
# Use kernel...
526
527
# Clean up
528
process.terminate()
529
process.wait()
530
```
531
532
### Custom Environment Provisioner
533
534
```python
535
from jupyter_client.provisioning import LocalProvisioner
536
537
class ContainerProvisioner(LocalProvisioner):
538
"""Example provisioner that runs kernels in containers."""
539
540
async def pre_launch(self, **kwargs):
541
"""Modify command to run in container."""
542
cmd = kwargs.get('cmd', [])
543
544
# Wrap command with container runtime
545
container_cmd = [
546
'docker', 'run', '--rm', '-i',
547
'--name', f'kernel-{self.kernel_id}',
548
'jupyter/scipy-notebook'
549
] + cmd
550
551
kwargs['cmd'] = container_cmd
552
return kwargs
553
554
async def kill(self, restart=False):
555
"""Kill container instead of process."""
556
import subprocess
557
try:
558
subprocess.run([
559
'docker', 'kill', f'kernel-{self.kernel_id}'
560
], check=True)
561
except subprocess.CalledProcessError:
562
pass # Container might already be stopped
563
564
# Use container provisioner
565
from jupyter_client import KernelManager
566
km = KernelManager()
567
km.provisioner_class = ContainerProvisioner
568
km.start_kernel()
569
```