Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eff739c

Browse files
authored
Ensure working directory is an actual directory (microsoft#13403)
* Ensure working directory is an actual directory * Fix interrupt double test too. * Skip remote when doing raw kernel * Fix switch failure and try to fix ipywidget failure too * Update comment * Fix hygiene
1 parent 66a4ffb commit eff739c

File tree

8 files changed

+73
-42
lines changed

8 files changed

+73
-42
lines changed

src/client/datascience/jupyter/jupyterUtils.ts

+13
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
'use strict';
44
import '../../common/extensions';
55

6+
import * as fs from 'fs-extra';
67
import * as path from 'path';
78
import { Uri } from 'vscode';
89

910
import { IWorkspaceService } from '../../common/application/types';
11+
import { Resource } from '../../common/types';
1012
import { noop } from '../../common/utils/misc';
1113
import { SystemVariables } from '../../common/variables/systemVariables';
1214
import { getJupyterConnectionDisplayName } from '../jupyter/jupyterConnection';
@@ -77,3 +79,14 @@ export function createRemoteConnectionInfo(
7779
getAuthHeader: serverUri ? () => getJupyterServerUri(uri)?.authorizationHeader : undefined
7880
};
7981
}
82+
83+
export async function computeWorkingDirectory(resource: Resource, workspace: IWorkspaceService): Promise<string> {
84+
// Returning directly doesn't seem to work (typescript complains)
85+
// tslint:disable-next-line: no-unnecessary-local-variable
86+
const workingDirectory =
87+
resource && resource.scheme === 'file' && (await fs.pathExists(path.dirname(resource.fsPath)))
88+
? path.dirname(resource.fsPath)
89+
: workspace.getWorkspaceFolder(resource)?.uri.fsPath || process.cwd();
90+
91+
return workingDirectory;
92+
}

src/client/datascience/jupyter/liveshare/hostJupyterServer.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import '../../../common/extensions';
66
// tslint:disable-next-line: no-require-imports
77
import cloneDeep = require('lodash/cloneDeep');
88
import * as os from 'os';
9-
import * as path from 'path';
109
import * as vscode from 'vscode';
1110
import { CancellationToken } from 'vscode-jsonrpc';
1211
import * as vsls from 'vsls/vscode';
@@ -40,6 +39,7 @@ import {
4039
INotebookServerLaunchInfo
4140
} from '../../types';
4241
import { JupyterServerBase } from '../jupyterServer';
42+
import { computeWorkingDirectory } from '../jupyterUtils';
4343
import { KernelSelector } from '../kernels/kernelSelector';
4444
import { HostJupyterNotebook } from './hostJupyterNotebook';
4545
import { LiveShareParticipantHost } from './liveShareParticipantMixin';
@@ -217,10 +217,7 @@ export class HostJupyterServer extends LiveShareParticipantHost(JupyterServerBas
217217
}
218218

219219
// Figure out the working directory we need for our new notebook.
220-
const workingDirectory =
221-
resource && resource.scheme === 'file'
222-
? path.dirname(resource.fsPath)
223-
: this.workspaceService.getWorkspaceFolder(resource)?.uri.fsPath || process.cwd();
220+
const workingDirectory = await computeWorkingDirectory(resource, this.workspaceService);
224221

225222
// Start a session (or use the existing one if allowed)
226223
const session =

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
'use strict';
55

