Skip to content

Commit 28ded73

Browse files
Connect to raw kernel with command line args instead of connection file (#11894)
1 parent df6c38d commit 28ded73

File tree

6 files changed

+38
-37
lines changed

6 files changed

+38
-37
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@
271271
],
272272
"env": {
273273
// Remove `X` prefix to test with real browser to host DS ui (for DS functional tests).
274-
"xVSC_PYTHON_DS_UI_BROWSER": "1",
274+
"XVSC_PYTHON_DS_UI_BROWSER": "1",
275275
// Remove `X` prefix to test with real python (for DS functional tests).
276276
"XVSCODE_PYTHON_ROLLING": "1",
277277
// Remove 'X' to turn on all logging in the debug output

news/2 Fixes/11883.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use command line arguments to launch our raw kernels as opposed to a connection file. The connection file seems to be causing issues in particular on windows CI machines with permissions.

src/client/datascience/kernel-launcher/kernelLauncher.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { inject, injectable } from 'inversify';
66
import * as portfinder from 'portfinder';
77
import { promisify } from 'util';
88
import * as uuid from 'uuid/v4';
9-
import { IFileSystem } from '../../common/platform/types';
109
import { IProcessServiceFactory } from '../../common/process/types';
1110
import { Resource } from '../../common/types';
1211
import { PythonInterpreter } from '../../interpreter/contracts';
@@ -27,7 +26,6 @@ export class KernelLauncher implements IKernelLauncher {
2726
private static nextFreePortToTryAndUse = PortToStartFrom;
2827
constructor(
2928
@inject(IProcessServiceFactory) private processExecutionFactory: IProcessServiceFactory,
30-
@inject(IFileSystem) private file: IFileSystem,
3129
@inject(KernelDaemonPool) private readonly daemonPool: KernelDaemonPool
3230
) {}
3331

@@ -40,7 +38,6 @@ export class KernelLauncher implements IKernelLauncher {
4038
const connection = await this.getKernelConnection();
4139
const kernelProcess = new KernelProcess(
4240
this.processExecutionFactory,
43-
this.file,
4441
this.daemonPool,
4542
connection,
4643
kernelSpec,

src/client/datascience/kernel-launcher/kernelProcess.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import * as tcpPortUsed from 'tcp-port-used';
77
import { Event, EventEmitter } from 'vscode';
88
import { PYTHON_LANGUAGE } from '../../common/constants';
99
import { traceError, traceInfo, traceWarning } from '../../common/logger';
10-
import { IFileSystem, TemporaryFile } from '../../common/platform/types';
1110
import { IProcessServiceFactory, ObservableExecutionResult } from '../../common/process/types';
1211
import { Resource } from '../../common/types';
1312
import { noop, swallowExceptions } from '../../common/utils/misc';
@@ -39,7 +38,6 @@ export class KernelProcess implements IKernelProcess {
3938
return this.kernelSpec.language.toLowerCase() === PYTHON_LANGUAGE.toLowerCase();
4039
}
4140
private _process?: ChildProcess;
42-
private connectionFile?: TemporaryFile;
4341
private exitEvent = new EventEmitter<{ exitCode?: number; reason?: string }>();
4442
private pythonKernelLauncher?: PythonKernelLauncherDaemon;
4543
private launchedOnce?: boolean;
@@ -49,7 +47,6 @@ export class KernelProcess implements IKernelProcess {
4947
private readonly originalKernelSpec: IJupyterKernelSpec;
5048
constructor(
5149
private readonly processExecutionFactory: IProcessServiceFactory,
52-
private readonly file: IFileSystem,
5350
private readonly daemonPool: KernelDaemonPool,
5451
private readonly _connection: IKernelConnection,
5552
kernelSpec: IJupyterKernelSpec,
@@ -72,7 +69,8 @@ export class KernelProcess implements IKernelProcess {
7269
}
7370
this.launchedOnce = true;
7471

75-
await this.createAndUpdateConnectionFile();
72+
// Update our connection arguments in the kernel spec
73+
this.updateConnectionArgs();
7674

7775
const exeObs = await this.launchAsObservable();
7876

@@ -127,7 +125,6 @@ export class KernelProcess implements IKernelProcess {
127125
this.exitEvent.fire();
128126
});
129127
swallowExceptions(() => this.pythonKernelLauncher?.dispose());
130-
swallowExceptions(() => this.connectionFile?.dispose());
131128
}
132129

133130
// Make sure that the heartbeat channel is open for connections
@@ -144,21 +141,42 @@ export class KernelProcess implements IKernelProcess {
144141
}
145142
}
146143

147-
private async createAndUpdateConnectionFile() {
148-
// Create the connection file so that any user can destroy it. The kernel will
149-
// actually read it, delete it, and recreate it with more restricted permissions
150-
this.connectionFile = await this.file.createTemporaryFile('.json', 0o777);
151-
await this.file.writeFile(this.connectionFile.filePath, JSON.stringify(this._connection), {
152-
encoding: 'utf-8',
153-
flag: 'w'
154-
});
155-
156-
// Update the args in the kernelspec to include the conenction file.
144+
// Instead of having to use a connection file update our local copy of the kernelspec to launch
145+
// directly with command line arguments
146+
private updateConnectionArgs() {
147+
// First check to see if we have a kernelspec that expects a connection file,
148+
// Error if we don't have one. We expect '-f', '{connectionfile}' in our launch args
157149
const indexOfConnectionFile = findIndexOfConnectionFile(this._kernelSpec);
158-
if (indexOfConnectionFile === -1) {
150+
if (
151+
indexOfConnectionFile === -1 ||
152+
indexOfConnectionFile === 0 ||
153+
this._kernelSpec.argv[indexOfConnectionFile - 1] !== '-f'
154+
) {
159155
throw new Error(`Connection file not found in kernelspec json args, ${this._kernelSpec.argv.join(' ')}`);
160156
}
161-
this._kernelSpec.argv[indexOfConnectionFile] = this.connectionFile.filePath;
157+
158+
// Slice out -f and the connection file from the args
159+
this._kernelSpec.argv.splice(indexOfConnectionFile - 1, 2);
160+
161+
// Add in our connection command line args
162+
this._kernelSpec.argv.push(...this.addConnectionArgs());
163+
}
164+
165+
// Add the command line arguments
166+
private addConnectionArgs(): string[] {
167+
const newConnectionArgs: string[] = [];
168+
169+
newConnectionArgs.push(`--ip=${this._connection.ip}`);
170+
newConnectionArgs.push(`--stdin=${this._connection.stdin_port}`);
171+
newConnectionArgs.push(`--control=${this._connection.control_port}`);
172+
newConnectionArgs.push(`--hb=${this._connection.hb_port}`);
173+
newConnectionArgs.push(`--Session.signature_scheme="${this._connection.signature_scheme}"`);
174+
newConnectionArgs.push(`--Session.key=b"${this._connection.key}"`); // Note we need the 'b here at the start for a byte string
175+
newConnectionArgs.push(`--shell=${this._connection.shell_port}`);
176+
newConnectionArgs.push(`--transport="${this._connection.transport}"`);
177+
newConnectionArgs.push(`--iopub=${this._connection.iopub_port}`);
178+
179+
return newConnectionArgs;
162180
}
163181

164182
private async launchAsObservable() {

src/test/datascience/kernelLauncher.functional.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { assert } from 'chai';
66

77
import { KernelMessage } from '@jupyterlab/services';
88
import * as uuid from 'uuid/v4';
9-
import { IFileSystem } from '../../client/common/platform/types';
109
import { IProcessServiceFactory } from '../../client/common/process/types';
1110
import { createDeferred } from '../../client/common/utils/async';
1211
import { JupyterZMQBinariesNotFoundError } from '../../client/datascience/jupyter/jupyterZMQBinariesNotFoundError';
@@ -39,10 +38,9 @@ suite('DataScience - Kernel Launcher', () => {
3938
ioc = new DataScienceIocContainer();
4039
ioc.registerDataScienceTypes();
4140
kernelFinder = new MockKernelFinder(ioc.serviceContainer.get<IKernelFinder>(IKernelFinder));
42-
const file = ioc.serviceContainer.get<IFileSystem>(IFileSystem);
4341
const processServiceFactory = ioc.serviceContainer.get<IProcessServiceFactory>(IProcessServiceFactory);
4442
const daemonPool = ioc.serviceContainer.get<KernelDaemonPool>(KernelDaemonPool);
45-
kernelLauncher = new KernelLauncher(processServiceFactory, file, daemonPool);
43+
kernelLauncher = new KernelLauncher(processServiceFactory, daemonPool);
4644
await ioc.activate();
4745
if (!ioc.mockJupyter) {
4846
pythonInterpreter = await ioc.getJupyterCapableInterpreter();

src/test/datascience/raw-kernel/rawKernel.functional.test.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@
22
// Licensed under the MIT License.
33
'use strict';
44
import { assert } from 'chai';
5-
import * as fs from 'fs-extra';
65
import { noop } from 'jquery';
7-
import * as os from 'os';
8-
import * as path from 'path';
96
import * as uuid from 'uuid/v4';
10-
import { IFileSystem } from '../../../client/common/platform/types';
117
import { IProcessServiceFactory } from '../../../client/common/process/types';
128
import { createDeferred, sleep } from '../../../client/common/utils/async';
139
import { KernelDaemonPool } from '../../../client/datascience/kernel-launcher/kernelDaemonPool';
@@ -21,7 +17,6 @@ import { requestExecute, requestInspect } from './rawKernelTestHelpers';
2117
// tslint:disable:no-any no-multiline-string max-func-body-length no-console max-classes-per-file trailing-comma
2218
suite('DataScience raw kernel tests', () => {
2319
let ioc: DataScienceIocContainer;
24-
let connectionFile: string;
2520
let rawKernel: RawKernel;
2621
const connectionInfo = {
2722
shell_port: 57718,
@@ -67,8 +62,6 @@ suite('DataScience raw kernel tests', () => {
6762
.getInterpreterDetails(ioc.getSettings().pythonPath);
6863
assert.ok(interpreter, 'No jupyter interpreter found');
6964
// Start our kernel
70-
connectionFile = path.join(os.tmpdir(), `tmp_${Date.now()}_k.json`);
71-
await fs.writeFile(connectionFile, JSON.stringify(connectionInfo), { encoding: 'utf-8', flag: 'w' });
7265
const kernelSpec: IJupyterKernelSpec = {
7366
argv: [interpreter!.path, '-m', 'ipykernel_launcher', '-f', '{connection_file}'],
7467
metadata: {
@@ -83,7 +76,6 @@ suite('DataScience raw kernel tests', () => {
8376
};
8477
kernelProcess = new KernelProcess(
8578
ioc.get<IProcessServiceFactory>(IProcessServiceFactory),
86-
ioc.get<IFileSystem>(IFileSystem),
8779
ioc.get<KernelDaemonPool>(KernelDaemonPool),
8880
connectionInfo as any,
8981
kernelSpec,
@@ -97,11 +89,6 @@ suite('DataScience raw kernel tests', () => {
9789
async function disconnectFromKernel() {
9890
if (kernelProcess) {
9991
await kernelProcess.dispose().catch(noop);
100-
try {
101-
await fs.remove(connectionFile);
102-
} catch {
103-
noop();
104-
}
10592
}
10693
}
10794

0 commit comments

Comments
 (0)