mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Notebooks: Improve Jupyter server start time (#13838)
* let jupyter server find port * add undefined to return type * remove time log * add test for port number * check that match[1] exists
This commit is contained in:
@@ -16,11 +16,9 @@ import { IServerInstance } from './common';
|
|||||||
import { JupyterServerInstallation } from './jupyterServerInstallation';
|
import { JupyterServerInstallation } from './jupyterServerInstallation';
|
||||||
import * as utils from '../common/utils';
|
import * as utils from '../common/utils';
|
||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import * as ports from '../common/ports';
|
|
||||||
|
|
||||||
const NotebookConfigFilename = 'jupyter_notebook_config.py';
|
const NotebookConfigFilename = 'jupyter_notebook_config.py';
|
||||||
const CustomJsFilename = 'custom.js';
|
const CustomJsFilename = 'custom.js';
|
||||||
const defaultPort = 8888;
|
|
||||||
|
|
||||||
type MessageListener = (data: string | Buffer) => void;
|
type MessageListener = (data: string | Buffer) => void;
|
||||||
type ErrorListener = (err: any) => void;
|
type ErrorListener = (err: any) => void;
|
||||||
@@ -85,6 +83,7 @@ export class PerFolderServerInstance implements IServerInstance {
|
|||||||
|
|
||||||
private _systemJupyterDir: string;
|
private _systemJupyterDir: string;
|
||||||
private _port: string;
|
private _port: string;
|
||||||
|
private _token: string;
|
||||||
private _uri: vscode.Uri;
|
private _uri: vscode.Uri;
|
||||||
private _isStarted: boolean = false;
|
private _isStarted: boolean = false;
|
||||||
private _isStopping: boolean = false;
|
private _isStopping: boolean = false;
|
||||||
@@ -211,12 +210,8 @@ export class PerFolderServerInstance implements IServerInstance {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let notebookDirectory = this.getNotebookDirectory();
|
let notebookDirectory = this.getNotebookDirectory();
|
||||||
// Find a port in a given range. If run into trouble, try another port inside the given range
|
this._token = await utils.getRandomToken();
|
||||||
let port = await ports.strictFindFreePort(new ports.StrictPortFindOptions(defaultPort, defaultPort + 1000));
|
let startCommand = `"${this.options.install.pythonExecutable}" "${this.notebookScriptPath}" --no-browser --no-mathjax --notebook-dir "${notebookDirectory}" --NotebookApp.token=${this._token}`;
|
||||||
let token = await utils.getRandomToken();
|
|
||||||
this._uri = vscode.Uri.parse(`http://localhost:${port}/?token=${token}`);
|
|
||||||
this._port = port.toString();
|
|
||||||
let startCommand = `"${this.options.install.pythonExecutable}" "${this.notebookScriptPath}" --no-browser --no-mathjax --notebook-dir "${notebookDirectory}" --port=${port} --NotebookApp.token=${token}`;
|
|
||||||
this.notifyStarting(this.options.install, startCommand);
|
this.notifyStarting(this.options.install, startCommand);
|
||||||
|
|
||||||
// Execute the command
|
// Execute the command
|
||||||
@@ -242,14 +237,11 @@ export class PerFolderServerInstance implements IServerInstance {
|
|||||||
let handleStdout = (data: string | Buffer) => { install.outputChannel.appendLine(data.toString()); };
|
let handleStdout = (data: string | Buffer) => { install.outputChannel.appendLine(data.toString()); };
|
||||||
let handleStdErr = (data: string | Buffer) => {
|
let handleStdErr = (data: string | Buffer) => {
|
||||||
// For some reason, URL info is sent on StdErr
|
// For some reason, URL info is sent on StdErr
|
||||||
let [url, port] = this.matchUrlAndPort(data);
|
let port: string = this.getPort(data);
|
||||||
if (url) {
|
if (port) {
|
||||||
// For now, will verify port matches
|
let url: vscode.Uri = vscode.Uri.parse(`http://localhost:${port}/?token=${this._token}`);
|
||||||
if (url.authority !== this._uri.authority
|
this._uri = url;
|
||||||
|| url.query !== this._uri.query) {
|
this._port = port;
|
||||||
this._uri = url;
|
|
||||||
this._port = port;
|
|
||||||
}
|
|
||||||
this.notifyStarted(install, url.toString(true));
|
this.notifyStarted(install, url.toString(true));
|
||||||
this._isStarted = true;
|
this._isStarted = true;
|
||||||
|
|
||||||
@@ -300,7 +292,7 @@ export class PerFolderServerInstance implements IServerInstance {
|
|||||||
return path.dirname(this.options.documentPath);
|
return path.dirname(this.options.documentPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private matchUrlAndPort(data: string | Buffer): [vscode.Uri, string] {
|
private getPort(data: string | Buffer): string | undefined {
|
||||||
// regex: Looks for the successful startup log message like:
|
// regex: Looks for the successful startup log message like:
|
||||||
// [C 12:08:51.947 NotebookApp]
|
// [C 12:08:51.947 NotebookApp]
|
||||||
//
|
//
|
||||||
@@ -308,18 +300,8 @@ export class PerFolderServerInstance implements IServerInstance {
|
|||||||
// to login with a token:
|
// to login with a token:
|
||||||
// http://localhost:8888/?token=f5ee846e9bd61c3a8d835ecd9b965591511a331417b997b7
|
// http://localhost:8888/?token=f5ee846e9bd61c3a8d835ecd9b965591511a331417b997b7
|
||||||
let dataString = data.toString();
|
let dataString = data.toString();
|
||||||
let urlMatch = dataString.match(/\[C[\s\S]+ {8}(.+:(\d+)\/.*)$/m);
|
let match = dataString.match(/.+:(\d+)\/.*$/m);
|
||||||
if (urlMatch) {
|
return match && match[1] ? match[1] : undefined;
|
||||||
// Legacy case: manually parse token info if no token/port were passed
|
|
||||||
return [vscode.Uri.parse(urlMatch[1]), urlMatch[2]];
|
|
||||||
} else if (this._uri && dataString.match(/jupyter notebook .*is running at:/im)) {
|
|
||||||
// Default case: detect the notebook started message, indicating our preferred port and token were used
|
|
||||||
//
|
|
||||||
// Newer versions of the notebook package include a version number (e.g. 1.2.3) as part of the "notebook running"
|
|
||||||
// message, thus the regex above.
|
|
||||||
return [this._uri, this._port];
|
|
||||||
}
|
|
||||||
return [undefined, undefined];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private notifyStarted(install: JupyterServerInstallation, jupyterUri: string): void {
|
private notifyStarted(install: JupyterServerInstallation, jupyterUri: string): void {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const successMessage = `[I 14:00:38.811 NotebookApp] The Jupyter Notebook is run
|
|||||||
[I 14:00:38.812 NotebookApp] http://localhost:8891/?token=...
|
[I 14:00:38.812 NotebookApp] http://localhost:8891/?token=...
|
||||||
[I 14:00:38.812 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
|
[I 14:00:38.812 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
|
||||||
`;
|
`;
|
||||||
|
const expectedPort = '8891';
|
||||||
|
|
||||||
describe('Jupyter server instance', function (): void {
|
describe('Jupyter server instance', function (): void {
|
||||||
let expectedPath = 'mydir/notebook.ipynb';
|
let expectedPath = 'mydir/notebook.ipynb';
|
||||||
@@ -89,6 +90,7 @@ describe('Jupyter server instance', function (): void {
|
|||||||
let hostAndPort = serverInstance.uri.authority.split(':');
|
let hostAndPort = serverInstance.uri.authority.split(':');
|
||||||
// verify port was set as expected
|
// verify port was set as expected
|
||||||
should(hostAndPort[1]).length(4);
|
should(hostAndPort[1]).length(4);
|
||||||
|
should(hostAndPort[1]).equal(expectedPort);
|
||||||
|
|
||||||
// And I expect it to be started
|
// And I expect it to be started
|
||||||
should(serverInstance.isStarted).be.true();
|
should(serverInstance.isStarted).be.true();
|
||||||
|
|||||||
Reference in New Issue
Block a user