Skip to content

Commit d3b8985

Browse files
author
Kartik Raj
authored
Refactor API code in a separate module (#21604)
For #20949 We plan to publish the "api" module as an npm package. Inspired from https://insiders.vscode.dev/github/microsoft/vscode-wasi/blob/main/wasm-wasi/src/api/main.ts#L408.
1 parent 049ca8b commit d3b8985

12 files changed

+172
-28
lines changed

src/client/api.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient
1010
import { LanguageClient } from 'vscode-languageclient/node';
1111
import { PYLANCE_NAME } from './activation/node/languageClientFactory';
1212
import { ILanguageServerOutputChannel } from './activation/types';
13-
import { IExtensionApi } from './apiTypes';
13+
import { PythonExtension } from './api/main';
1414
import { isTestExecution, PYTHON_LANGUAGE } from './common/constants';
1515
import { IConfigurationService, Resource } from './common/types';
1616
import { getDebugpyLauncherArgs, getDebugpyPackagePath } from './debugger/extension/adapter/remoteLaunchers';
@@ -29,22 +29,22 @@ export function buildApi(
2929
serviceManager: IServiceManager,
3030
serviceContainer: IServiceContainer,
3131
discoveryApi: IDiscoveryAPI,
32-
): IExtensionApi {
32+
): PythonExtension {
3333
const configurationService = serviceContainer.get<IConfigurationService>(IConfigurationService);
3434
const interpreterService = serviceContainer.get<IInterpreterService>(IInterpreterService);
3535
serviceManager.addSingleton<JupyterExtensionIntegration>(JupyterExtensionIntegration, JupyterExtensionIntegration);
3636
const jupyterIntegration = serviceContainer.get<JupyterExtensionIntegration>(JupyterExtensionIntegration);
3737
const outputChannel = serviceContainer.get<ILanguageServerOutputChannel>(ILanguageServerOutputChannel);
3838

39-
const api: IExtensionApi & {
39+
const api: PythonExtension & {
4040
/**
4141
* @deprecated Temporarily exposed for Pylance until we expose this API generally. Will be removed in an
4242
* iteration or two.
4343
*/
4444
pylance: ApiForPylance;
4545
} & {
4646
/**
47-
* @deprecated Use IExtensionApi.environments API instead.
47+
* @deprecated Use PythonExtension.environments API instead.
4848
*
4949
* Return internal settings within the extension which are stored in VSCode storage
5050
*/

src/client/apiTypes.ts renamed to src/client/api/main.ts

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
import { CancellationToken, Event, Uri, WorkspaceFolder } from 'vscode';
5-
import { IDataViewerDataProvider, IJupyterUriProvider } from './jupyter/types';
4+
import { CancellationToken, Event, Uri, WorkspaceFolder, QuickPickItem, extensions } from 'vscode';
65

76
/*
87
* Do not introduce any breaking changes to this API.
98
* This is the public API for other extensions to interact with this extension.
109
*/
11-
12-
export interface IExtensionApi {
10+
export interface PythonExtension {
1311
/**
1412
* Promise indicating whether all parts of the extension have completed loading or not.
1513
* @type {Promise<void>}
16-
* @memberof IExtensionApi
1714
*/
1815
ready: Promise<void>;
1916
jupyter: {
@@ -128,6 +125,47 @@ export interface IExtensionApi {
128125
};
129126
}
130127

128+
interface IJupyterServerUri {
129+
baseUrl: string;
130+
token: string;
131+
132+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
133+
authorizationHeader: any; // JSON object for authorization header.
134+
expiration?: Date; // Date/time when header expires and should be refreshed.
135+
displayName: string;
136+
}
137+
138+
type JupyterServerUriHandle = string;
139+
140+
export interface IJupyterUriProvider {
141+
readonly id: string; // Should be a unique string (like a guid)
142+
getQuickPickEntryItems(): QuickPickItem[];
143+
handleQuickPick(item: QuickPickItem, backEnabled: boolean): Promise<JupyterServerUriHandle | 'back' | undefined>;
144+
getServerUri(handle: JupyterServerUriHandle): Promise<IJupyterServerUri>;
145+
}
146+
147+
interface IDataFrameInfo {
148+
columns?: { key: string; type: ColumnType }[];
149+
indexColumn?: string;
150+
rowCount?: number;
151+
}
152+
153+
export interface IDataViewerDataProvider {
154+
dispose(): void;
155+
getDataFrameInfo(): Promise<IDataFrameInfo>;
156+
getAllRows(): Promise<IRowsResponse>;
157+
getRows(start: number, end: number): Promise<IRowsResponse>;
158+
}
159+
160+
enum ColumnType {
161+
String = 'string',
162+
Number = 'number',
163+
Bool = 'bool',
164+
}
165+
166+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
167+
type IRowsResponse = any[];
168+
131169
export type RefreshOptions = {
132170
/**
133171
* When `true`, force trigger a refresh regardless of whether a refresh was already triggered. Note this can be expensive so
@@ -349,3 +387,20 @@ export type EnvironmentVariablesChangeEvent = {
349387
*/
350388
readonly env: EnvironmentVariables;
351389
};
390+
391+
export const PVSC_EXTENSION_ID = 'ms-python.python';
392+
393+
// eslint-disable-next-line @typescript-eslint/no-namespace
394+
export namespace PythonExtension {
395+
export async function api(): Promise<PythonExtension> {
396+
const extension = extensions.getExtension(PVSC_EXTENSION_ID);
397+
if (extension === undefined) {
398+
throw new Error(`Python extension is not installed or is disabled`);
399+
}
400+
if (!extension.isActive) {
401+
await extension.activate();
402+
}
403+
const pythonApi: PythonExtension = extension.exports;
404+
return pythonApi;
405+
}
406+
}

src/client/api/package-lock.json

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client/api/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "@vscode/python-extension",
3+
"description": "VSCode Python extension's public API",
4+
"version": "1.0.0",
5+
"publisher": "ms-python",
6+
"author": {
7+
"name": "Microsoft Corporation"
8+
},
9+
"types": "./index.d.ts",
10+
"license": "MIT",
11+
"homepage": "https://github.com/Microsoft/vscode-python",
12+
"repository": {
13+
"type": "git",
14+
"url": "https://github.com/Microsoft/vscode-python"
15+
},
16+
"bugs": {
17+
"url": "https://github.com/Microsoft/vscode-python/issues"
18+
},
19+
"dependencies": {
20+
"@types/vscode": "^1.78.0"
21+
},
22+
"devDependencies": {
23+
"@types/node": "^16.11.7",
24+
"typescript": "^4.7.2"
25+
}
26+
}

src/client/deprecatedProposedApiTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { Uri, Event } from 'vscode';
55
import { PythonEnvKind, EnvPathType } from './pythonEnvironments/base/info';
66
import { ProgressNotificationEvent, GetRefreshEnvironmentsOptions } from './pythonEnvironments/base/locator';
7-
import { Resource } from './apiTypes';
7+
import { Resource } from './api/main';
88

99
export interface EnvironmentDetailsOptions {
1010
useCache: boolean;

src/client/environmentApi.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ import {
2626
EnvironmentTools,
2727
EnvironmentType,
2828
EnvironmentVariablesChangeEvent,
29-
IExtensionApi,
29+
PythonExtension,
3030
RefreshOptions,
3131
ResolvedEnvironment,
3232
Resource,
33-
} from './apiTypes';
33+
} from './api/main';
3434
import { buildEnvironmentCreationApi } from './pythonEnvironments/creation/createEnvApi';
3535

3636
type ActiveEnvironmentChangeEvent = {
@@ -114,7 +114,7 @@ function filterUsingVSCodeContext(e: PythonEnvInfo) {
114114
export function buildEnvironmentApi(
115115
discoveryApi: IDiscoveryAPI,
116116
serviceContainer: IServiceContainer,
117-
): IExtensionApi['environments'] {
117+
): PythonExtension['environments'] {
118118
const interpreterPathService = serviceContainer.get<IInterpreterPathService>(IInterpreterPathService);
119119
const configService = serviceContainer.get<IConfigurationService>(IConfigurationService);
120120
const disposables = serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
@@ -180,7 +180,7 @@ export function buildEnvironmentApi(
180180
onEnvironmentVariablesChanged,
181181
);
182182

183-
const environmentApi: IExtensionApi['environments'] = {
183+
const environmentApi: PythonExtension['environments'] = {
184184
getEnvironmentVariables: (resource?: Resource) => {
185185
sendApiTelemetry('getEnvironmentVariables');
186186
resource = resource && 'uri' in resource ? resource.uri : resource;

src/client/extension.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { sendErrorTelemetry, sendStartupTelemetry } from './startupTelemetry';
4141
import { IStartupDurations } from './types';
4242
import { runAfterActivation } from './common/utils/runAfterActivation';
4343
import { IInterpreterService } from './interpreter/contracts';
44-
import { IExtensionApi } from './apiTypes';
44+
import { PythonExtension } from './api/main';
4545
import { WorkspaceService } from './common/application/workspace';
4646
import { disposeAll } from './common/utils/resourceLifecycle';
4747
import { ProposedExtensionAPI } from './proposedApiTypes';
@@ -58,8 +58,8 @@ let activatedServiceContainer: IServiceContainer | undefined;
5858
/////////////////////////////
5959
// public functions
6060

61-
export async function activate(context: IExtensionContext): Promise<IExtensionApi> {
62-
let api: IExtensionApi;
61+
export async function activate(context: IExtensionContext): Promise<PythonExtension> {
62+
let api: PythonExtension;
6363
let ready: Promise<void>;
6464
let serviceContainer: IServiceContainer;
6565
try {
@@ -103,7 +103,7 @@ async function activateUnsafe(
103103
context: IExtensionContext,
104104
startupStopWatch: StopWatch,
105105
startupDurations: IStartupDurations,
106-
): Promise<[IExtensionApi & ProposedExtensionAPI, Promise<void>, IServiceContainer]> {
106+
): Promise<[PythonExtension & ProposedExtensionAPI, Promise<void>, IServiceContainer]> {
107107
// Add anything that we got from initializing logs to dispose.
108108
context.subscriptions.push(...logDispose);
109109

src/client/pythonEnvironments/creation/proposed.createEnvApis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License
33

44
import { Event, Disposable, WorkspaceFolder } from 'vscode';
5-
import { EnvironmentTools } from '../../apiTypes';
5+
import { EnvironmentTools } from '../../api/main';
66

77
export type CreateEnvironmentUserActions = 'Back' | 'Cancel';
88
export type EnvironmentProviderId = string;

src/test/api.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
// Licensed under the MIT License.
33

44
import { expect } from 'chai';
5-
import { IExtensionApi } from '../client/apiTypes';
5+
import { PythonExtension } from '../client/api/main';
66
import { ProposedExtensionAPI } from '../client/proposedApiTypes';
77
import { initialize } from './initialize';
88

99
suite('Python API tests', () => {
10-
let api: IExtensionApi & ProposedExtensionAPI;
10+
let api: PythonExtension & ProposedExtensionAPI;
1111
suiteSetup(async () => {
1212
api = await initialize();
1313
});

src/test/common.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as glob from 'glob';
1010
import * as path from 'path';
1111
import { coerce, SemVer } from 'semver';
1212
import { ConfigurationTarget, Event, TextDocument, Uri } from 'vscode';
13-
import type { IExtensionApi } from '../client/apiTypes';
13+
import type { PythonExtension } from '../client/api/main';
1414
import { IProcessService } from '../client/common/process/types';
1515
import { IDisposable } from '../client/common/types';
1616
import { IServiceContainer, IServiceManager } from '../client/ioc/types';
@@ -438,7 +438,7 @@ export async function isPythonVersion(...versions: string[]): Promise<boolean> {
438438
}
439439
}
440440

441-
export interface IExtensionTestApi extends IExtensionApi, ProposedExtensionAPI {
441+
export interface IExtensionTestApi extends PythonExtension, ProposedExtensionAPI {
442442
serviceContainer: IServiceContainer;
443443
serviceManager: IServiceManager;
444444
}

src/test/environmentApi.unit.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ import {
3636
ActiveEnvironmentPathChangeEvent,
3737
EnvironmentVariablesChangeEvent,
3838
EnvironmentsChangeEvent,
39-
IExtensionApi,
40-
} from '../client/apiTypes';
39+
PythonExtension,
40+
} from '../client/api/main';
4141

4242
suite('Python Environment API', () => {
4343
const workspacePath = 'path/to/workspace';
@@ -57,7 +57,7 @@ suite('Python Environment API', () => {
5757
let onDidChangeEnvironments: EventEmitter<PythonEnvCollectionChangedEvent>;
5858
let onDidChangeEnvironmentVariables: EventEmitter<Uri | undefined>;
5959

60-
let environmentApi: IExtensionApi['environments'];
60+
let environmentApi: PythonExtension['environments'];
6161

6262
setup(() => {
6363
serviceContainer = typemoq.Mock.ofType<IServiceContainer>();

src/test/initialize.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from 'path';
22
import * as vscode from 'vscode';
3-
import type { IExtensionApi } from '../client/apiTypes';
3+
import type { PythonExtension } from '../client/api/main';
44
import {
55
clearPythonPathInWorkspaceFolder,
66
IExtensionTestApi,
@@ -42,7 +42,7 @@ export async function initialize(): Promise<IExtensionTestApi> {
4242
return (api as any) as IExtensionTestApi;
4343
}
4444
export async function activateExtension() {
45-
const extension = vscode.extensions.getExtension<IExtensionApi>(PVSC_EXTENSION_ID_FOR_TESTS)!;
45+
const extension = vscode.extensions.getExtension<PythonExtension>(PVSC_EXTENSION_ID_FOR_TESTS)!;
4646
const api = await extension.activate();
4747
// Wait until its ready to use.
4848
await api.ready;

0 commit comments

Comments
 (0)