@@ -12,7 +12,7 @@ import { getWorkspaceFolders, getWorkspaceFolder as getVSCodeWorkspaceFolder } f
1212import { AttachRequestArguments , DebugOptions , LaunchRequestArguments , PathMapping } from '../../../types' ;
1313import { PythonPathSource } from '../../types' ;
1414import { IDebugConfigurationResolver } from '../types' ;
15- import { resolveVariables } from '../utils/common' ;
15+ import { resolveWorkspaceVariables } from '../utils/common' ;
1616import { getProgram } from './helper' ;
1717import { getSettingsPythonPath , getInterpreterDetails } from '../../../common/python' ;
1818import { getOSType , OSType } from '../../../common/platform' ;
@@ -80,6 +80,16 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
8080 return undefined ;
8181 }
8282
83+ /**
84+ * Resolves and updates file paths and Python interpreter paths in the debug configuration.
85+ *
86+ * This method performs two main operations:
87+ * 1. Resolves workspace variables in the envFile path (if specified)
88+ * 2. Resolves and updates Python interpreter paths, handling legacy pythonPath deprecation
89+ *
90+ * @param workspaceFolder The workspace folder URI for variable resolution
91+ * @param debugConfiguration The launch configuration to update
92+ */
8393 protected async resolveAndUpdatePaths (
8494 workspaceFolder : Uri | undefined ,
8595 debugConfiguration : LaunchRequestArguments ,
@@ -88,76 +98,131 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
8898 await this . resolveAndUpdatePythonPath ( workspaceFolder , debugConfiguration ) ;
8999 }
90100
101+ /**
102+ * Resolves workspace variables in the envFile path.
103+ *
104+ * Expands variables like ${workspaceFolder} in the envFile configuration using the
105+ * workspace folder path or current working directory as the base for resolution.
106+ *
107+ * @param workspaceFolder The workspace folder URI for variable resolution
108+ * @param debugConfiguration The launch configuration containing the envFile path
109+ */
91110 protected static resolveAndUpdateEnvFilePath (
92111 workspaceFolder : Uri | undefined ,
93112 debugConfiguration : LaunchRequestArguments ,
94113 ) : void {
95- if ( ! debugConfiguration ) {
114+ // Early exit if no configuration or no envFile to resolve
115+ if ( ! debugConfiguration ?. envFile ) {
96116 return ;
97117 }
98- if ( debugConfiguration . envFile && ( workspaceFolder || debugConfiguration . cwd ) ) {
99- debugConfiguration . envFile = resolveVariables (
100- debugConfiguration . envFile ,
101- ( workspaceFolder ? workspaceFolder . fsPath : undefined ) || debugConfiguration . cwd ,
102- undefined ,
103- ) ;
118+
119+ const basePath = workspaceFolder ?. fsPath || debugConfiguration . cwd ;
120+
121+ if ( basePath ) {
122+ // update envFile with resolved variables
123+ debugConfiguration . envFile = resolveWorkspaceVariables ( debugConfiguration . envFile , basePath , undefined ) ;
104124 }
105125 }
106126
127+ /**
128+ * Resolves Python interpreter paths and handles the legacy pythonPath deprecation.
129+ *
130+ * @param workspaceFolder The workspace folder URI for variable resolution and interpreter detection
131+ * @param debugConfiguration The launch configuration to update with resolved Python paths
132+ */
107133 protected async resolveAndUpdatePythonPath (
108134 workspaceFolder : Uri | undefined ,
109135 debugConfiguration : LaunchRequestArguments ,
110136 ) : Promise < void > {
111137 if ( ! debugConfiguration ) {
112138 return ;
113139 }
140+
141+ // get the interpreter details in the context of the workspace folder
142+ const interpreterDetail = await getInterpreterDetails ( workspaceFolder ) ;
143+ const interpreterPath = interpreterDetail ?. path ?? ( await getSettingsPythonPath ( workspaceFolder ) ) ;
144+ const resolvedInterpreterPath = interpreterPath ? interpreterPath [ 0 ] : interpreterPath ;
145+
146+ traceLog (
147+ `resolveAndUpdatePythonPath - Initial state: ` +
148+ `pythonPath='${ debugConfiguration . pythonPath } ', ` +
149+ `python='${ debugConfiguration . python } ', ` +
150+ `debugAdapterPython='${ debugConfiguration . debugAdapterPython } ', ` +
151+ `debugLauncherPython='${ debugConfiguration . debugLauncherPython } ', ` +
152+ `workspaceFolder='${ workspaceFolder ?. fsPath } '` +
153+ `resolvedInterpreterPath='${ resolvedInterpreterPath } '` ,
154+ ) ;
155+
156+ // STEP 1: Resolve legacy pythonPath property (DEPRECATED)
157+ // pythonPath will equal user set value, or getInterpreterDetails if undefined or set to command
114158 if ( debugConfiguration . pythonPath === '${command:python.interpreterPath}' || ! debugConfiguration . pythonPath ) {
115- const interpreterDetail = await getInterpreterDetails ( workspaceFolder ) ;
116- const interpreterPath = interpreterDetail
117- ? interpreterDetail . path
118- : await getSettingsPythonPath ( workspaceFolder ) ;
119- debugConfiguration . pythonPath = interpreterPath ? interpreterPath [ 0 ] : interpreterPath ;
159+ this . pythonPathSource = PythonPathSource . settingsJson ;
160+ debugConfiguration . pythonPath = resolvedInterpreterPath ;
120161 } else {
121- debugConfiguration . pythonPath = resolveVariables (
122- debugConfiguration . pythonPath ? debugConfiguration . pythonPath : undefined ,
162+ // User provided explicit pythonPath in launch.json
163+ debugConfiguration . pythonPath = resolveWorkspaceVariables (
164+ debugConfiguration . pythonPath ,
123165 workspaceFolder ?. fsPath ,
124166 undefined ,
125167 ) ;
126168 }
127169
170+ // STEP 2: Resolve current python property (CURRENT STANDARD)
128171 if ( debugConfiguration . python === '${command:python.interpreterPath}' ) {
172+ // if python is set to the command, resolve it
129173 this . pythonPathSource = PythonPathSource . settingsJson ;
130- const interpreterDetail = await getInterpreterDetails ( workspaceFolder ) ;
131- const interpreterPath = interpreterDetail . path
132- ? interpreterDetail . path
133- : await getSettingsPythonPath ( workspaceFolder ) ;
134- debugConfiguration . python = interpreterPath ? interpreterPath [ 0 ] : interpreterPath ;
135- } else if ( debugConfiguration . python === undefined ) {
174+ debugConfiguration . python = resolvedInterpreterPath ;
175+ } else if ( ! debugConfiguration . python ) {
176+ // fallback to pythonPath if python undefined
136177 this . pythonPathSource = PythonPathSource . settingsJson ;
137178 debugConfiguration . python = debugConfiguration . pythonPath ;
138179 } else {
180+ // User provided explicit python path in launch.json
139181 this . pythonPathSource = PythonPathSource . launchJson ;
140- debugConfiguration . python = resolveVariables (
141- debugConfiguration . python ?? debugConfiguration . pythonPath ,
182+ debugConfiguration . python = resolveWorkspaceVariables (
183+ debugConfiguration . python ,
142184 workspaceFolder ?. fsPath ,
143185 undefined ,
144186 ) ;
145187 }
146188
147- if (
189+ // STEP 3: Set debug adapter and launcher Python paths (backwards compatible)
190+ this . setDebugComponentPythonPaths ( debugConfiguration ) ;
191+
192+ // STEP 4: Clean up - remove the deprecated pythonPath property
193+ delete debugConfiguration . pythonPath ;
194+ }
195+
196+ /**
197+ * Sets debugAdapterPython and debugLauncherPython with backwards compatibility.
198+ * Prefers pythonPath over python for these internal properties.
199+ *
200+ * @param debugConfiguration The debug configuration to update
201+ */
202+ private setDebugComponentPythonPaths ( debugConfiguration : LaunchRequestArguments ) : void {
203+ const shouldSetDebugAdapter =
148204 debugConfiguration . debugAdapterPython === '${command:python.interpreterPath}' ||
149- debugConfiguration . debugAdapterPython === undefined
150- ) {
151- debugConfiguration . debugAdapterPython = debugConfiguration . pythonPath ?? debugConfiguration . python ;
152- }
153- if (
205+ debugConfiguration . debugAdapterPython === undefined ;
206+
207+ const shouldSetDebugLauncher =
154208 debugConfiguration . debugLauncherPython === '${command:python.interpreterPath}' ||
155- debugConfiguration . debugLauncherPython === undefined
156- ) {
157- debugConfiguration . debugLauncherPython = debugConfiguration . pythonPath ?? debugConfiguration . python ;
209+ debugConfiguration . debugLauncherPython === undefined ;
210+
211+ // Default fallback path (prefer pythonPath for backwards compatibility)
212+ const fallbackPath = debugConfiguration . pythonPath ?? debugConfiguration . python ;
213+
214+ if ( debugConfiguration . pythonPath !== debugConfiguration . python ) {
215+ sendTelemetryEvent ( EventName . DEPRECATED_CODE_PATH_USAGE , undefined , {
216+ codePath : 'different_python_paths_in_debug_config' ,
217+ } ) ;
158218 }
159219
160- delete debugConfiguration . pythonPath ;
220+ if ( shouldSetDebugAdapter ) {
221+ debugConfiguration . debugAdapterPython = fallbackPath ;
222+ }
223+ if ( shouldSetDebugLauncher ) {
224+ debugConfiguration . debugLauncherPython = fallbackPath ;
225+ }
161226 }
162227
163228 protected static debugOption ( debugOptions : DebugOptions [ ] , debugOption : DebugOptions ) : void {
@@ -194,7 +259,7 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
194259 } else {
195260 // Expand ${workspaceFolder} variable first if necessary.
196261 pathMappings = pathMappings . map ( ( { localRoot : mappedLocalRoot , remoteRoot } ) => {
197- const resolvedLocalRoot = resolveVariables ( mappedLocalRoot , defaultLocalRoot , undefined ) ;
262+ const resolvedLocalRoot = resolveWorkspaceVariables ( mappedLocalRoot , defaultLocalRoot , undefined ) ;
198263 return {
199264 localRoot : resolvedLocalRoot || '' ,
200265 // TODO: Apply to remoteRoot too?
0 commit comments