|
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT License. |
| 3 | + |
| 4 | +import { Terminal, Uri } from 'vscode'; |
| 5 | +import { getEnvExtApi, getEnvironment } from './api.internal'; |
| 6 | +import { EnvironmentType, PythonEnvironment as PythonEnvironmentLegacy } from '../pythonEnvironments/info'; |
| 7 | +import { PythonEnvironment, PythonTerminalOptions } from './types'; |
| 8 | +import { Architecture } from '../common/utils/platform'; |
| 9 | +import { parseVersion } from '../pythonEnvironments/base/info/pythonVersion'; |
| 10 | +import { PythonEnvType } from '../pythonEnvironments/base/info'; |
| 11 | +import { traceError, traceInfo } from '../logging'; |
| 12 | +import { reportActiveInterpreterChanged } from '../environmentApi'; |
| 13 | +import { getWorkspaceFolder } from '../common/vscodeApis/workspaceApis'; |
| 14 | + |
| 15 | +function toEnvironmentType(pythonEnv: PythonEnvironment): EnvironmentType { |
| 16 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('system')) { |
| 17 | + return EnvironmentType.System; |
| 18 | + } |
| 19 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('venv')) { |
| 20 | + return EnvironmentType.Venv; |
| 21 | + } |
| 22 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('virtualenv')) { |
| 23 | + return EnvironmentType.VirtualEnv; |
| 24 | + } |
| 25 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('conda')) { |
| 26 | + return EnvironmentType.Conda; |
| 27 | + } |
| 28 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('pipenv')) { |
| 29 | + return EnvironmentType.Pipenv; |
| 30 | + } |
| 31 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('poetry')) { |
| 32 | + return EnvironmentType.Poetry; |
| 33 | + } |
| 34 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('pyenv')) { |
| 35 | + return EnvironmentType.Pyenv; |
| 36 | + } |
| 37 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('hatch')) { |
| 38 | + return EnvironmentType.Hatch; |
| 39 | + } |
| 40 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('pixi')) { |
| 41 | + return EnvironmentType.Pixi; |
| 42 | + } |
| 43 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('virtualenvwrapper')) { |
| 44 | + return EnvironmentType.VirtualEnvWrapper; |
| 45 | + } |
| 46 | + if (pythonEnv.envId.managerId.toLowerCase().endsWith('activestate')) { |
| 47 | + return EnvironmentType.ActiveState; |
| 48 | + } |
| 49 | + return EnvironmentType.Unknown; |
| 50 | +} |
| 51 | + |
| 52 | +function getEnvType(kind: EnvironmentType): PythonEnvType | undefined { |
| 53 | + switch (kind) { |
| 54 | + case EnvironmentType.Pipenv: |
| 55 | + case EnvironmentType.VirtualEnv: |
| 56 | + case EnvironmentType.Pyenv: |
| 57 | + case EnvironmentType.Venv: |
| 58 | + case EnvironmentType.Poetry: |
| 59 | + case EnvironmentType.Hatch: |
| 60 | + case EnvironmentType.Pixi: |
| 61 | + case EnvironmentType.VirtualEnvWrapper: |
| 62 | + case EnvironmentType.ActiveState: |
| 63 | + return PythonEnvType.Virtual; |
| 64 | + |
| 65 | + case EnvironmentType.Conda: |
| 66 | + return PythonEnvType.Conda; |
| 67 | + |
| 68 | + case EnvironmentType.MicrosoftStore: |
| 69 | + case EnvironmentType.Global: |
| 70 | + case EnvironmentType.System: |
| 71 | + default: |
| 72 | + return undefined; |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +function toLegacyType(env: PythonEnvironment): PythonEnvironmentLegacy { |
| 77 | + const ver = parseVersion(env.version); |
| 78 | + const envType = toEnvironmentType(env); |
| 79 | + return { |
| 80 | + id: env.environmentPath.fsPath, |
| 81 | + displayName: env.displayName, |
| 82 | + detailedDisplayName: env.name, |
| 83 | + envType, |
| 84 | + envPath: env.sysPrefix, |
| 85 | + type: getEnvType(envType), |
| 86 | + path: env.environmentPath.fsPath, |
| 87 | + version: { |
| 88 | + raw: env.version, |
| 89 | + major: ver.major, |
| 90 | + minor: ver.minor, |
| 91 | + patch: ver.micro, |
| 92 | + build: [], |
| 93 | + prerelease: [], |
| 94 | + }, |
| 95 | + sysVersion: env.version, |
| 96 | + architecture: Architecture.x64, |
| 97 | + sysPrefix: env.sysPrefix, |
| 98 | + }; |
| 99 | +} |
| 100 | + |
| 101 | +const previousEnvMap = new Map<string, PythonEnvironment | undefined>(); |
| 102 | +export async function getActiveInterpreterLegacy(resource?: Uri): Promise<PythonEnvironmentLegacy | undefined> { |
| 103 | + const api = await getEnvExtApi(); |
| 104 | + const uri = resource ? api.getPythonProject(resource)?.uri : undefined; |
| 105 | + |
| 106 | + const pythonEnv = await getEnvironment(resource); |
| 107 | + const oldEnv = previousEnvMap.get(uri?.fsPath || ''); |
| 108 | + const newEnv = pythonEnv ? toLegacyType(pythonEnv) : undefined; |
| 109 | + if (newEnv && oldEnv?.envId.id !== pythonEnv?.envId.id) { |
| 110 | + reportActiveInterpreterChanged({ |
| 111 | + resource: getWorkspaceFolder(resource), |
| 112 | + path: newEnv.path, |
| 113 | + }); |
| 114 | + } |
| 115 | + return pythonEnv ? toLegacyType(pythonEnv) : undefined; |
| 116 | +} |
| 117 | + |
| 118 | +export async function ensureEnvironmentContainsPythonLegacy(pythonPath: string): Promise<void> { |
| 119 | + const api = await getEnvExtApi(); |
| 120 | + const pythonEnv = await api.resolveEnvironment(Uri.file(pythonPath)); |
| 121 | + if (!pythonEnv) { |
| 122 | + traceError(`EnvExt: Failed to resolve environment for ${pythonPath}`); |
| 123 | + return; |
| 124 | + } |
| 125 | + |
| 126 | + const envType = toEnvironmentType(pythonEnv); |
| 127 | + if (envType === EnvironmentType.Conda) { |
| 128 | + const packages = await api.getPackages(pythonEnv); |
| 129 | + if (packages && packages.length > 0 && packages.some((pkg) => pkg.name.toLowerCase() === 'python')) { |
| 130 | + return; |
| 131 | + } |
| 132 | + traceInfo(`EnvExt: Python not found in ${envType} environment ${pythonPath}`); |
| 133 | + traceInfo(`EnvExt: Installing Python in ${envType} environment ${pythonPath}`); |
| 134 | + await api.installPackages(pythonEnv, ['python']); |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +export async function setInterpreterLegacy(pythonPath: string, uri: Uri | undefined): Promise<void> { |
| 139 | + const api = await getEnvExtApi(); |
| 140 | + const pythonEnv = await api.resolveEnvironment(Uri.file(pythonPath)); |
| 141 | + if (!pythonEnv) { |
| 142 | + traceError(`EnvExt: Failed to resolve environment for ${pythonPath}`); |
| 143 | + return; |
| 144 | + } |
| 145 | + await api.setEnvironment(uri, pythonEnv); |
| 146 | +} |
| 147 | + |
| 148 | +export async function resetInterpreterLegacy(uri: Uri | undefined): Promise<void> { |
| 149 | + const api = await getEnvExtApi(); |
| 150 | + await api.setEnvironment(uri, undefined); |
| 151 | +} |
| 152 | + |
| 153 | +export async function ensureTerminalLegacy( |
| 154 | + resource: Uri | undefined, |
| 155 | + options?: PythonTerminalOptions, |
| 156 | +): Promise<Terminal> { |
| 157 | + const api = await getEnvExtApi(); |
| 158 | + const pythonEnv = await api.getEnvironment(resource); |
| 159 | + const project = resource ? api.getPythonProject(resource) : undefined; |
| 160 | + |
| 161 | + if (pythonEnv && project) { |
| 162 | + const fixedOptions = options ? { ...options } : { cwd: project.uri }; |
| 163 | + const terminal = await api.createTerminal(pythonEnv, fixedOptions); |
| 164 | + return terminal; |
| 165 | + } |
| 166 | + throw new Error('Invalid arguments to create terminal'); |
| 167 | +} |
0 commit comments