Skip to content

Commit ea0dc14

Browse files
Correctly handle interpreter changes for Interactive and Notebook (#8385)
1 parent a139d63 commit ea0dc14

File tree

8 files changed

+59
-9
lines changed

8 files changed

+59
-9
lines changed

news/2 Fixes/8019.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correctly restart jupyter sessions when the active interpreter is changed.

src/client/datascience/interactive-common/interactiveBase.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,9 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
935935

936936
private onInterpreterChanged = () => {
937937
// Update our load promise. We need to restart the jupyter server
938-
this.loadPromise = this.reloadWithNew();
938+
if (this.loadPromise) {
939+
this.loadPromise = this.reloadWithNew();
940+
}
939941
}
940942

941943
private async stopServer(): Promise<void> {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class GuestJupyterExecution extends LiveShareParticipantGuest(JupyterExec
6363
commandFactory,
6464
serviceContainer);
6565
asyncRegistry.push(this);
66-
this.serverCache = new ServerCache(configuration, workspace, fileSystem);
66+
this.serverCache = new ServerCache(configuration, workspace, fileSystem, interpreterService);
6767
}
6868

6969
public async dispose(): Promise<void> {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class HostJupyterExecution
6464
configService,
6565
commandFactory,
6666
serviceContainer);
67-
this.serverCache = new ServerCache(configService, workspace, fileSys);
67+
this.serverCache = new ServerCache(configService, workspace, fileSys, interpreterService);
6868
}
6969

