22// Licensed under the MIT License.
33'use strict' ;
44
5- import { Kernel } from '@jupyterlab/services ' ;
5+ import type { nbformat } from '@jupyterlab/coreutils ' ;
66import { inject , injectable , named } from 'inversify' ;
77import * as path from 'path' ;
88import { CancellationToken , CancellationTokenSource } from 'vscode' ;
@@ -14,6 +14,7 @@ import { IExtensionContext, IInstaller, InstallerResponse, IPathUtils, Product,
1414import { IInterpreterLocatorService , IInterpreterService , KNOWN_PATH_SERVICE } from '../../interpreter/contracts' ;
1515import { captureTelemetry } from '../../telemetry' ;
1616import { Telemetry } from '../constants' ;
17+ import { createDefaultKernelSpec , defaultKernelSpecName } from '../jupyter/kernels/helpers' ;
1718import { JupyterKernelSpec } from '../jupyter/kernels/jupyterKernelSpec' ;
1819import { IJupyterKernelSpec } from '../types' ;
1920import { getKernelInterpreter } from './helpers' ;
@@ -27,14 +28,6 @@ const macJupyterPath = path.join('Library', 'Jupyter', 'kernels');
2728const baseKernelPath = path . join ( 'share' , 'jupyter' , 'kernels' ) ;
2829
2930const 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- }
3831
3932// This class searches for a kernel that matches the given kernel name.
4033// First it searches on a global persistent state, then on the installed python interpreters,
@@ -68,40 +61,49 @@ export class KernelFinder implements IKernelFinder {
6861 @captureTelemetry ( Telemetry . KernelFinderPerf )
6962 public async findKernelSpec (
7063 resource : Resource ,
71- kernelName ?: string ,
64+ kernelSpecMetadata ?: nbformat . IKernelspecMetadata ,
7265 cancelToken ?: CancellationToken
7366 ) : Promise < IJupyterKernelSpec > {
7467 this . cache = await this . readCache ( ) ;
7568 let foundKernel : IJupyterKernelSpec | undefined ;
7669
77- if ( kernelName && ! kernelName . includes ( defaultSpecName ) ) {
78- let kernelSpec = await this . searchCache ( kernelName ) ;
70+ const kernelName = kernelSpecMetadata ?. name ;
7971
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 ) ;
8376
84- // Check in active interpreter first
85- kernelSpec = await this . getKernelSpecFromActiveInterpreter ( kernelName , resource ) ;
77+ if ( kernelSpec ) {
78+ return kernelSpec ;
79+ }
8680
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 ) ;
9183
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+ }
9688
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+ }
10299
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+ }
104105 } else {
106+ // If we don't have kernel metadata then just get a default spec to use
105107 foundKernel = await this . getDefaultKernelSpec ( resource ) ;
106108 }
107109
@@ -132,6 +134,10 @@ export class KernelFinder implements IKernelFinder {
132134 return this . workspaceToKernels . get ( workspaceFolderId ) ! ;
133135 }
134136
137+ private reuseExistingDefaultSpec ( kernelMetadata : nbformat . IKernelspecMetadata ) : IJupyterKernelSpec {
138+ return createDefaultKernelSpec ( kernelMetadata . display_name ) ;
139+ }
140+
135141 private async findResourceKernelSpecs ( resource : Resource ) : Promise < IJupyterKernelSpec [ ] > {
136142 const results : IJupyterKernelSpec [ ] = [ ] ;
137143
@@ -184,7 +190,11 @@ export class KernelFinder implements IKernelFinder {
184190 traceError ( `Failed to parse kernelspec ${ specPath } ` ) ;
185191 return undefined ;
186192 }
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 ;
188198 }
189199
190200 // 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 {
319329 private async getDefaultKernelSpec ( resource : Resource ) : Promise < IJupyterKernelSpec > {
320330 const activeInterpreter = await this . interpreterService . getActiveInterpreter ( resource ) ;
321331
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 ) ;
334333 }
335334
336335 private async readCache ( ) : Promise < string [ ] > {
@@ -369,12 +368,7 @@ export class KernelFinder implements IKernelFinder {
369368 } ) ;
370369
371370 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 ) ;
378372 }
379373
380374 return undefined ;
0 commit comments