33
44import * as path from 'path' ;
55import { Uri } from 'vscode' ;
6+ import { uniq } from 'lodash' ;
67import { traceError , traceWarning } from '../../../../common/logger' ;
7- import { PythonEnvInfo , PythonEnvKind , PythonEnvSource } from '../../info' ;
8- import { buildEnvInfo , getEnvMatcher } from '../../info/env' ;
8+ import { PythonEnvInfo , PythonEnvKind , PythonEnvSource , UNKNOWN_PYTHON_VERSION } from '../../info' ;
9+ import { buildEnvInfo , comparePythonVersionSpecificity , getEnvMatcher } from '../../info/env' ;
910import {
1011 getEnvironmentDirFromPath ,
1112 getInterpreterPathFromDir ,
@@ -15,8 +16,9 @@ import { identifyEnvironment } from '../../../common/environmentIdentifier';
1516import { getFileInfo , getWorkspaceFolders , isParentPath } from '../../../common/externalDependencies' ;
1617import { AnacondaCompanyName , Conda } from '../../../discovery/locators/services/conda' ;
1718import { parsePyenvVersion } from '../../../discovery/locators/services/pyenvLocator' ;
18- import { Architecture } from '../../../../common/utils/platform' ;
19- import { getPythonVersionFromPath as parsePythonVersionFromPath } from '../../info/pythonVersion' ;
19+ import { Architecture , getOSType , OSType } from '../../../../common/utils/platform' ;
20+ import { getPythonVersionFromPath as parsePythonVersionFromPath , parseVersion } from '../../info/pythonVersion' ;
21+ import { getRegistryInterpreters } from '../../../common/windowsUtils' ;
2022
2123function getResolvers ( ) : Map < PythonEnvKind , ( executablePath : string ) => Promise < PythonEnvInfo > > {
2224 const resolvers = new Map < PythonEnvKind , ( _ : string ) => Promise < PythonEnvInfo > > ( ) ;
@@ -40,8 +42,17 @@ export async function resolveEnv(executablePath: string): Promise<PythonEnvInfo>
4042 const resolvers = getResolvers ( ) ;
4143 const resolverForKind = resolvers . get ( kind ) ! ;
4244 const resolvedEnv = await resolverForKind ( executablePath ) ;
45+ resolvedEnv . searchLocation = getSearchLocation ( resolvedEnv ) ;
46+ if ( getOSType ( ) === OSType . Windows ) {
47+ // We can update env further using information we can get from the Windows registry.
48+ await updateEnvUsingRegistry ( resolvedEnv ) ;
49+ }
50+ return resolvedEnv ;
51+ }
52+
53+ function getSearchLocation ( env : PythonEnvInfo ) : Uri | undefined {
4354 const folders = getWorkspaceFolders ( ) ;
44- const isRootedEnv = folders . some ( ( f ) => isParentPath ( executablePath , f ) ) ;
55+ const isRootedEnv = folders . some ( ( f ) => isParentPath ( env . executable . filename , f ) ) ;
4556 if ( isRootedEnv ) {
4657 // For environments inside roots, we need to set search location so they can be queried accordingly.
4758 // Search location particularly for virtual environments is intended as the directory in which the
@@ -52,17 +63,37 @@ export async function resolveEnv(executablePath: string): Promise<PythonEnvInfo>
5263 // |__ env
5364 // |__ bin or Scripts
5465 // |__ python <--- executable
55- resolvedEnv . searchLocation = Uri . file ( path . dirname ( resolvedEnv . location ) ) ;
66+ return Uri . file ( path . dirname ( env . location ) ) ;
67+ }
68+ return undefined ;
69+ }
70+
71+ async function updateEnvUsingRegistry ( env : PythonEnvInfo ) : Promise < void > {
72+ const interpreters = await getRegistryInterpreters ( ) ;
73+ const data = interpreters . find ( ( i ) => i . interpreterPath . toUpperCase ( ) === env . executable . filename . toUpperCase ( ) ) ;
74+ if ( data ) {
75+ const versionStr = data . versionStr ?? data . sysVersionStr ?? data . interpreterPath ;
76+ let version ;
77+ try {
78+ version = parseVersion ( versionStr ) ;
79+ } catch ( ex ) {
80+ version = UNKNOWN_PYTHON_VERSION ;
81+ }
82+ env . kind = env . kind === PythonEnvKind . Unknown ? PythonEnvKind . OtherGlobal : env . kind ;
83+ env . version = comparePythonVersionSpecificity ( version , env . version ) > 0 ? version : env . version ;
84+ env . distro . defaultDisplayName = data . companyDisplayName ;
85+ env . arch = data . bitnessStr === '32bit' ? Architecture . x86 : Architecture . x64 ;
86+ env . distro . org = data . distroOrgName ?? env . distro . org ;
87+ env . distro . defaultDisplayName = data . companyDisplayName ;
88+ env . source = uniq ( env . source . concat ( PythonEnvSource . WindowsRegistry ) ) ;
5689 }
57- return resolvedEnv ;
5890}
5991
6092async function resolveSimpleEnv ( executablePath : string , kind : PythonEnvKind ) : Promise < PythonEnvInfo > {
6193 const envInfo = buildEnvInfo ( {
6294 kind,
6395 version : await getPythonVersionFromPath ( executablePath ) ,
6496 executable : executablePath ,
65- source : [ PythonEnvSource . Other ] ,
6697 } ) ;
6798 const location = getEnvironmentDirFromPath ( executablePath ) ;
6899 envInfo . location = location ;
@@ -109,14 +140,35 @@ async function resolvePyenvEnv(executablePath: string): Promise<PythonEnvInfo> {
109140 const location = getEnvironmentDirFromPath ( executablePath ) ;
110141 const name = path . basename ( location ) ;
111142
143+ // The sub-directory name sometimes can contain distro and python versions.
144+ // here we attempt to extract the texts out of the name.
112145 const versionStrings = await parsePyenvVersion ( name ) ;
113146
114147 const envInfo = buildEnvInfo ( {
115148 kind : PythonEnvKind . Pyenv ,
116149 executable : executablePath ,
117150 source : [ PythonEnvSource . Pyenv ] ,
118151 location,
152+ // Pyenv environments can fall in to these three categories:
153+ // 1. Global Installs : These are environments that are created when you install
154+ // a supported python distribution using `pyenv install <distro>` command.
155+ // These behave similar to globally installed version of python or distribution.
156+ //
157+ // 2. Virtual Envs : These are environments that are created when you use
158+ // `pyenv virtualenv <distro> <env-name>`. These are similar to environments
159+ // created using `python -m venv <env-name>`.
160+ //
161+ // 3. Conda Envs : These are environments that are created when you use
162+ // `pyenv virtualenv <miniconda|anaconda> <env-name>`. These are similar to
163+ // environments created using `conda create -n <env-name>.
164+ //
165+ // All these environments are fully handled by `pyenv` and should be activated using
166+ // `pyenv local|global <env-name>` or `pyenv shell <env-name>`
167+ //
168+ // For the display name we are going to treat these as `pyenv` environments.
119169 display : `${ name } :pyenv` ,
170+ // Here we look for near by files, or config files to see if we can get python version info
171+ // without running python itself.
120172 version : await getPythonVersionFromPath ( executablePath , versionStrings ?. pythonVer ) ,
121173 org : versionStrings && versionStrings . distro ? versionStrings . distro : '' ,
122174 fileInfo : await getFileInfo ( executablePath ) ,
0 commit comments