2
2
// Licensed under the MIT License.
3
3
'use strict' ;
4
4
5
- import { Kernel } from '@jupyterlab/services ' ;
5
+ import type { nbformat } from '@jupyterlab/coreutils ' ;
6
6
import { inject , injectable , named } from 'inversify' ;
7
7
import * as path from 'path' ;
8
8
import { CancellationToken , CancellationTokenSource } from 'vscode' ;
@@ -14,6 +14,7 @@ import { IExtensionContext, IInstaller, InstallerResponse, IPathUtils, Product,
14
14
import { IInterpreterLocatorService , IInterpreterService , KNOWN_PATH_SERVICE } from '../../interpreter/contracts' ;
15
15
import { captureTelemetry } from '../../telemetry' ;
16
16
import { Telemetry } from '../constants' ;
17
+ import { createDefaultKernelSpec , defaultKernelSpecName } from '../jupyter/kernels/helpers' ;
17
18
import { JupyterKernelSpec } from '../jupyter/kernels/jupyterKernelSpec' ;
18
19
import { IJupyterKernelSpec } from '../types' ;
19
20
import { getKernelInterpreter } from './helpers' ;
@@ -27,14 +28,6 @@ const macJupyterPath = path.join('Library', 'Jupyter', 'kernels');
27
28
const baseKernelPath = path . join ( 'share' , 'jupyter' , 'kernels' ) ;
28
29
29
30
const cacheFile = 'kernelSpecPathCache.json' ;
30
- const defaultSpecName = 'python_defaultSpec_' ;
31
-
32
- // https://jupyter-client.readthedocs.io/en/stable/kernels.html
33
- const connectionFilePlaceholder = '{connection_file}' ;
34
-
35
- export function findIndexOfConnectionFile ( kernelSpec : Readonly < IJupyterKernelSpec > ) : number {
36
- return kernelSpec . argv . indexOf ( connectionFilePlaceholder ) ;
37
- }
38
31
39
32
// This class searches for a kernel that matches the given kernel name.
40
33
// First it searches on a global persistent state, then on the installed python interpreters,
@@ -68,40 +61,49 @@ export class KernelFinder implements IKernelFinder {
68
61
@captureTelemetry ( Telemetry . KernelFinderPerf )
69
62
public async findKernelSpec (
70
63
resource : Resource ,
71
- kernelName ?: string ,
64
+ kernelSpecMetadata ?: nbformat . IKernelspecMetadata ,
72
65
cancelToken ?: CancellationToken
73
66
) : Promise < IJupyterKernelSpec > {
74
67
this . cache = await this . readCache ( ) ;
75
68
let foundKernel : IJupyterKernelSpec | undefined ;
76
69
77
- if ( kernelName && ! kernelName . includes ( defaultSpecName ) ) {
78
- let kernelSpec = await this . searchCache ( kernelName ) ;
70
+ const kernelName = kernelSpecMetadata ?. name ;
79
71
80
- if ( kernelSpec ) {
81
- return kernelSpec ;
82
- }
72
+ if ( kernelSpecMetadata && kernelName ) {
73
+ // For a non default kernelspec search for it
74
+ if ( ! kernelName . includes ( defaultKernelSpecName ) ) {
75
+ let kernelSpec = await this . searchCache ( kernelName ) ;
83
76
84
- // Check in active interpreter first
85
- kernelSpec = await this . getKernelSpecFromActiveInterpreter ( kernelName , resource ) ;
77
+ if ( kernelSpec ) {
78
+ return kernelSpec ;
79
+ }
86
80
87
- if ( kernelSpec ) {
88
- this . writeCache ( this . cache ) . ignoreErrors ( ) ;
89
- return kernelSpec ;
90
- }
81
+ // Check in active interpreter first
82
+ kernelSpec = await this . getKernelSpecFromActiveInterpreter ( kernelName , resource ) ;
91
83
92
- const diskSearch = this . findDiskPath ( kernelName ) ;
93
- const interpreterSearch = this . getInterpreterPaths ( resource ) . then ( ( interpreterPaths ) => {
94
- return this . findInterpreterPath ( interpreterPaths , kernelName ) ;
95
- } ) ;
84
+ if ( kernelSpec ) {
85
+ this . writeCache ( this . cache ) . ignoreErrors ( ) ;
86
+ return kernelSpec ;
87
+ }
96
88
97
- let result = await Promise . race ( [ diskSearch , interpreterSearch ] ) ;
98
- if ( ! result ) {
99
- const both = await Promise . all ( [ diskSearch , interpreterSearch ] ) ;
100
- result = both [ 0 ] ? both [ 0 ] : both [ 1 ] ;
101
- }
89
+ const diskSearch = this . findDiskPath ( kernelName ) ;
90
+ const interpreterSearch = this . getInterpreterPaths ( resource ) . then ( ( interpreterPaths ) => {
91
+ return this . findInterpreterPath ( interpreterPaths , kernelName ) ;
92
+ } ) ;
93
+
94
+ let result = await Promise . race ( [ diskSearch , interpreterSearch ] ) ;
95
+ if ( ! result ) {
96
+ const both = await Promise . all ( [ diskSearch , interpreterSearch ] ) ;
97
+ result = both [ 0 ] ? both [ 0 ] : both [ 1 ] ;
98
+ }
102
99
103
- foundKernel = result ? result : await this . getDefaultKernelSpec ( resource ) ;
100
+ foundKernel = result ? result : await this . getDefaultKernelSpec ( resource ) ;
101
+ } else {
102
+ // For a previous default kernel spec, just use it again
103
+ foundKernel = this . reuseExistingDefaultSpec ( kernelSpecMetadata ) ;
104
+ }
104
105
} else {
106
+ // If we don't have kernel metadata then just get a default spec to use
105
107
foundKernel = await this . getDefaultKernelSpec ( resource ) ;
106
108
}
107
109
@@ -132,6 +134,10 @@ export class KernelFinder implements IKernelFinder {
132
134
return this . workspaceToKernels . get ( workspaceFolderId ) ! ;
133
135
}
134
136
137
+ private reuseExistingDefaultSpec ( kernelMetadata : nbformat . IKernelspecMetadata ) : IJupyterKernelSpec {
138
+ return createDefaultKernelSpec ( kernelMetadata . display_name ) ;
139
+ }
140
+
135
141
private async findResourceKernelSpecs ( resource : Resource ) : Promise < IJupyterKernelSpec [ ] > {
136
142
const results : IJupyterKernelSpec [ ] = [ ] ;
137
143
@@ -184,7 +190,11 @@ export class KernelFinder implements IKernelFinder {
184
190
traceError ( `Failed to parse kernelspec ${ specPath } ` ) ;
185
191
return undefined ;
186
192
}
187
- return new JupyterKernelSpec ( kernelJson , specPath ) ;
193
+ const kernelSpec : IJupyterKernelSpec = new JupyterKernelSpec ( kernelJson , specPath ) ;
194
+
195
+ // Some registered kernel specs do not have a name, in this case use the last part of the path
196
+ kernelSpec . name = kernelJson ?. name || path . basename ( path . dirname ( specPath ) ) ;
197
+ return kernelSpec ;
188
198
}
189
199
190
200
// For the given resource, find atll the file paths for kernel specs that wewant to associate with this
@@ -319,18 +329,7 @@ export class KernelFinder implements IKernelFinder {
319
329
private async getDefaultKernelSpec ( resource : Resource ) : Promise < IJupyterKernelSpec > {
320
330
const activeInterpreter = await this . interpreterService . getActiveInterpreter ( resource ) ;
321
331
322
- // This creates a default kernel spec. When launched, 'python' argument will map to using the interpreter
323
- // associated with the current resource for launching.
324
- const defaultSpec : Kernel . ISpecModel = {
325
- name : defaultSpecName + Date . now ( ) . toString ( ) ,
326
- language : 'python' ,
327
- display_name : activeInterpreter ?. displayName ? activeInterpreter . displayName : 'Python 3' ,
328
- metadata : { } ,
329
- argv : [ 'python' , '-m' , 'ipykernel_launcher' , '-f' , connectionFilePlaceholder ] ,
330
- env : { } ,
331
- resources : { }
332
- } ;
333
- return new JupyterKernelSpec ( defaultSpec ) ;
332
+ return createDefaultKernelSpec ( activeInterpreter ?. displayName ) ;
334
333
}
335
334
336
335
private async readCache ( ) : Promise < string [ ] > {
@@ -369,12 +368,7 @@ export class KernelFinder implements IKernelFinder {
369
368
} ) ;
370
369
371
370
if ( kernelJsonFile ) {
372
- const spec = await this . getKernelSpec ( kernelJsonFile ) ;
373
-
374
- if ( spec ) {
375
- spec . name = kernelName ;
376
- return spec ;
377
- }
371
+ return this . getKernelSpec ( kernelJsonFile ) ;
378
372
}
379
373
380
374
return undefined ;
0 commit comments