Skip to content

Commit

Permalink
More dead code
Browse files Browse the repository at this point in the history
  • Loading branch information
DonJayamanne committed May 18, 2023
1 parent 3dd04e9 commit ceb9e79
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 169 deletions.
24 changes: 5 additions & 19 deletions src/kernels/jupyter/connection/jupyterConnection.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,56 +167,42 @@ suite('JupyterConnection', () => {
when(serviceContainer.get<IConfigurationService>(IConfigurationService)).thenReturn(instance(configService));
});

function createConnectionWaiter(cancelToken?: CancellationToken) {
function createConnectionWaiter() {
return new JupyterConnectionWaiter(
launchResult,
notebookDir,
Uri.file(EXTENSION_ROOT_DIR),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getServerInfoStub as any,
instance(serviceContainer),
undefined,
cancelToken
undefined
);
}
test('Successfully gets connection info', async () => {
(<any>dsSettings).jupyterLaunchTimeout = 10_000;
const waiter = createConnectionWaiter();
observableOutput.next({ source: 'stderr', out: 'Jupyter listening on http://123.123.123:8888' });

const connection = await waiter.waitForConnection();
const connection = await waiter.ready;

assert.equal(connection.localLaunch, true);
assert.equal(connection.baseUrl, expectedServerInfo.url);
assert.equal(connection.hostName, expectedServerInfo.hostname);
assert.equal(connection.token, expectedServerInfo.token);
});
test('Disconnect event is fired in connection', async () => {
(<any>dsSettings).jupyterLaunchTimeout = 10_000;
const waiter = createConnectionWaiter();
observableOutput.next({ source: 'stderr', out: 'Jupyter listening on http://123.123.123:8888' });
let disconnected = false;

const connection = await waiter.waitForConnection();
connection.disconnected(() => (disconnected = true));

childProc.emit('exit', 999);

assert.isTrue(disconnected);
});
test('Throw timeout error', async () => {
(<any>dsSettings).jupyterLaunchTimeout = 10;
const waiter = createConnectionWaiter();

const promise = waiter.waitForConnection();
const promise = waiter.ready;

await assert.isRejected(promise, DataScience.jupyterLaunchTimedOut);
});
test('Throw crashed error', async () => {
const exitCode = 999;
const waiter = createConnectionWaiter();

const promise = waiter.waitForConnection();
const promise = waiter.ready;
childProc.emit('exit', exitCode);
observableOutput.complete();

Expand Down
4 changes: 1 addition & 3 deletions src/kernels/jupyter/finder/remoteKernelFinder.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Session } from '@jupyterlab/services';
import { assert } from 'chai';
import { anything, instance, mock, when } from 'ts-mockito';
import { getDisplayNameOrNameOfKernelConnection } from '../../helpers';
import { Disposable, EventEmitter, Memento, Uri } from 'vscode';
import { Disposable, Memento, Uri } from 'vscode';
import { CryptoUtils } from '../../../platform/common/crypto';
import { noop } from '../../../test/core';
import {
Expand Down Expand Up @@ -41,7 +41,6 @@ suite(`Remote Kernel Finder`, () => {
let fs: IFileSystemNode;
let memento: Memento;
let jupyterSessionManager: IJupyterSessionManager;
const dummyEvent = new EventEmitter<number>();
let cachedRemoteKernelValidator: IJupyterRemoteCachedKernelValidator;
let kernelsChanged: TestEventHandler<void>;
let jupyterConnection: JupyterConnection;
Expand All @@ -50,7 +49,6 @@ suite(`Remote Kernel Finder`, () => {
localLaunch: false,
baseUrl: 'http://foobar',
displayName: 'foobar connection',
disconnected: dummyEvent.event,
token: '',
hostName: 'foobar',
rootDirectory: Uri.file('.'),
Expand Down
3 changes: 0 additions & 3 deletions src/kernels/jupyter/jupyterUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,6 @@ export function createRemoteConnectionInfo(uri: string, serverUri?: IJupyterServ
serverUri && serverUri.displayName
? serverUri.displayName
: getJupyterConnectionDisplayName(token, baseUrl),
disconnected: (_l) => {
return { dispose: noop };
},
dispose: noop,
rootDirectory: Uri.file(''),
// Temporarily support workingDirectory as a fallback for old extensions using that (to be removed in the next release).
Expand Down
148 changes: 34 additions & 114 deletions src/kernels/jupyter/launcher/jupyterConnection.node.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { ChildProcess } from 'child_process';
import { Subscription } from 'rxjs/Subscription';
import { CancellationError, CancellationToken, Disposable, Event, EventEmitter, Uri } from 'vscode';
import { Uri } from 'vscode';
import { IConfigurationService, IDisposable } from '../../../platform/common/types';
import { Cancellation } from '../../../platform/common/cancellation';
import { traceError, traceWarning, traceVerbose } from '../../../platform/logging';
import { ObservableExecutionResult, Output } from '../../../platform/common/process/types.node';
import { Deferred, createDeferred } from '../../../platform/common/utils/async';
import { createDeferred } from '../../../platform/common/utils/async';
import { DataScience } from '../../../platform/common/utils/localize';
import { IServiceContainer } from '../../../platform/ioc/types';
import { RegExpValues } from '../../../platform/common/constants';
Expand All @@ -28,56 +26,46 @@ const urlMatcher = new RegExp(RegExpValues.UrlPatternRegEx);
* When starting a local jupyter server, this object waits for the server to come up.
*/
export class JupyterConnectionWaiter implements IDisposable {
private startPromise: Deferred<IJupyterConnection>;
private startPromise = createDeferred<IJupyterConnection>();
private launchTimeout: NodeJS.Timer | number;
private configService: IConfigurationService;
private stderr: string[] = [];
private connectionDisposed = false;
private stderr = '';
public readonly ready = this.startPromise.promise;
private subscriptions: Subscription[] = [];

constructor(
private readonly launchResult: ObservableExecutionResult<string>,
private readonly notebookDir: Uri,
private readonly rootDir: Uri,
private readonly getServerInfo: (cancelToken?: CancellationToken) => Promise<JupyterServerInfo[] | undefined>,
private readonly getServerInfo: () => Promise<JupyterServerInfo[] | undefined>,
serviceContainer: IServiceContainer,
private readonly interpreter: PythonEnvironment | undefined,
private cancelToken?: CancellationToken
private readonly interpreter: PythonEnvironment | undefined
) {
this.configService = serviceContainer.get<IConfigurationService>(IConfigurationService);

// Cancel our start promise if a cancellation occurs
if (cancelToken) {
cancelToken.onCancellationRequested(() => this.startPromise.reject(new CancellationError()));
}

// Setup our start promise
this.startPromise = createDeferred<IJupyterConnection>();

// We want to reject our Jupyter connection after a specific timeout
const settings = this.configService.getSettings(undefined);
const jupyterLaunchTimeout = settings.jupyterLaunchTimeout;

this.launchTimeout = setTimeout(() => {
this.launchTimedOut();
if (!this.startPromise.completed) {
this.rejectStartPromise(DataScience.jupyterLaunchTimedOut);
}
}, jupyterLaunchTimeout);

// Listen for crashes
let exitCode = 0;
if (launchResult.proc) {
launchResult.proc.on('exit', (c) => (exitCode = c ? c : 0));
}
let stderr = '';
// Listen on stderr for its connection information
this.subscriptions.push(
launchResult.out.subscribe(
(output: Output<string>) => {
traceVerbose(output.out.toString());
if (output.source === 'stderr') {
stderr += output.out;
this.stderr.push(output.out);
this.extractConnectionInformation(stderr);
} else {
this.output(output.out);
this.stderr += output.out;
this.extractConnectionInformation(this.stderr);
}
},
(e) => this.rejectStartPromise(e),
Expand All @@ -92,23 +80,12 @@ export class JupyterConnectionWaiter implements IDisposable {
this.subscriptions.forEach((d) => d.unsubscribe());
}

public waitForConnection(): Promise<IJupyterConnection> {
return this.startPromise.promise;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private output(data: any) {
if (!this.connectionDisposed) {
traceVerbose(data.toString('utf8'));
}
}

// From a list of jupyter server infos try to find the matching jupyter that we launched
private getJupyterURL(serverInfos: JupyterServerInfo[] | undefined, data: string) {
if (serverInfos && serverInfos.length > 0 && !this.startPromise.completed) {
const matchInfo = serverInfos.find((info) => {
return arePathsSame(getFilePath(this.notebookDir), getFilePath(Uri.file(info.notebook_dir)));
});
const matchInfo = serverInfos.find((info) =>
arePathsSame(getFilePath(this.notebookDir), getFilePath(Uri.file(info.notebook_dir)))
);
if (matchInfo) {
const url = matchInfo.url;
const token = matchInfo.token;
Expand All @@ -126,7 +103,7 @@ export class JupyterConnectionWaiter implements IDisposable {
private getJupyterURLFromString(data: string) {
const urlMatch = urlMatcher.exec(data);
const groups = urlMatch?.groups;
if (!this.startPromise.completed && groups && (groups.LOCAL || groups.IP)) {
if (this.startPromise.completed && groups && (groups.LOCAL || groups.IP)) {
// Rebuild the URI from our group hits
const host = groups.LOCAL ? groups.LOCAL : groups.IP;
const uriString = `${groups.PREFIX}${host}${groups.REST}`;
Expand Down Expand Up @@ -154,57 +131,40 @@ export class JupyterConnectionWaiter implements IDisposable {
}

private extractConnectionInformation = (data: string) => {
this.output(data);

const httpMatch = RegExpValues.HttpPattern.exec(data);

if (httpMatch && this.notebookDir && this.startPromise && !this.startPromise.completed && this.getServerInfo) {
if (RegExpValues.HttpPattern.exec(data) && !this.startPromise.completed) {
// .then so that we can keep from pushing aync up to the subscribed observable function
this.getServerInfo(this.cancelToken)
this.getServerInfo()
.then((serverInfos) => this.getJupyterURL(serverInfos, data))
.catch((ex) => traceWarning('Failed to get server info', ex));
}

// Sometimes jupyter will return a 403 error. Not sure why. We used
// to fail on this, but it looks like jupyter works with this error in place.
};

private launchTimedOut = () => {
if (!this.startPromise.completed) {
this.rejectStartPromise(DataScience.jupyterLaunchTimedOut);
}
};

private resolveStartPromise(url: string, baseUrl: string, token: string, hostName: string) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
clearTimeout(this.launchTimeout as any);
if (!this.startPromise.rejected) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
const connection = new JupyterConnection(
url,
baseUrl,
token,
hostName,
this.rootDir,
this.launchResult,
this.launchResult.proc,
() => (this.connectionDisposed = true)
);
this.startPromise.resolve(connection);
if (this.startPromise.completed) {
return;
}
this.startPromise.resolve({
localLaunch: true,
url,
baseUrl,
token,
hostName,
rootDirectory: this.rootDir,
displayName: getJupyterConnectionDisplayName(token, baseUrl),
dispose: () => this.launchResult.dispose()
});
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private rejectStartPromise = (message: string | Error) => {
private rejectStartPromise(message: string | Error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
clearTimeout(this.launchTimeout as any);
if (!this.startPromise.resolved) {
message = typeof message === 'string' ? message : message.message;
let error: Error;
const stderr = this.stderr.join('\n');
if (Cancellation.isCanceled(this.cancelToken)) {
error = new CancellationError();
} else if (stderr.includes('Jupyter command `jupyter-notebook` not found')) {
const stderr = this.stderr;
if (stderr.includes('Jupyter command `jupyter-notebook` not found')) {
error = new JupyterNotebookNotInstalled(message, stderr, this.interpreter);
} else if (stderr.includes('Running as root is not recommended. Use --allow-root to bypass')) {
error = new JupyterCannotBeLaunchedWithRootError(message, stderr, this.interpreter);
Expand All @@ -213,45 +173,5 @@ export class JupyterConnectionWaiter implements IDisposable {
}
this.startPromise.reject(error);
}
};
}

// Represents an active connection to a running jupyter notebook
class JupyterConnection implements IJupyterConnection {
public readonly localLaunch: boolean = true;
private eventEmitter: EventEmitter<number> = new EventEmitter<number>();
constructor(
public readonly url: string,
public readonly baseUrl: string,
public readonly token: string,
public readonly hostName: string,
public readonly rootDirectory: Uri,
private readonly disposable: Disposable,
childProc: ChildProcess | undefined,
private readonly onDidDispose: () => void
) {
// If the local process exits, set our exit code and fire our event
if (childProc) {
childProc.on('exit', (c) => {
// Our code expects the exit code to be of type `number` or `undefined`.
const code = typeof c === 'number' ? c : 0;
this.eventEmitter.fire(code);
});
}
}

public get displayName(): string {
return getJupyterConnectionDisplayName(this.token, this.baseUrl);
}

public get disconnected(): Event<number> {
return this.eventEmitter.event;
}

public dispose() {
this.onDidDispose();
if (this.disposable) {
this.disposable.dispose();
}
}
}
Loading

0 comments on commit ceb9e79

Please sign in to comment.