66
import { ChildProcess } from 'child_process';
7+
import * as fs from 'fs-extra';
78
import { inject, injectable } from 'inversify';
89
import { IDisposable } from 'monaco-editor';
910
import { ObservableExecutionResult } from '../../common/process/types';
@@ -29,7 +30,10 @@ export class PythonKernelLauncherDaemon implements IDisposable {
2930
kernelSpec: IJupyterKernelSpec,
3031
interpreter?: PythonInterpreter
3132
): Promise<{ observableOutput: ObservableExecutionResult<string>; daemon: IPythonKernelDaemon | undefined }> {
32-
const daemon = await this.daemonPool.get(resource, kernelSpec, interpreter);
33+
const [daemon, wdExists] = await Promise.all([
34+
this.daemonPool.get(resource, kernelSpec, interpreter),
35+
fs.pathExists(workingDirectory)
36+
]);
3337

3438
// Check to see if we have the type of kernelspec that we expect
3539
const args = kernelSpec.argv.slice();
@@ -51,7 +55,7 @@ export class PythonKernelLauncherDaemon implements IDisposable {
5155
// If we don't have a KernelDaemon here then we have an execution service and should use that to launch
5256
const observableOutput = daemon.execModuleObservable(moduleName, moduleArgs, {
5357
env,
54-
cwd: workingDirectory
58+
cwd: wdExists ? workingDirectory : process.cwd()
5559
});
5660

5761
return { observableOutput, daemon: undefined };

src/client/datascience/raw-kernel/liveshare/hostRawNotebookProvider.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
'use strict';
44
import '../../../common/extensions';
55

6-
import * as path from 'path';
76
import * as vscode from 'vscode';
87
import { CancellationToken } from 'vscode-jsonrpc';
98
import * as vsls from 'vsls/vscode';
@@ -24,6 +23,7 @@ import * as localize from '../../../common/utils/localize';
2423
import { noop } from '../../../common/utils/misc';
2524
import { IServiceContainer } from '../../../ioc/types';
2625
import { Identifiers, LiveShare, LiveShareCommands, Settings } from '../../constants';
26+
import { computeWorkingDirectory } from '../../jupyter/jupyterUtils';
2727
import { KernelSelector, KernelSpecInterpreter } from '../../jupyter/kernels/kernelSelector';
2828
import { HostJupyterNotebook } from '../../jupyter/liveshare/hostJupyterNotebook';
2929
import { LiveShareParticipantHost } from '../../jupyter/liveshare/liveShareParticipantMixin';
@@ -142,10 +142,7 @@ export class HostRawNotebookProvider
142142
? this.progressReporter.createProgressIndicator(localize.DataScience.connectingIPyKernel())
143143
: undefined;
144144

145-
const workingDirectory =
146-
resource && resource.scheme === 'file'
147-
? path.dirname(resource.fsPath)
148-
: this.workspaceService.getWorkspaceFolder(resource)?.uri.fsPath || process.cwd();
145+
const workingDirectory = await computeWorkingDirectory(resource, this.workspaceService);
149146

150147
const rawSession = new RawJupyterSession(
151148
this.kernelLauncher,

src/test/datascience/interactiveWindow.functional.test.tsx

+30-24
Original file line numberDiff line numberDiff line change
@@ -577,35 +577,41 @@ for i in range(0, 100):
577577

578578
runTest(
579579
'Interrupt double',
580-
async () => {
581-
let interruptedKernel = false;
582-
const { window, mount } = await getOrCreateInteractiveWindow(ioc);
583-
window.notebook?.onKernelInterrupted(() => (interruptedKernel = true));
584-
585-
let timerCount = 0;
586-
addContinuousMockData(ioc, interruptCode, async (_c) => {
587-
timerCount += 1;
588-
await sleep(0.5);
589-
return Promise.resolve({ result: '', haveMore: timerCount < 100 });
590-
});
580+
async (context: Mocha.Context) => {
581+
if (ioc.mockJupyter) {
582+
let interruptedKernel = false;
583+
const { window, mount } = await getOrCreateInteractiveWindow(ioc);
584+
window.notebook?.onKernelInterrupted(() => (interruptedKernel = true));
585+
586+
let timerCount = 0;
587+
addContinuousMockData(ioc, interruptCode, async (_c) => {
588+
timerCount += 1;
589+
await sleep(0.5);
590+
return Promise.resolve({ result: '', haveMore: timerCount < 100 });
591+
});
591592

592-
addMockData(ioc, interruptCode, undefined, 'text/plain');
593+
addMockData(ioc, interruptCode, undefined, 'text/plain');
593594

594-
// Run the interrupt code and then interrupt it twice to make sure we can interrupt twice
595-
const waitForAdd = addCode(ioc, interruptCode);
595+
// Run the interrupt code and then interrupt it twice to make sure we can interrupt twice
596+
const waitForAdd = addCode(ioc, interruptCode);
596597

597-
// 'Click' the button in the react control. We need to verify we can
598-
// click it more than once.
599-
const interrupt = findButton(mount.wrapper, InteractivePanel, 4);
600-
interrupt?.simulate('click');
601-
await sleep(0.1);
602-
interrupt?.simulate('click');
598+
// 'Click' the button in the react control. We need to verify we can
599+
// click it more than once.
600+
const interrupt = findButton(mount.wrapper, InteractivePanel, 4);
601+
interrupt?.simulate('click');
602+
await sleep(0.1);
603+
interrupt?.simulate('click');
603604

604-
// We should get out of the wait for add
605-
await waitForAdd;
605+
// We should get out of the wait for add
606+
await waitForAdd;
606607

607-
// We should have also fired an interrupt
608-
assert.ok(interruptedKernel, 'Kernel was not interrupted');
608+
// We should have also fired an interrupt
609+
assert.ok(interruptedKernel, 'Kernel was not interrupted');
610+
} else {
611+
// Timing is too iffy for real jupyter. However we really just
612+
// want to make sure double interrupt is supported so keep the test.
613+
context.skip();
614+
}
609615
},
610616
() => {
611617
return ioc;

src/test/datascience/nativeEditor.functional.test.tsx

+13-3
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ suite('DataScience Native Editor', () => {
317317
});
318318

319319
runMountedTest('Remote kernel can be switched and remembered', async () => {
320+
// Turn off raw kernel for this test as it's testing remote
321+
ioc.setExperimentState(LocalZMQKernel.experiment, false);
322+
320323
const pythonService = await createPythonService(ioc, 2);
321324

322325
// Skip test for older python and raw kernel and mac
@@ -341,9 +344,16 @@ suite('DataScience Native Editor', () => {
341344
// Create another notebook and connect it to the already running kernel of the other one
342345
when(ioc.applicationShell.showQuickPick(anything(), anything(), anything())).thenCall(
343346
async (o: IKernelSpecQuickPickItem[]) => {
344-
const existing = o.find((s) => s.selection.kernelModel?.numberOfConnections);
345-
if (existing) {
346-
return existing;
347+
const existing = o.filter((s) => s.selection.kernelModel?.numberOfConnections);
348+
349+
// Might be more than one. Get the oldest one. It has the actual activity.
350+
const sorted = existing.sort(
351+
(a, b) =>
352+
b.selection.kernelModel!.lastActivityTime.getTime() -
353+
a.selection.kernelModel!.lastActivityTime.getTime()
354+
);
355+
if (sorted && sorted.length) {
356+
return sorted[0];
347357
}
348358
}
349359
);

src/test/datascience/notebook.functional.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ suite('DataScience notebook tests', () => {
403403
async () => {
404404
const pythonService = await createPythonService(ioc);
405405

406-
if (pythonService) {
406+
if (pythonService && !useRawKernel) {
407407
const configFile = path.join(
408408
EXTENSION_ROOT_DIR,
409409
'src',
@@ -443,7 +443,7 @@ suite('DataScience notebook tests', () => {
443443

444444
const pythonService = await createPythonService(ioc);
445445

446-
if (pythonService) {
446+
if (pythonService && !useRawKernel) {
447447
const configFile = path.join(
448448
EXTENSION_ROOT_DIR,
449449
'src',
@@ -509,7 +509,7 @@ suite('DataScience notebook tests', () => {
509509
runTest('Remote', async () => {
510510
const pythonService = await createPythonService(ioc);
511511

512-
if (pythonService) {
512+
if (pythonService && !useRawKernel) {
513513
const configFile = path.join(
514514
EXTENSION_ROOT_DIR,
515515
'src',

src/test/datascience/uiTests/ipywidget.ui.functional.test.ts

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as path from 'path';
1414
import * as sinon from 'sinon';
1515
import { Disposable } from 'vscode';
1616
import { LocalZMQKernel } from '../../../client/common/experiments/groups';
17+
import { sleep } from '../../../client/common/utils/async';
1718
import { EXTENSION_ROOT_DIR } from '../../../client/constants';
1819
import { retryIfFail as retryIfFailOriginal } from '../../common';
1920
import { mockedVSCodeNamespaces } from '../../vscode-mock';
@@ -170,6 +171,9 @@ use(chaiAsPromised);
170171
async function verifySliderWidgetIsAvailableAfterExecution(notebookUI: NotebookEditorUI) {
171172
await notebookUI.executeCell(0);
172173

174+
// Slider output could take a bit. Wait some
175+
await sleep(2000);
176+
173177
await retryIfFail(async () => {
174178
await assert.eventually.isTrue(notebookUI.cellHasOutput(0));
175179
const outputHtml = await notebookUI.getCellOutputHTML(0);

0 commit comments

Comments
 (0)