Skip to content

Commit 5d749eb

Browse files
committed
tests and split legacy python from python
1 parent 22c77f0 commit 5d749eb

File tree

10 files changed

+1627
-174
lines changed

10 files changed

+1627
-174
lines changed

src/extension/common/application/commands/reportIssueCommand.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { getActiveEnvironmentPath, resolveEnvironment } from '../../python';
1010
import { EXTENSION_ROOT_DIR } from '../../constants';
1111
import { sendTelemetryEvent } from '../../../telemetry';
1212
import { EventName } from '../../../telemetry/constants';
13+
import { PythonEnvironment } from '../../../envExtApi';
1314

1415
/**
1516
* Allows the user to report an issue related to the Python Debugger extension using our template.
@@ -19,11 +20,17 @@ export async function openReportIssue(): Promise<void> {
1920
const userDataTemplatePath = path.join(EXTENSION_ROOT_DIR, 'resources', 'report_issue_user_data_template.md');
2021
const template = await fs.readFile(templatePath, 'utf8');
2122
const userTemplate = await fs.readFile(userDataTemplatePath, 'utf8');
23+
// get active environment and resolve it
2224
const interpreterPath = await getActiveEnvironmentPath();
23-
const interpreter = await resolveEnvironment(interpreterPath);
24-
const virtualEnvKind = interpreter?.envId.managerId ?? 'N/A';
25-
25+
let interpreter: PythonEnvironment | undefined = undefined;
26+
if (interpreterPath && 'environmentPath' in interpreterPath) {
27+
interpreter = interpreterPath ? await resolveEnvironment(interpreterPath.environmentPath.fsPath) : undefined;
28+
} else if (interpreterPath && 'path' in interpreterPath) {
29+
interpreter = interpreterPath ? await resolveEnvironment(interpreterPath.path) : undefined;
30+
}
31+
const virtualEnvKind = interpreter && interpreter.envId ? interpreter.envId.managerId : 'Unknown';
2632
const pythonVersion = interpreter?.version ?? 'unknown';
33+
2734
await executeCommand('workbench.action.openIssueReporter', {
2835
extensionId: 'ms-python.debugpy',
2936
issueBody: template,
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
/* eslint-disable @typescript-eslint/naming-convention */
5+
import {
6+
ActiveEnvironmentPathChangeEvent,
7+
Environment,
8+
EnvironmentPath,
9+
EnvironmentVariables,
10+
PythonExtension,
11+
ResolvedEnvironment,
12+
Resource,
13+
} from '@vscode/python-extension';
14+
import { EventEmitter, extensions, Uri, Disposable, Extension } from 'vscode';
15+
import { createDeferred } from './utils/async';
16+
import { traceError, traceLog } from './log/logging';
17+
18+
/**
19+
* Interface for the Python extension API.
20+
*/
21+
interface LegacyIExtensionApi {
22+
ready: Promise<void>;
23+
settings: {
24+
getExecutionDetails(resource?: Resource): { execCommand: string[] | undefined };
25+
};
26+
}
27+
28+
/**
29+
* Details about a Python interpreter.
30+
*/
31+
export interface LegacyIInterpreterDetails {
32+
/** Array of path components to the Python executable */
33+
path?: string[];
34+
/** The workspace resource associated with this interpreter */
35+
resource?: Uri;
36+
}
37+
38+
// /** Event emitter for Python interpreter changes */
39+
// const legacyOnDidChangePythonInterpreterEvent = new EventEmitter<LegacyIInterpreterDetails>();
40+
41+
// /** Event that fires when the active Python interpreter changes */
42+
// export const legacyOnDidChangePythonInterpreter: Event<LegacyIInterpreterDetails> =
43+
// legacyOnDidChangePythonInterpreterEvent.event;
44+
/**
45+
* Activates the Python extension and ensures it's ready for use.
46+
* @returns The activated Python extension instance
47+
*/
48+
async function legacyActivateExtension(): Promise<Extension<any> | undefined> {
49+
console.log('Activating Python extension...');
50+
activateEnvsExtension();
51+
const extension = extensions.getExtension('ms-python.python');
52+
if (extension) {
53+
if (!extension.isActive) {
54+
await extension.activate();
55+
}
56+
}
57+
console.log('Python extension activated.');
58+
return extension;
59+
}
60+
/**
61+
* Activates the Python environments extension.
62+
* @returns The activated Python environments extension instance
63+
*/
64+
async function activateEnvsExtension(): Promise<Extension<any> | undefined> {
65+
const extension = extensions.getExtension('ms-python.vscode-python-envs');
66+
if (extension) {
67+
if (!extension.isActive) {
68+
await extension.activate();
69+
}
70+
}
71+
return extension;
72+
}
73+
74+
/**
75+
* Gets the Python extension's API interface.
76+
* @returns The Python extension API or undefined if not available
77+
*/
78+
async function legacyGetPythonExtensionAPI(): Promise<LegacyIExtensionApi | undefined> {
79+
const extension = await legacyActivateExtension();
80+
return extension?.exports as LegacyIExtensionApi;
81+
}
82+
83+
/**
84+
* Gets the Python extension's environment API.
85+
* @returns The Python extension environment API
86+
*/
87+
async function legacyGetPythonExtensionEnviromentAPI(): Promise<PythonExtension> {
88+
// Load the Python extension API
89+
await legacyActivateExtension();
90+
return await PythonExtension.api();
91+
}
92+
93+
/**
94+
* Initializes Python integration by setting up event listeners and getting initial interpreter details.
95+
* @param disposables Array to store disposable resources for cleanup
96+
*/
97+
export async function legacyInitializePython(
98+
disposables: Disposable[],
99+
onDidChangePythonInterpreterEvent: EventEmitter<LegacyIInterpreterDetails>,
100+
): Promise<void> {
101+
try {
102+
const api = await legacyGetPythonExtensionEnviromentAPI();
103+
104+
if (api) {
105+
disposables.push(
106+
// This event is triggered when the active environment setting changes.
107+
api.environments.onDidChangeActiveEnvironmentPath((e: ActiveEnvironmentPathChangeEvent) => {
108+
let resourceUri: Uri | undefined;
109+
if (e.resource instanceof Uri) {
110+
resourceUri = e.resource;
111+
}
112+
if (e.resource && 'uri' in e.resource) {
113+
// WorkspaceFolder type
114+
resourceUri = e.resource.uri;
115+
}
116+
onDidChangePythonInterpreterEvent.fire({ path: [e.path], resource: resourceUri });
117+
}),
118+
);
119+
120+
traceLog('Waiting for interpreter from python extension.');
121+
onDidChangePythonInterpreterEvent.fire(await legacyGetInterpreterDetails());
122+
}
123+
} catch (error) {
124+
traceError('Error initializing python: ', error);
125+
}
126+
}
127+
128+
/**
129+
* Returns all the details needed to execute code within the selected environment,
130+
* corresponding to the specified resource taking into account any workspace-specific settings
131+
* for the workspace to which this resource belongs.
132+
* @param resource Optional workspace resource to get settings for
133+
* @returns Array of command components or undefined if not available
134+
*/
135+
export async function legacyGetSettingsPythonPath(resource?: Uri): Promise<string[] | undefined> {
136+
const api = await legacyGetPythonExtensionAPI();
137+
return api?.settings.getExecutionDetails(resource).execCommand;
138+
}
139+
140+
/**
141+
* Returns the environment variables used by the extension for a resource, which includes the custom
142+
* variables configured by user in `.env` files.
143+
* @param resource Optional workspace resource to get environment variables for
144+
* @returns Environment variables object
145+
*/
146+
export async function legacyGetEnvironmentVariables(resource?: Resource): Promise<EnvironmentVariables> {
147+
const api = await legacyGetPythonExtensionEnviromentAPI();
148+
return Promise.resolve(api.environments.getEnvironmentVariables(resource));
149+
}
150+
151+
/**
152+
* Returns details for the given environment, or `undefined` if the env is invalid.
153+
* @param env Environment to resolve (can be Environment object, path, or string)
154+
* @returns Resolved environment details
155+
*/
156+
export async function legacyResolveEnvironment(
157+
env: Environment | EnvironmentPath | string,
158+
): Promise<ResolvedEnvironment | undefined> {
159+
const api = await legacyGetPythonExtensionEnviromentAPI();
160+
return api.environments.resolveEnvironment(env);
161+
}
162+
163+
/**
164+
* Returns the environment configured by user in settings. Note that this can be an invalid environment, use
165+
* resolve the environment to get full details.
166+
* @param resource Optional workspace resource to get active environment for
167+
* @returns Path to the active environment
168+
*/
169+
export async function legacyGetActiveEnvironmentPath(resource?: Resource): Promise<EnvironmentPath> {
170+
const api = await legacyGetPythonExtensionEnviromentAPI();
171+
return api.environments.getActiveEnvironmentPath(resource);
172+
}
173+
174+
/**
175+
* Gets detailed information about the active Python interpreter.
176+
* @param resource Optional workspace resource to get interpreter details for
177+
* @returns Interpreter details including path and resource information
178+
*/
179+
export async function legacyGetInterpreterDetails(resource?: Uri): Promise<LegacyIInterpreterDetails> {
180+
const api = await legacyGetPythonExtensionEnviromentAPI();
181+
const environment = await api.environments.resolveEnvironment(api.environments.getActiveEnvironmentPath(resource));
182+
if (environment?.executable.uri) {
183+
return { path: [environment?.executable.uri.fsPath], resource };
184+
}
185+
return { path: undefined, resource };
186+
}
187+
188+
/**
189+
* Checks if any Python interpreters are available in the system.
190+
* @returns True if interpreters are found, false otherwise
191+
*/
192+
export async function legacyHasInterpreters(): Promise<boolean> {
193+
const api = await legacyGetPythonExtensionEnviromentAPI();
194+
const onAddedToCollection = createDeferred();
195+
api.environments.onDidChangeEnvironments(async () => {
196+
if (api.environments.known) {
197+
onAddedToCollection.resolve();
198+
}
199+
});
200+
const initialEnvs = api.environments.known;
201+
if (initialEnvs.length > 0) {
202+
return true;
203+
}
204+
// Initiates a refresh of Python environments within the specified scope.
205+
await Promise.race([onAddedToCollection.promise, api?.environments.refreshEnvironments()]);
206+
207+
return api.environments.known.length > 0;
208+
}
209+
210+
/**
211+
* Gets environments known to the extension at the time of fetching the property. Note this may not
212+
* contain all environments in the system as a refresh might be going on.
213+
* @returns Array of known Python environments
214+
*/
215+
export async function legacyGetInterpreters(): Promise<readonly Environment[]> {
216+
const api = await legacyGetPythonExtensionEnviromentAPI();
217+
return api.environments.known || [];
218+
}

0 commit comments

Comments
 (0)