Skip to content

Commit 83e7208

Browse files
authored
Fix debugger with conda-run (#18399)
* Fix debugger with cond-run * fix tests
1 parent 19f9d22 commit 83e7208

File tree

3 files changed

+76
-23
lines changed

3 files changed

+76
-23
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client/debugger/extension/adapter/factory.ts

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { IApplicationShell } from '../../../common/application/types';
1616
import { EXTENSION_ROOT_DIR } from '../../../constants';
1717
import { IInterpreterService } from '../../../interpreter/contracts';
1818
import { traceVerbose } from '../../../logging';
19+
import { Conda } from '../../../pythonEnvironments/common/environmentManagers/conda';
20+
import { EnvironmentType, PythonEnvironment } from '../../../pythonEnvironments/info';
1921
import { sendTelemetryEvent } from '../../../telemetry';
2022
import { EventName } from '../../../telemetry/constants';
2123
import { AttachRequestArguments, LaunchRequestArguments } from '../../types';
@@ -55,18 +57,23 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac
5557
}
5658
}
5759

58-
const pythonPath = await this.getDebugAdapterPython(configuration, session.workspaceFolder);
59-
if (pythonPath.length !== 0) {
60+
const command = await this.getDebugAdapterPython(configuration, session.workspaceFolder);
61+
if (command.length !== 0) {
6062
if (configuration.request === 'attach' && configuration.processId !== undefined) {
6163
sendTelemetryEvent(EventName.DEBUGGER_ATTACH_TO_LOCAL_PROCESS);
6264
}
6365

66+
const executable = command.shift() ?? 'python';
67+
6468
// "logToFile" is not handled directly by the adapter - instead, we need to pass
6569
// the corresponding CLI switch when spawning it.
6670
const logArgs = configuration.logToFile ? ['--log-dir', EXTENSION_ROOT_DIR] : [];
6771

6872
if (configuration.debugAdapterPath !== undefined) {
69-
return new DebugAdapterExecutable(pythonPath, [configuration.debugAdapterPath, ...logArgs]);
73+
return new DebugAdapterExecutable(
74+
executable,
75+
command.concat([configuration.debugAdapterPath, ...logArgs]),
76+
);
7077
}
7178

7279
const debuggerAdapterPathToUse = path.join(
@@ -79,7 +86,7 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac
7986
);
8087

8188
sendTelemetryEvent(EventName.DEBUG_ADAPTER_USING_WHEELS_PATH, undefined, { usingWheels: true });
82-
return new DebugAdapterExecutable(pythonPath, [debuggerAdapterPathToUse, ...logArgs]);
89+
return new DebugAdapterExecutable(executable, command.concat([debuggerAdapterPathToUse, ...logArgs]));
8390
}
8491

8592
// Unlikely scenario.
@@ -100,29 +107,66 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac
100107
private async getDebugAdapterPython(
101108
configuration: LaunchRequestArguments | AttachRequestArguments,
102109
workspaceFolder?: WorkspaceFolder,
103-
): Promise<string> {
110+
): Promise<string[]> {
104111
if (configuration.debugAdapterPython !== undefined) {
105-
return configuration.debugAdapterPython;
112+
return this.getExecutableCommand(
113+
await this.interpreterService.getInterpreterDetails(configuration.debugAdapterPython),
114+
);
106115
} else if (configuration.pythonPath) {
107-
return configuration.pythonPath;
116+
return this.getExecutableCommand(
117+
await this.interpreterService.getInterpreterDetails(configuration.pythonPath),
118+
);
108119
}
109120

110121
const resourceUri = workspaceFolder ? workspaceFolder.uri : undefined;
111122
const interpreter = await this.interpreterService.getActiveInterpreter(resourceUri);
112123
if (interpreter) {
113124
traceVerbose(`Selecting active interpreter as Python Executable for DA '${interpreter.path}'`);
114-
return interpreter.path;
125+
return this.getExecutableCommand(interpreter);
115126
}
116127

117128
await this.interpreterService.hasInterpreters(); // Wait until we know whether we have an interpreter
118129
const interpreters = await this.interpreterService.getInterpreters(resourceUri);
119130
if (interpreters.length === 0) {
120131
this.notifySelectInterpreter().ignoreErrors();
121-
return '';
132+
return [];
122133
}
123134

124135
traceVerbose(`Picking first available interpreter to launch the DA '${interpreters[0].path}'`);
125-
return interpreters[0].path;
136+
return this.getExecutableCommand(interpreters[0]);
137+
}
138+
139+
private async getExecutableCommand(interpreter: PythonEnvironment | undefined): Promise<string[]> {
140+
if (interpreter) {
141+
if (interpreter.envType === EnvironmentType.Conda) {
142+
const condaCommand = await Conda.getConda();
143+
if (condaCommand) {
144+
if (interpreter.envName) {
145+
return [
146+
condaCommand.command,
147+
'run',
148+
'-n',
149+
interpreter.envName,
150+
'--no-capture-output',
151+
'--live-stream',
152+
'python',
153+
];
154+
} else if (interpreter.envPath) {
155+
return [
156+
condaCommand.command,
157+
'run',
158+
'-p',
159+
interpreter.envPath,
160+
'--no-capture-output',
161+
'--live-stream',
162+
'python',
163+
];
164+
}
165+
}
166+
}
167+
return interpreter.path.length > 0 ? [interpreter.path] : [];
168+
}
169+
return [];
126170
}
127171

128172
/**

src/test/debugger/extension/adapter/factory.unit.test.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ suite('Debugging - Adapter Factory', () => {
106106

107107
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
108108

109-
assert.deepEqual(descriptor, debugExecutable);
109+
assert.deepStrictEqual(descriptor, debugExecutable);
110110
});
111111

112112
test('Return the path of the active interpreter as the current python path, it exists and configuration.pythonPath is not defined', async () => {
@@ -117,7 +117,7 @@ suite('Debugging - Adapter Factory', () => {
117117

118118
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
119119

120-
assert.deepEqual(descriptor, debugExecutable);
120+
assert.deepStrictEqual(descriptor, debugExecutable);
121121
});
122122

123123
test('Return the path of the first available interpreter as the current python path, configuration.pythonPath is not defined and there is no active interpreter', async () => {
@@ -126,7 +126,7 @@ suite('Debugging - Adapter Factory', () => {
126126

127127
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
128128

129-
assert.deepEqual(descriptor, debugExecutable);
129+
assert.deepStrictEqual(descriptor, debugExecutable);
130130
});
131131

132132
test('Display a message if no python interpreter is set', async () => {
@@ -147,7 +147,7 @@ suite('Debugging - Adapter Factory', () => {
147147

148148
// Interpreter not needed for host/port
149149
verify(interpreterService.getInterpreters(anything())).never();
150-
assert.deepEqual(descriptor, debugServer);
150+
assert.deepStrictEqual(descriptor, debugServer);
151151
});
152152

153153
test('Return Debug Adapter server if request is "attach", and connect is specified', async () => {
@@ -161,7 +161,7 @@ suite('Debugging - Adapter Factory', () => {
161161

162162
// Interpreter not needed for connect
163163
verify(interpreterService.getInterpreters(anything())).never();
164-
assert.deepEqual(descriptor, debugServer);
164+
assert.deepStrictEqual(descriptor, debugServer);
165165
});
166166

167167
test('Return Debug Adapter executable if request is "attach", and listen is specified', async () => {
@@ -171,7 +171,7 @@ suite('Debugging - Adapter Factory', () => {
171171
when(interpreterService.getActiveInterpreter(anything())).thenResolve(interpreter);
172172

173173
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
174-
assert.deepEqual(descriptor, debugExecutable);
174+
assert.deepStrictEqual(descriptor, debugExecutable);
175175
});
176176

177177
test('Throw error if request is "attach", and neither port, processId, listen, nor connect is specified', async () => {
@@ -200,7 +200,7 @@ suite('Debugging - Adapter Factory', () => {
200200

201201
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
202202

203-
assert.deepEqual(descriptor, debugExecutable);
203+
assert.deepStrictEqual(descriptor, debugExecutable);
204204
});
205205

206206
test("Don't pass the --log-dir argument to debug adapter if configuration.logToFile is not set", async () => {
@@ -209,7 +209,7 @@ suite('Debugging - Adapter Factory', () => {
209209

210210
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
211211

212-
assert.deepEqual(descriptor, debugExecutable);
212+
assert.deepStrictEqual(descriptor, debugExecutable);
213213
});
214214

215215
test("Don't pass the --log-dir argument to debugger if configuration.logToFile is set to false", async () => {
@@ -218,7 +218,7 @@ suite('Debugging - Adapter Factory', () => {
218218

219219
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
220220

221-
assert.deepEqual(descriptor, debugExecutable);
221+
assert.deepStrictEqual(descriptor, debugExecutable);
222222
});
223223

224224
test('Send attach to local process telemetry if attaching to a local process', async () => {
@@ -243,16 +243,25 @@ suite('Debugging - Adapter Factory', () => {
243243

244244
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
245245

246-
assert.deepEqual(descriptor, debugExecutable);
246+
assert.deepStrictEqual(descriptor, debugExecutable);
247247
});
248248

249249
test('Use "debugAdapterPython" when specified', async () => {
250250
const session = createSession({ debugAdapterPython: '/bin/custompy' });
251251
const debugExecutable = new DebugAdapterExecutable('/bin/custompy', [debugAdapterPath]);
252+
const customInterpreter = {
253+
architecture: Architecture.Unknown,
254+
path: '/bin/custompy',
255+
sysPrefix: '',
256+
sysVersion: '',
257+
envType: EnvironmentType.Unknown,
258+
version: new SemVer('3.7.4-test'),
259+
};
260+
when(interpreterService.getInterpreterDetails('/bin/custompy')).thenResolve(customInterpreter);
252261

253262
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
254263

255-
assert.deepEqual(descriptor, debugExecutable);
264+
assert.deepStrictEqual(descriptor, debugExecutable);
256265
});
257266

258267
test('Do not use "python" to spawn the debug adapter', async () => {
@@ -261,6 +270,6 @@ suite('Debugging - Adapter Factory', () => {
261270

262271
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
263272

264-
assert.deepEqual(descriptor, debugExecutable);
273+
assert.deepStrictEqual(descriptor, debugExecutable);
265274
});
266275
});

0 commit comments

Comments
 (0)