7070
public async dispose(): Promise<void> {

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as uuid from 'uuid/v4';
99
import { IWorkspaceService } from '../../../common/application/types';
1010
import { IFileSystem } from '../../../common/platform/types';
1111
import { IAsyncDisposable, IConfigurationService } from '../../../common/types';
12+
import { IInterpreterService } from '../../../interpreter/contracts';
1213
import { INotebookServer, INotebookServerOptions } from '../../types';
1314

1415
export class ServerCache implements IAsyncDisposable {
@@ -18,7 +19,8 @@ export class ServerCache implements IAsyncDisposable {
1819
constructor(
1920
private configService: IConfigurationService,
2021
private workspace: IWorkspaceService,
21-
private fileSystem: IFileSystem
22+
private fileSystem: IFileSystem,
23+
private interpreterService: IInterpreterService
2224
) { }
2325

2426
public async get(options?: INotebookServerOptions): Promise<INotebookServer | undefined> {
@@ -60,13 +62,16 @@ export class ServerCache implements IAsyncDisposable {
6062
}
6163

6264
public async generateDefaultOptions(options?: INotebookServerOptions): Promise<INotebookServerOptions> {
65+
const activeInterpreter = await this.interpreterService.getActiveInterpreter();
66+
const activeInterpreterPath = activeInterpreter ? activeInterpreter.path : undefined;
6367
return {
6468
enableDebugging: options ? options.enableDebugging : false,
6569
uri: options ? options.uri : undefined,
6670
useDefaultConfig: options ? options.useDefaultConfig : true, // Default for this is true.
6771
usingDarkTheme: options ? options.usingDarkTheme : undefined,
6872
purpose: options ? options.purpose : uuid(),
69-
workingDir: options && options.workingDir ? options.workingDir : await this.calculateWorkingDirectory()
73+
workingDir: options && options.workingDir ? options.workingDir : await this.calculateWorkingDirectory(),
74+
interpreterPath: options && options.interpreterPath ? options.interpreterPath : activeInterpreterPath
7075
};
7176
}
7277

@@ -76,11 +81,12 @@ export class ServerCache implements IAsyncDisposable {
7681
} else {
7782
// combine all the values together to make a unique key
7883
const uri = options.uri ? options.uri : '';
84+
const interpreter = options.interpreterPath ? options.interpreterPath : '';
7985
const useFlag = options.useDefaultConfig ? 'true' : 'false';
8086
const debug = options.enableDebugging ? 'true' : 'false';
8187
// tslint:disable-next-line:no-suspicious-comment
8288
// TODO: Should there be some separator in the key?
83-
return `${options.purpose}${uri}${useFlag}${options.workingDir}${debug}`;
89+
return `${options.purpose}${uri}${useFlag}${options.workingDir}${debug}${interpreter}`;
8490
}
8591
}
8692

src/client/datascience/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export interface INotebookServerOptions {
107107
usingDarkTheme?: boolean;
108108
useDefaultConfig?: boolean;
109109
workingDir?: string;
110+
interpreterPath?: string;
110111
purpose: string;
111112
}
112113

src/test/datascience/dataScienceIocContainer.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -599,9 +599,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
599599
const interpreterManager = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
600600
interpreterManager.initialize();
601601

602-
if (this.mockJupyter) {
603-
this.mockJupyter.addInterpreter(this.workingPython, SupportedCommands.all);
604-
}
602+
this.addInterpreter(this.workingPython, SupportedCommands.all);
605603
}
606604

607605
// tslint:disable:any
@@ -718,6 +716,12 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
718716
this.pythonSettings.jediEnabled = enabled;
719717
}
720718

719+
public addInterpreter(newInterpreter: PythonInterpreter, commands: SupportedCommands) {
720+
if (this.mockJupyter) {
721+
this.mockJupyter.addInterpreter(newInterpreter, commands);
722+
}
723+
}
724+
721725
private findPythonPath(): string {
722726
try {
723727
const output = child_process.execFileSync('python', ['-c', 'import sys;print(sys.executable)'], { encoding: 'utf8' });

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as fs from 'fs-extra';
88
import { injectable } from 'inversify';
99
import * as os from 'os';
1010
import * as path from 'path';
11+
import { SemVer } from 'semver';
1112
import { Readable, Writable } from 'stream';
1213
import { anything, instance, mock, when } from 'ts-mockito';
1314
import * as TypeMoq from 'typemoq';
@@ -23,6 +24,7 @@ import { IFileSystem } from '../../client/common/platform/types';
2324
import { IProcessServiceFactory, IPythonExecutionFactory, Output } from '../../client/common/process/types';
2425
import { createDeferred, waitForPromise } from '../../client/common/utils/async';
2526
import { noop } from '../../client/common/utils/misc';
27+
import { Architecture } from '../../client/common/utils/platform';
2628
import { concatMultilineStringInput } from '../../client/datascience/common';
2729
import { Identifiers } from '../../client/datascience/constants';
2830
import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory';
@@ -42,13 +44,15 @@ import {
4244
import {
4345
IInterpreterService,
4446
IKnownSearchPathsForInterpreters,
47+
InterpreterType,
4548
PythonInterpreter
4649
} from '../../client/interpreter/contracts';
4750
import { generateTestState, ICellViewModel } from '../../datascience-ui/interactive-common/mainState';
4851
import { asyncDump } from '../common/asyncDump';
4952
import { sleep } from '../core';
5053
import { DataScienceIocContainer } from './dataScienceIocContainer';
5154
import { getConnectionInfo, getIPConnectionInfo, getNotebookCapableInterpreter } from './jupyterHelpers';
55+
import { SupportedCommands } from './mockJupyterManager';
5256
import { MockPythonService } from './mockPythonService';
5357

5458
// tslint:disable:no-any no-multiline-string max-func-body-length no-console max-classes-per-file trailing-comma
@@ -519,6 +523,38 @@ suite('DataScience notebook tests', () => {
519523
}
520524
});
521525

526+
runTest('Change Interpreter', async () => {
527+
const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false;
528+
529+
// Real Jupyter doesn't help this test at all and is tricky to set up for it, so just skip it
530+
if (!isRollingBuild) {
531+
const server = await createNotebook(true);
532+
533+
// Create again, we should get the same server from the cache
534+
const server2 = await createNotebook(true);
535+
assert.equal(server, server2, 'With no settings changed we should return the cached server');
536+
537+
// Create a new mock interpreter with a different path
538+
const newPython: PythonInterpreter = {
539+
path: '/foo/bar/baz/python.exe',
540+
version: new SemVer('3.6.6-final'),
541+
sysVersion: '1.0.0.0',
542+
sysPrefix: 'Python',
543+
type: InterpreterType.Unknown,
544+
architecture: Architecture.x64,
545+
};
546+
547+
// Add interpreter into mock jupyter service and set it as active
548+
ioc.addInterpreter(newPython, SupportedCommands.all);
549+
550+
// Create a new notebook, we should not be the same anymore
551+
const server3 = await createNotebook(true);
552+
assert.notEqual(server, server3, 'With interpreter changed we should return a new server');
553+
} else {
554+
console.log(`Skipping Change Interpreter test in non-mocked Jupyter case`);
555+
}
556+
});
557+
522558
runTest('Restart kernel', async () => {
523559
addMockData(`a=1${os.EOL}a`, 1);
524560
addMockData(`a+=1${os.EOL}a`, 2);

0 commit comments

Comments
 (0)