Skip to content

Commit 7c746b8

Browse files
authored
Ensure kernelspec is readonly (#16090)
* Ensure kernel variables are cloned before updating * Ensure kernelspec is readonnly
1 parent 5bc44a4 commit 7c746b8

File tree

5 files changed

+53
-46
lines changed

5 files changed

+53
-46
lines changed

src/kernels/raw/finder/localKernelSpecFinderBase.node.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -367,18 +367,22 @@ export async function loadKernelSpec(
367367
kernelJson.name = `${kernelJson.name}.${argv.join('#')}`;
368368
}
369369
}
370-
kernelJson.metadata = kernelJson.metadata || {};
371-
kernelJson.metadata.vscode = kernelJson.metadata.vscode || {};
372-
if (!kernelJson.metadata.vscode.originalSpecFile) {
373-
kernelJson.metadata.vscode.originalSpecFile = specPath.fsPath;
370+
const metadata = (kernelJson.metadata as ReadWrite<typeof kernelJson.metadata>) || {};
371+
metadata.vscode = metadata.vscode || {};
372+
if (!metadata.vscode.originalSpecFile) {
373+
metadata.vscode.originalSpecFile = specPath.fsPath;
374374
}
375-
if (!kernelJson.metadata.vscode.originalDisplayName) {
376-
kernelJson.metadata.vscode.originalDisplayName = kernelJson.display_name;
375+
if (!metadata.vscode.originalDisplayName) {
376+
metadata.vscode.originalDisplayName = kernelJson.display_name;
377377
}
378-
if (kernelJson.metadata.originalSpecFile) {
379-
kernelJson.metadata.vscode.originalSpecFile = kernelJson.metadata.originalSpecFile;
380-
delete kernelJson.metadata.originalSpecFile;
378+
if (metadata.originalSpecFile) {
379+
metadata.vscode.originalSpecFile = metadata.originalSpecFile;
380+
delete metadata.originalSpecFile;
381381
}
382+
kernelJson.metadata = metadata;
383+
384+
// Some registered kernel specs do not have a name, in this case use the last part of the path
385+
kernelJson.name = kernelJson?.name || path.basename(path.dirname(specPath.fsPath));
382386

383387
const kernelSpec: IJupyterKernelSpec = new JupyterKernelSpec(
384388
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -389,9 +393,6 @@ export async function loadKernelSpec(
389393
getKernelRegistrationInfo(kernelJson)
390394
);
391395

392-
// Some registered kernel specs do not have a name, in this case use the last part of the path
393-
kernelSpec.name = kernelJson?.name || path.basename(path.dirname(specPath.fsPath));
394-
395396
// Possible user deleted the underlying interpreter.
396397
const interpreterPath = interpreter?.uri.fsPath || kernelJson?.metadata?.interpreter?.path;
397398
const isEmptyCondaEnv = isCondaEnvironmentWithoutPython(interpreter);

src/kernels/raw/launcher/kernelEnvVarsService.node.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { inject, injectable } from 'inversify';
55
import { logger } from '../../../platform/logging';
66
import { getDisplayPath } from '../../../platform/common/platform/fs-paths';
7-
import { IConfigurationService, Resource } from '../../../platform/common/types';
7+
import { IConfigurationService, Resource, type ReadWrite } from '../../../platform/common/types';
88
import { noop } from '../../../platform/common/utils/misc';
99
import {
1010
IEnvironmentVariablesService,
@@ -51,7 +51,10 @@ export class KernelEnvironmentVariablesService {
5151
kernelSpec: IJupyterKernelSpec,
5252
token?: CancellationToken
5353
) {
54-
let kernelEnv = kernelSpec.env && Object.keys(kernelSpec.env).length > 0 ? kernelSpec.env : undefined;
54+
let kernelEnv =
55+
kernelSpec.env && Object.keys(kernelSpec.env).length > 0
56+
? (Object.assign({}, kernelSpec.env) as ReadWrite<NodeJS.ProcessEnv>)
57+
: undefined;
5558
const isPythonKernel = (kernelSpec.language || '').toLowerCase() === PYTHON_LANGUAGE;
5659
// If an interpreter was not explicitly passed in, check for an interpreter path in the kernelspec to use
5760
if (!interpreter && kernelSpec.interpreterPath) {

src/kernels/raw/launcher/kernelEnvVarsService.unit.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { anything, instance, mock, when } from 'ts-mockito';
1515
import { KernelEnvironmentVariablesService } from './kernelEnvVarsService.node';
1616
import { IJupyterKernelSpec } from '../../types';
1717
import { Uri } from 'vscode';
18-
import { IConfigurationService, IWatchableJupyterSettings } from '../../../platform/common/types';
18+
import { IConfigurationService, IWatchableJupyterSettings, type ReadWrite } from '../../../platform/common/types';
1919
import { JupyterSettings } from '../../../platform/common/configSettings';
2020

2121
use(chaiAsPromised);
@@ -34,7 +34,7 @@ suite('Kernel Environment Variables Service', () => {
3434
uri: pathFile,
3535
id: pathFile.fsPath
3636
};
37-
let kernelSpec: IJupyterKernelSpec;
37+
let kernelSpec: ReadWrite<IJupyterKernelSpec>;
3838
let processEnv: NodeJS.ProcessEnv;
3939
const originalEnvVars = Object.assign({}, process.env);
4040
let processPath: string | undefined;

src/kernels/raw/launcher/kernelProcess.node.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ import {
3535
IOutputChannel,
3636
IJupyterSettings,
3737
IExperimentService,
38-
Experiments
38+
Experiments,
39+
type ReadWrite
3940
} from '../../../platform/common/types';
4041
import { createDeferred, raceTimeout } from '../../../platform/common/utils/async';
4142
import { DataScience } from '../../../platform/common/utils/localize';
@@ -117,7 +118,7 @@ export class KernelProcess extends ObservableDisposable implements IKernelProces
117118
private exitEvent = new EventEmitter<{ exitCode?: number; reason?: string; stderr: string }>();
118119
private launchedOnce?: boolean;
119120
private connectionFile?: Uri;
120-
private _launchKernelSpec?: IJupyterKernelSpec;
121+
private _launchKernelSpec?: ReadWrite<IJupyterKernelSpec>;
121122
private interrupter?: Interrupter;
122123
private exitEventFired = false;
123124
private readonly _kernelConnectionMetadata: Readonly<

src/kernels/types.ts

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -596,11 +596,11 @@ export interface IJupyterKernelSpec {
596596
* @type {string}
597597
* @memberof IJupyterKernel
598598
*/
599-
id?: string;
600-
name: string;
601-
language?: string;
602-
executable: string; // argv[0] of the kernelspec.json
603-
env?: NodeJS.ProcessEnv | undefined;
599+
readonly id?: string;
600+
readonly name: string;
601+
readonly language?: string;
602+
readonly executable: string; // argv[0] of the kernelspec.json
603+
readonly env?: Readonly<NodeJS.ProcessEnv> | undefined;
604604
/**
605605
* Kernel display name.
606606
*
@@ -612,45 +612,47 @@ export interface IJupyterKernelSpec {
612612
* A dictionary of additional attributes about this kernel; used by clients to aid in kernel selection.
613613
* Optionally storing the interpreter information in the metadata (helping extension search for kernels that match an interpereter).
614614
*/
615-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
616-
readonly metadata?: Record<string, any> & {
617-
vscode?: {
615+
readonly metadata?: Readonly<
616+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
617+
Record<string, any> & {
618+
vscode?: {
619+
/**
620+
* Optionally where the original user-created kernel spec json is located on the local FS.
621+
* Remember when using non-raw we create kernelspecs from the original spec.
622+
*/
623+
originalSpecFile?: string;
624+
/**
625+
* E.g. assume we're loading a kernlespec for a default Python kernel, the name would be `python3`
626+
* However we give this a completely different name, and at that point its not possible to determine
627+
* whether this is a default kernel or not.
628+
* Hence keep track of the original name in the metadata.
629+
*/
630+
originalDisplayName?: string;
631+
};
632+
interpreter?: Partial<PythonEnvironment_PythonApi>; // read from disk so has to follow old format
618633
/**
619-
* Optionally where the original user-created kernel spec json is located on the local FS.
620-
* Remember when using non-raw we create kernelspecs from the original spec.
634+
* @deprecated (use metadata.jupyter.originalSpecFile)
621635
*/
622636
originalSpecFile?: string;
623637
/**
624-
* E.g. assume we're loading a kernlespec for a default Python kernel, the name would be `python3`
625-
* However we give this a completely different name, and at that point its not possible to determine
626-
* whether this is a default kernel or not.
627-
* Hence keep track of the original name in the metadata.
638+
* Whether the kernels supports the debugger Protocol.
628639
*/
629-
originalDisplayName?: string;
630-
};
631-
interpreter?: Partial<PythonEnvironment_PythonApi>; // read from disk so has to follow old format
632-
/**
633-
* @deprecated (use metadata.jupyter.originalSpecFile)
634-
*/
635-
originalSpecFile?: string;
636-
/**
637-
* Whether the kernels supports the debugger Protocol.
638-
*/
639-
debugger?: boolean;
640-
};
640+
debugger?: boolean;
641+
}
642+
>;
641643
readonly argv: string[];
642644
/**
643645
* Optionally where this kernel spec json is located on the local FS.
644646
*/
645-
specFile?: string;
647+
readonly specFile?: string;
646648
/**
647649
* Optionally the Interpreter this kernel spec belongs to.
648650
* You can have kernel specs that are scoped to an interpreter.
649651
* E.g. if you have Python in `c:\Python\Python3.8`
650652
* Then you could have kernels in `<sys.prefix folder for this interpreter>\share\jupyter\kernels`
651653
* Plenty of conda packages ship kernels in this manner (beakerx, etc).
652654
*/
653-
interpreterPath?: string; // Has to be a string as old kernelspecs wrote it this way
655+
readonly interpreterPath?: string; // Has to be a string as old kernelspecs wrote it this way
654656
readonly interrupt_mode?: 'message' | 'signal';
655657
/**
656658
* Whether the kernelspec is registered by VS Code

0 commit comments

Comments
 (0)