44 *--------------------------------------------------------------------------------------------*/
55
66import { AzureWizardPromptStep , type IAzureQuickPickItem , type IWizardOptions } from "@microsoft/vscode-azext-utils" ;
7+ import { gt , satisfies } from "semver" ;
78import { localize } from "../../../localize" ;
89import { getGlobalSetting } from "../../../vsCodeConfig/settings" ;
910import { EnterPythonAliasStep } from "./EnterPythonAliasStep" ;
@@ -14,6 +15,21 @@ export class PythonAliasListStep extends AzureWizardPromptStep<IPythonVenvWizard
1415 public hideStepCount : boolean = true ;
1516
1617 public async prompt ( context : IPythonVenvWizardContext ) : Promise < void > {
18+
19+
20+ if ( context . externalRuntimeConfig ) {
21+ const installedVersions = await getInstalledPythonVersions ( context ) ;
22+ const matchingVersion = findBestMatchingVersion ( context . externalRuntimeConfig . runtimeVersion , installedVersions ) ;
23+
24+ if ( matchingVersion ) {
25+ context . pythonAlias = matchingVersion . alias ;
26+ context . telemetry . properties . pythonAliasBehavior = 'externalRuntimeConfigMatch' ;
27+ return ;
28+ } else {
29+ return ;
30+ }
31+ }
32+
1733 const placeHolder : string = localize ( 'selectAlias' , 'Select a Python interpreter to create a virtual environment' ) ;
1834 const result : string | boolean = ( await context . ui . showQuickPick ( getPicks ( context ) , { placeHolder } ) ) . data ;
1935 if ( typeof result === 'string' ) {
@@ -26,6 +42,7 @@ export class PythonAliasListStep extends AzureWizardPromptStep<IPythonVenvWizard
2642 }
2743
2844 public shouldPrompt ( context : IPythonVenvWizardContext ) : boolean {
45+ // Skip prompting if external runtime configuration is provided
2946 return ! context . useExistingVenv && ! context . pythonAlias ;
3047 }
3148
@@ -55,7 +72,43 @@ async function getPicks(context: IPythonVenvWizardContext): Promise<IAzureQuickP
5572 }
5673
5774 const picks : IAzureQuickPickItem < string | boolean > [ ] = [ ] ;
58- const versions : string [ ] = [ ] ;
75+ const pythonVersions = await getInstalledPythonVersions ( context ) ;
76+ pythonVersions . forEach ( pv => picks . push ( {
77+ label : pv . alias ,
78+ description : pv . version ,
79+ data : pv . alias
80+ } ) ) ;
81+
82+ picks . push ( { label : localize ( 'enterAlias' , '$(keyboard) Manually enter Python interpreter or full path' ) , data : true } ) ;
83+
84+ if ( ! context . suppressSkipVenv ) {
85+ picks . push ( { label : localize ( 'skipVenv' , '$(circle-slash) Skip virtual environment' ) , data : false , suppressPersistence : true } ) ;
86+ }
87+
88+ return picks ;
89+ }
90+
91+ interface InstalledPythonVersion {
92+ alias : string ;
93+ version : string ;
94+ }
95+
96+ // use this method to preselect python version if runtime version is provided externally
97+
98+ async function getInstalledPythonVersions ( context : IPythonVenvWizardContext ) : Promise < InstalledPythonVersion [ ] > {
99+ const supportedVersions : string [ ] = await getSupportedPythonVersions ( context , context . version ) ;
100+
101+ const aliasesToTry : string [ ] = [ 'python' , 'python3' , 'py' ] ;
102+ for ( const version of supportedVersions ) {
103+ aliasesToTry . push ( `python${ version } ` , `py -${ version } ` ) ;
104+ }
105+
106+ const globalPythonPathSetting : string | undefined = getGlobalSetting ( 'pythonPath' , 'python' ) ;
107+ if ( globalPythonPathSetting ) {
108+ aliasesToTry . unshift ( globalPythonPathSetting ) ;
109+ }
110+
111+ const versions : InstalledPythonVersion [ ] = [ ] ;
59112 for ( const alias of aliasesToTry ) {
60113 let version : string ;
61114 try {
@@ -64,23 +117,27 @@ async function getPicks(context: IPythonVenvWizardContext): Promise<IAzureQuickP
64117 continue ;
65118 }
66119
67- if ( isSupportedPythonVersion ( supportedVersions , version ) && ! versions . some ( v => v === version ) ) {
68- picks . push ( {
69- label : alias ,
70- description : version ,
71- data : alias
120+ if ( isSupportedPythonVersion ( supportedVersions , version ) && ! versions . some ( v => v . version === version ) ) {
121+ versions . push ( {
122+ alias,
123+ version,
72124 } ) ;
73- versions . push ( version ) ;
74125 }
75126 }
76127
77128 context . telemetry . properties . detectedPythonVersions = versions . join ( ',' ) ;
129+ return versions ;
130+ }
78131
79- picks . push ( { label : localize ( 'enterAlias' , '$(keyboard) Manually enter Python interpreter or full path' ) , data : true } ) ;
132+ // use semver to find a matching python version
133+ function findMatchingVersion ( requestedVersion : string , versions : InstalledPythonVersion [ ] ) : InstalledPythonVersion | undefined {
134+ return versions . find ( v => satisfies ( v . version , requestedVersion ) ) ;
135+ }
80136
81- if ( ! context . suppressSkipVenv ) {
82- picks . push ( { label : localize ( 'skipVenv' , '$(circle-slash) Skip virtual environment' ) , data : false , suppressPersistence : true } ) ;
137+ function findBestMatchingVersion ( requestedVersion : string , versions : InstalledPythonVersion [ ] ) : InstalledPythonVersion | undefined {
138+ let matchingVersion = findMatchingVersion ( requestedVersion , versions ) ;
139+ if ( ! matchingVersion ) {
140+ matchingVersion = versions . reduce ( ( prev , current ) => ( gt ( current . version , prev . version ) ? current : prev ) ) ;
83141 }
84-
85- return picks ;
142+ return matchingVersion ;
86143}
0 commit comments