forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use env extension when available (#24564)
- Loading branch information
1 parent
63c3780
commit e789348
Showing
36 changed files
with
2,272 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
import { Terminal, Uri } from 'vscode'; | ||
import { getExtension } from '../common/vscodeApis/extensionsApi'; | ||
import { | ||
GetEnvironmentScope, | ||
PythonBackgroundRunOptions, | ||
PythonEnvironment, | ||
PythonEnvironmentApi, | ||
PythonProcess, | ||
RefreshEnvironmentsScope, | ||
} from './types'; | ||
import { executeCommand } from '../common/vscodeApis/commandApis'; | ||
|
||
export const ENVS_EXTENSION_ID = 'ms-python.vscode-python-envs'; | ||
|
||
let _useExt: boolean | undefined; | ||
export function useEnvExtension(): boolean { | ||
if (_useExt !== undefined) { | ||
return _useExt; | ||
} | ||
_useExt = !!getExtension(ENVS_EXTENSION_ID); | ||
return _useExt; | ||
} | ||
|
||
let _extApi: PythonEnvironmentApi | undefined; | ||
export async function getEnvExtApi(): Promise<PythonEnvironmentApi> { | ||
if (_extApi) { | ||
return _extApi; | ||
} | ||
const extension = getExtension(ENVS_EXTENSION_ID); | ||
if (!extension) { | ||
throw new Error('Python Environments extension not found.'); | ||
} | ||
if (extension?.isActive) { | ||
_extApi = extension.exports as PythonEnvironmentApi; | ||
return _extApi; | ||
} | ||
|
||
await extension.activate(); | ||
|
||
_extApi = extension.exports as PythonEnvironmentApi; | ||
return _extApi; | ||
} | ||
|
||
export async function runInBackground( | ||
environment: PythonEnvironment, | ||
options: PythonBackgroundRunOptions, | ||
): Promise<PythonProcess> { | ||
const envExtApi = await getEnvExtApi(); | ||
return envExtApi.runInBackground(environment, options); | ||
} | ||
|
||
export async function getEnvironment(scope: GetEnvironmentScope): Promise<PythonEnvironment | undefined> { | ||
const envExtApi = await getEnvExtApi(); | ||
return envExtApi.getEnvironment(scope); | ||
} | ||
|
||
export async function refreshEnvironments(scope: RefreshEnvironmentsScope): Promise<void> { | ||
const envExtApi = await getEnvExtApi(); | ||
return envExtApi.refreshEnvironments(scope); | ||
} | ||
|
||
export async function runInTerminal( | ||
resource: Uri | undefined, | ||
args?: string[], | ||
cwd?: string | Uri, | ||
show?: boolean, | ||
): Promise<Terminal> { | ||
const envExtApi = await getEnvExtApi(); | ||
const env = await getEnvironment(resource); | ||
const project = resource ? envExtApi.getPythonProject(resource) : undefined; | ||
if (env && resource) { | ||
return envExtApi.runInTerminal(env, { | ||
cwd: cwd ?? project?.uri ?? process.cwd(), | ||
args, | ||
show, | ||
}); | ||
} | ||
throw new Error('Invalid arguments to run in terminal'); | ||
} | ||
|
||
export async function runInDedicatedTerminal( | ||
resource: Uri | undefined, | ||
args?: string[], | ||
cwd?: string | Uri, | ||
show?: boolean, | ||
): Promise<Terminal> { | ||
const envExtApi = await getEnvExtApi(); | ||
const env = await getEnvironment(resource); | ||
const project = resource ? envExtApi.getPythonProject(resource) : undefined; | ||
if (env) { | ||
return envExtApi.runInDedicatedTerminal(resource ?? 'global', env, { | ||
cwd: cwd ?? project?.uri ?? process.cwd(), | ||
args, | ||
show, | ||
}); | ||
} | ||
throw new Error('Invalid arguments to run in dedicated terminal'); | ||
} | ||
|
||
export async function clearCache(): Promise<void> { | ||
const envExtApi = await getEnvExtApi(); | ||
if (envExtApi) { | ||
await executeCommand('python-envs.clearCache'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
import { Terminal, Uri } from 'vscode'; | ||
import { getEnvExtApi, getEnvironment } from './api.internal'; | ||
import { EnvironmentType, PythonEnvironment as PythonEnvironmentLegacy } from '../pythonEnvironments/info'; | ||
import { PythonEnvironment, PythonTerminalOptions } from './types'; | ||
import { Architecture } from '../common/utils/platform'; | ||
import { parseVersion } from '../pythonEnvironments/base/info/pythonVersion'; | ||
import { PythonEnvType } from '../pythonEnvironments/base/info'; | ||
import { traceError, traceInfo } from '../logging'; | ||
import { reportActiveInterpreterChanged } from '../environmentApi'; | ||
import { getWorkspaceFolder } from '../common/vscodeApis/workspaceApis'; | ||
|
||
function toEnvironmentType(pythonEnv: PythonEnvironment): EnvironmentType { | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('system')) { | ||
return EnvironmentType.System; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('venv')) { | ||
return EnvironmentType.Venv; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('virtualenv')) { | ||
return EnvironmentType.VirtualEnv; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('conda')) { | ||
return EnvironmentType.Conda; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('pipenv')) { | ||
return EnvironmentType.Pipenv; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('poetry')) { | ||
return EnvironmentType.Poetry; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('pyenv')) { | ||
return EnvironmentType.Pyenv; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('hatch')) { | ||
return EnvironmentType.Hatch; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('pixi')) { | ||
return EnvironmentType.Pixi; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('virtualenvwrapper')) { | ||
return EnvironmentType.VirtualEnvWrapper; | ||
} | ||
if (pythonEnv.envId.managerId.toLowerCase().endsWith('activestate')) { | ||
return EnvironmentType.ActiveState; | ||
} | ||
return EnvironmentType.Unknown; | ||
} | ||
|
||
function getEnvType(kind: EnvironmentType): PythonEnvType | undefined { | ||
switch (kind) { | ||
case EnvironmentType.Pipenv: | ||
case EnvironmentType.VirtualEnv: | ||
case EnvironmentType.Pyenv: | ||
case EnvironmentType.Venv: | ||
case EnvironmentType.Poetry: | ||
case EnvironmentType.Hatch: | ||
case EnvironmentType.Pixi: | ||
case EnvironmentType.VirtualEnvWrapper: | ||
case EnvironmentType.ActiveState: | ||
return PythonEnvType.Virtual; | ||
|
||
case EnvironmentType.Conda: | ||
return PythonEnvType.Conda; | ||
|
||
case EnvironmentType.MicrosoftStore: | ||
case EnvironmentType.Global: | ||
case EnvironmentType.System: | ||
default: | ||
return undefined; | ||
} | ||
} | ||
|
||
function toLegacyType(env: PythonEnvironment): PythonEnvironmentLegacy { | ||
const ver = parseVersion(env.version); | ||
const envType = toEnvironmentType(env); | ||
return { | ||
id: env.environmentPath.fsPath, | ||
displayName: env.displayName, | ||
detailedDisplayName: env.name, | ||
envType, | ||
envPath: env.sysPrefix, | ||
type: getEnvType(envType), | ||
path: env.environmentPath.fsPath, | ||
version: { | ||
raw: env.version, | ||
major: ver.major, | ||
minor: ver.minor, | ||
patch: ver.micro, | ||
build: [], | ||
prerelease: [], | ||
}, | ||
sysVersion: env.version, | ||
architecture: Architecture.x64, | ||
sysPrefix: env.sysPrefix, | ||
}; | ||
} | ||
|
||
const previousEnvMap = new Map<string, PythonEnvironment | undefined>(); | ||
export async function getActiveInterpreterLegacy(resource?: Uri): Promise<PythonEnvironmentLegacy | undefined> { | ||
const api = await getEnvExtApi(); | ||
const uri = resource ? api.getPythonProject(resource)?.uri : undefined; | ||
|
||
const pythonEnv = await getEnvironment(resource); | ||
const oldEnv = previousEnvMap.get(uri?.fsPath || ''); | ||
const newEnv = pythonEnv ? toLegacyType(pythonEnv) : undefined; | ||
if (newEnv && oldEnv?.envId.id !== pythonEnv?.envId.id) { | ||
reportActiveInterpreterChanged({ | ||
resource: getWorkspaceFolder(resource), | ||
path: newEnv.path, | ||
}); | ||
} | ||
return pythonEnv ? toLegacyType(pythonEnv) : undefined; | ||
} | ||
|
||
export async function ensureEnvironmentContainsPythonLegacy(pythonPath: string): Promise<void> { | ||
const api = await getEnvExtApi(); | ||
const pythonEnv = await api.resolveEnvironment(Uri.file(pythonPath)); | ||
if (!pythonEnv) { | ||
traceError(`EnvExt: Failed to resolve environment for ${pythonPath}`); | ||
return; | ||
} | ||
|
||
const envType = toEnvironmentType(pythonEnv); | ||
if (envType === EnvironmentType.Conda) { | ||
const packages = await api.getPackages(pythonEnv); | ||
if (packages && packages.length > 0 && packages.some((pkg) => pkg.name.toLowerCase() === 'python')) { | ||
return; | ||
} | ||
traceInfo(`EnvExt: Python not found in ${envType} environment ${pythonPath}`); | ||
traceInfo(`EnvExt: Installing Python in ${envType} environment ${pythonPath}`); | ||
await api.installPackages(pythonEnv, ['python']); | ||
} | ||
} | ||
|
||
export async function setInterpreterLegacy(pythonPath: string, uri: Uri | undefined): Promise<void> { | ||
const api = await getEnvExtApi(); | ||
const pythonEnv = await api.resolveEnvironment(Uri.file(pythonPath)); | ||
if (!pythonEnv) { | ||
traceError(`EnvExt: Failed to resolve environment for ${pythonPath}`); | ||
return; | ||
} | ||
await api.setEnvironment(uri, pythonEnv); | ||
} | ||
|
||
export async function resetInterpreterLegacy(uri: Uri | undefined): Promise<void> { | ||
const api = await getEnvExtApi(); | ||
await api.setEnvironment(uri, undefined); | ||
} | ||
|
||
export async function ensureTerminalLegacy( | ||
resource: Uri | undefined, | ||
options?: PythonTerminalOptions, | ||
): Promise<Terminal> { | ||
const api = await getEnvExtApi(); | ||
const pythonEnv = await api.getEnvironment(resource); | ||
const project = resource ? api.getPythonProject(resource) : undefined; | ||
|
||
if (pythonEnv && project) { | ||
const fixedOptions = options ? { ...options } : { cwd: project.uri }; | ||
const terminal = await api.createTerminal(pythonEnv, fixedOptions); | ||
return terminal; | ||
} | ||
throw new Error('Invalid arguments to create terminal'); | ||
} |
Oops, something went wrong.