Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web version of the DataFrame #10545

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { inject, injectable } from 'inversify';
import { SemVer } from 'semver';
import { CancellationToken } from 'vscode';
import { parseSemVer } from '../../../platform/common/utils.node';
import { parseSemVer } from '../../../platform/common/utils';
import { PythonEnvironment } from '../../../platform/pythonEnvironments/info';
import { ResourceSet } from '../../../platform/vscode-path/map';
import { JupyterCommands } from '../../../telemetry';
Expand Down
5 changes: 4 additions & 1 deletion src/platform/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ export enum Telemetry {
UserInstalledPandas = 'DATASCIENCE.USER_INSTALLED_PANDAS',
UserDidNotInstallJupyter = 'DATASCIENCE.USER_DID_NOT_INSTALL_JUPYTER',
UserDidNotInstallPandas = 'DATASCIENCE.USER_DID_NOT_INSTALL_PANDAS',
FailedToInstallPandas = 'DATASCIENCE.FAILED_TO_INSTALL_PANDAS',
OpenedInteractiveWindow = 'DATASCIENCE.OPENED_INTERACTIVE',
OpenNotebookFailure = 'DS_INTERNAL.NATIVE.OPEN_NOTEBOOK_FAILURE',
FindKernelForLocalConnection = 'DS_INTERNAL.FIND_KERNEL_FOR_LOCAL_CONNECTION',
Expand Down Expand Up @@ -638,7 +639,9 @@ export enum Telemetry {
FetchError = 'DS_INTERNAL.WEB_FETCH_ERROR',
TerminalShellIdentification = 'TERMINAL_SHELL_IDENTIFICATION',
TerminalEnvVariableExtraction = 'TERMINAL_ENV_VAR_EXTRACTION',
JupyterInstalled = 'JUPYTER_IS_INSTALLED'
JupyterInstalled = 'JUPYTER_IS_INSTALLED',
NoIdleKernel = 'DATASCIENCE.NO_IDLE_KERNEL',
NoActiveKernelSession = 'DATASCIENCE.NO_ACTIVE_KERNEL_SESSION'
}

export enum NativeKeyboardCommandTelemetry {
Expand Down
12 changes: 0 additions & 12 deletions src/platform/common/utils.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import * as path from '../../platform/vscode-path/path';
import * as fsExtra from 'fs-extra';
import { SemVer, parse } from 'semver';
import { Uri } from 'vscode';
import { fsPathToUri } from '../vscode-path/utils';
import { IWorkspaceService } from './application/types';
Expand Down Expand Up @@ -71,14 +70,3 @@ export async function calculateWorkingDirectory(
}
return workingDir;
}

// For the given string parse it out to a SemVer or return undefined
export function parseSemVer(versionString: string): SemVer | undefined {
const versionMatch = /^\s*(\d+)\.(\d+)\.(.+)\s*$/.exec(versionString);
if (versionMatch && versionMatch.length > 2) {
const major = parseInt(versionMatch[1], 10);
const minor = parseInt(versionMatch[2], 10);
const build = parseInt(versionMatch[3], 10);
return parse(`${major}.${minor}.${build}`, true) ?? undefined;
}
}
12 changes: 12 additions & 0 deletions src/platform/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import type * as nbformat from '@jupyterlab/nbformat';
import * as uriPath from '../../platform/vscode-path/resources';
import { SemVer, parse } from 'semver';
import { NotebookData, NotebookDocument, TextDocument, Uri, workspace } from 'vscode';
import { InteractiveWindowView, jupyterLanguageToMonacoLanguageMapping, JupyterNotebookView } from './constants';
import { traceError, traceInfo } from '../logging';
Expand Down Expand Up @@ -366,3 +367,14 @@ export function removeLinesFromFrontAndBack(code: string | string[]): string {
const lines = Array.isArray(code) ? code : code.splitLines({ trim: false, removeEmptyEntries: false });
return removeLinesFromFrontAndBackNoConcat(lines).join('\n');
}

// For the given string parse it out to a SemVer or return undefined
export function parseSemVer(versionString: string): SemVer | undefined {
const versionMatch = /^\s*(\d+)\.(\d+)\.(.+)\s*$/.exec(versionString);
if (versionMatch && versionMatch.length > 2) {
const major = parseInt(versionMatch[1], 10);
const minor = parseInt(versionMatch[2], 10);
const build = parseInt(versionMatch[3], 10);
return parse(`${major}.${minor}.${build}`, true) ?? undefined;
}
}
13 changes: 13 additions & 0 deletions src/platform/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,19 @@ export namespace DataScience {
localize('DataScience.webNotSupported', `Operation not supported in web version of Jupyter Extension.`);
export const validationErrorMessageForRemoteUrlProtocolNeedsToBeHttpOrHttps = () =>
localize('DataScience.validationErrorMessageForRemoteUrlProtocolNeedsToBeHttpOrHttps', 'Has to be http(s)');
export const noIdleKernel = () => localize('DataScience.noIdleKernel', 'No idle kernel');
export const noActiveKernelSession = () =>
localize('DataScience.noActiveKernelSession', 'No active kernel session');
export const failedToGetVersionOfPandas = () =>
localize(
{ key: 'DataScience.failedToGetVersionOfPandas', comment: ['{Locked="Pandas"}'] },
'Failed to get version of Pandas to use the Data Viewer'
);
export const failedToInstallPandas = () =>
localize(
{ key: 'DataScience.failedToInstallPandas', comment: ['{Locked="Pandas"}'] },
'Failed to install Pandas to use the Data Viewer'
);
}

export namespace Installer {
Expand Down
11 changes: 11 additions & 0 deletions src/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ export interface IEventNamePropertyMapping {
[Telemetry.UserInstalledPandas]: never | undefined;
[Telemetry.UserDidNotInstallJupyter]: never | undefined;
[Telemetry.UserDidNotInstallPandas]: never | undefined;
[Telemetry.FailedToInstallPandas]: never | undefined;
[Telemetry.PythonNotInstalled]: {
action:
| 'displayed' // Message displayed.
Expand Down Expand Up @@ -1382,4 +1383,14 @@ export interface IEventNamePropertyMapping {
* Total time take to copy the nb extensions folder.
*/
[Telemetry.IPyWidgetNbExtensionCopyTime]: never | undefined;
/**
* Useful when we need an active kernel in order to execute commands silently.
* Used by the data frame when attempting to install Pandas.
*/
[Telemetry.NoIdleKernel]: never | undefined;
/**
* Useful when we need an active kernel session in order to execute commands silently.
* Used by the data frame when attempting to install Pandas.
*/
[Telemetry.NoActiveKernelSession]: never | undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

import { assert } from 'chai';
import * as path from '../../../platform/vscode-path/path';
import { SemVer } from 'semver';
import { anything, deepEqual, instance, mock, verify, when } from 'ts-mockito';
import { ApplicationShell } from '../../../platform/common/application/applicationShell';
import { IApplicationShell } from '../../../platform/common/application/types';
import { PythonExecutionFactory } from '../../../platform/common/process/pythonExecutionFactory.node';
import { IPythonExecutionFactory, IPythonExecutionService } from '../../../platform/common/process/types.node';
import { Common, DataScience } from '../../../platform/common/utils/localize';
import { IInterpreterService } from '../../../platform/interpreter/contracts';
import { PythonEnvironment } from '../../../platform/pythonEnvironments/info';
import { ProductInstaller } from '../../../kernels/installer/productInstaller.node';
import { IInstaller, Product } from '../../../kernels/installer/types';
import { DataViewerDependencyService } from '../../../webviews/extension-side/dataviewer/dataViewerDependencyService.node';
import { Uri } from 'vscode';

suite('DataScience - DataViewerDependencyService (Node)', () => {
let dependencyService: DataViewerDependencyService;
let appShell: IApplicationShell;
let pythonExecFactory: IPythonExecutionFactory;
let installer: IInstaller;
let interpreter: PythonEnvironment;
let interpreterService: IInterpreterService;
let pythonExecService: IPythonExecutionService;
setup(async () => {
interpreter = {
displayName: '',
uri: Uri.file(path.join('users', 'python', 'bin', 'python.exe')),
sysPrefix: '',
sysVersion: '',
version: new SemVer('3.3.3')
};
pythonExecService = mock<IPythonExecutionService>();
installer = mock(ProductInstaller);
appShell = mock(ApplicationShell);
pythonExecFactory = mock(PythonExecutionFactory);
interpreterService = mock<IInterpreterService>();

dependencyService = new DataViewerDependencyService(
instance(appShell),
instance(installer),
instance(pythonExecFactory),
instance(interpreterService),
false
);

when(interpreterService.getActiveInterpreter()).thenResolve(interpreter);
when(interpreterService.getActiveInterpreter(anything())).thenResolve(interpreter);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(instance(pythonExecService) as any).then = undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(pythonExecService as any).then = undefined;
when(pythonExecFactory.createActivatedEnvironment(anything())).thenResolve(instance(pythonExecService));
});
test('All ok, if pandas is installed and version is > 1.20', async () => {
when(
pythonExecService.exec(deepEqual(['-c', 'import pandas;print(pandas.__version__)']), anything())
).thenResolve({ stdout: '0.30.0' });
await dependencyService.checkAndInstallMissingDependenciesOnEnvironment(interpreter);
});
test('Throw exception if pandas is installed and version is = 0.20', async () => {
when(
pythonExecService.exec(deepEqual(['-c', 'import pandas;print(pandas.__version__)']), anything())
).thenResolve({ stdout: '0.20.0' });

const promise = dependencyService.checkAndInstallMissingDependenciesOnEnvironment(interpreter);

await assert.isRejected(promise, DataScience.pandasTooOldForViewingFormat().format('0.20.'));
});
test('Throw exception if pandas is installed and version is < 0.20', async () => {
when(
pythonExecService.exec(deepEqual(['-c', 'import pandas;print(pandas.__version__)']), anything())
).thenResolve({ stdout: '0.10.0' });

const promise = dependencyService.checkAndInstallMissingDependenciesOnEnvironment(interpreter);

await assert.isRejected(promise, DataScience.pandasTooOldForViewingFormat().format('0.10.'));
});
test('Prompt to install pandas and install pandas', async () => {
when(
pythonExecService.exec(deepEqual(['-c', 'import pandas;print(pandas.__version__)']), anything())
).thenReject(new Error('Not Found'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
when(appShell.showErrorMessage(anything(), anything(), anything())).thenResolve(Common.install() as any);
when(installer.install(Product.pandas, interpreter, anything())).thenResolve();

await dependencyService.checkAndInstallMissingDependenciesOnEnvironment(interpreter);

verify(
appShell.showErrorMessage(
DataScience.pandasRequiredForViewing(),
deepEqual({ modal: true }),
Common.install()
)
).once();
verify(installer.install(Product.pandas, interpreter, anything())).once();
});
test('Prompt to install pandas and throw error if user does not install pandas', async () => {
when(
pythonExecService.exec(deepEqual(['-c', 'import pandas;print(pandas.__version__)']), anything())
).thenReject(new Error('Not Found'));
when(appShell.showErrorMessage(anything(), anything(), anything())).thenResolve();

const promise = dependencyService.checkAndInstallMissingDependenciesOnEnvironment(interpreter);

await assert.isRejected(promise, DataScience.pandasRequiredForViewing());
verify(
appShell.showErrorMessage(
DataScience.pandasRequiredForViewing(),
deepEqual({ modal: true }),
Common.install()
)
).once();
verify(installer.install(anything(), anything(), anything())).never();
});
});
Loading