Skip to content

Commit b5ee72d

Browse files
author
Kartik Raj
authored
Rename ${config:python.pythonPath} used in launch.json to ${config:python.interpreterPath} (#11453)
* News entry * Rename' * Tests * Code reviews
1 parent 71de303 commit b5ee72d

12 files changed

Lines changed: 142 additions & 23 deletions

File tree

news/1 Enhancements/11446.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Rename string `${config:python.pythonPath}` which is used in `launch.json` to refer to interpreter path set in settings, to `${config:python.interpreterPath}`.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,8 +1210,8 @@
12101210
},
12111211
"pythonPath": {
12121212
"type": "string",
1213-
"description": "Path (fully qualified) to python executable. Defaults to the value in settings.json",
1214-
"default": "${config:python.pythonPath}"
1213+
"description": "Path (fully qualified) to python executable. Defaults to the value in settings",
1214+
"default": "${config:python.interpreterPath}"
12151215
},
12161216
"args": {
12171217
"type": "array",
@@ -1348,8 +1348,8 @@
13481348
"properties": {
13491349
"pythonPath": {
13501350
"type": "string",
1351-
"description": "Path (fully qualified) to python executable. Defaults to the value in settings.json",
1352-
"default": "${config:python.pythonPath}"
1351+
"description": "Path (fully qualified) to python executable. Defaults to the value in settings",
1352+
"default": "${config:python.interpreterPath}"
13531353
},
13541354
"stopOnEntry": {
13551355
"type": "boolean",

src/client/application/diagnostics/base.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export abstract class BaseDiagnostic implements IDiagnostic {
2121
public readonly severity: DiagnosticSeverity,
2222
public readonly scope: DiagnosticScope,
2323
public readonly resource: Resource,
24-
public readonly invokeHandler: 'always' | 'default' = 'default'
24+
public readonly invokeHandler: 'always' | 'default' = 'default',
25+
public readonly shouldShowPrompt = true
2526
) {}
2627
}
2728

src/client/application/diagnostics/checks/invalidLaunchJsonDebugger.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,29 @@ import { DiagnosticScope, IDiagnostic, IDiagnosticHandlerService } from '../type
2020
const messages = {
2121
[DiagnosticCodes.InvalidDebuggerTypeDiagnostic]: Diagnostics.invalidDebuggerTypeDiagnostic(),
2222
[DiagnosticCodes.JustMyCodeDiagnostic]: Diagnostics.justMyCodeDiagnostic(),
23-
[DiagnosticCodes.ConsoleTypeDiagnostic]: Diagnostics.consoleTypeDiagnostic()
23+
[DiagnosticCodes.ConsoleTypeDiagnostic]: Diagnostics.consoleTypeDiagnostic(),
24+
[DiagnosticCodes.ConfigPythonPathDiagnostic]: ''
2425
};
2526

2627
export class InvalidLaunchJsonDebuggerDiagnostic extends BaseDiagnostic {
2728
constructor(
2829
code:
2930
| DiagnosticCodes.InvalidDebuggerTypeDiagnostic
3031
| DiagnosticCodes.JustMyCodeDiagnostic
31-
| DiagnosticCodes.ConsoleTypeDiagnostic,
32-
resource: Resource
32+
| DiagnosticCodes.ConsoleTypeDiagnostic
33+
| DiagnosticCodes.ConfigPythonPathDiagnostic,
34+
resource: Resource,
35+
shouldShowPrompt: boolean = true
3336
) {
34-
super(code, messages[code], DiagnosticSeverity.Error, DiagnosticScope.WorkspaceFolder, resource, 'always');
37+
super(
38+
code,
39+
messages[code],
40+
DiagnosticSeverity.Error,
41+
DiagnosticScope.WorkspaceFolder,
42+
resource,
43+
'always',
44+
shouldShowPrompt
45+
);
3546
}
3647
}
3748

@@ -52,7 +63,8 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
5263
[
5364
DiagnosticCodes.InvalidDebuggerTypeDiagnostic,
5465
DiagnosticCodes.JustMyCodeDiagnostic,
55-
DiagnosticCodes.ConsoleTypeDiagnostic
66+
DiagnosticCodes.ConsoleTypeDiagnostic,
67+
DiagnosticCodes.ConfigPythonPathDiagnostic
5668
],
5769
serviceContainer,
5870
disposableRegistry,
@@ -101,12 +113,20 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
101113
if (fileContents.indexOf('"console": "none"') > 0) {
102114
diagnostics.push(new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.ConsoleTypeDiagnostic, resource));
103115
}
116+
if (fileContents.indexOf('{config:python.pythonPath}') > 0) {
117+
diagnostics.push(
118+
new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.ConfigPythonPathDiagnostic, resource, false)
119+
);
120+
}
104121
return diagnostics;
105122
}
106123
private async handleDiagnostic(diagnostic: IDiagnostic): Promise<void> {
107124
if (!this.canHandle(diagnostic)) {
108125
return;
109126
}
127+
if (!diagnostic.shouldShowPrompt) {
128+
return this.fixLaunchJson(diagnostic.code);
129+
}
110130
const commandPrompts = [
111131
{
112132
prompt: Diagnostics.yesUpdateLaunch(),
@@ -145,6 +165,14 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
145165
fileContents = this.findAndReplace(fileContents, '"console": "none"', '"console": "internalConsole"');
146166
break;
147167
}
168+
case DiagnosticCodes.ConfigPythonPathDiagnostic: {
169+
fileContents = this.findAndReplace(
170+
fileContents,
171+
'{config:python.pythonPath}',
172+
'{config:python.interpreterPath}'
173+
);
174+
break;
175+
}
148176
default: {
149177
return;
150178
}

src/client/application/diagnostics/checks/invalidPythonPathInDebugger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class InvalidPythonPathInDebuggerService extends BaseDiagnosticsService
7676
public async validatePythonPath(pythonPath?: string, pythonPathSource?: PythonPathSource, resource?: Uri) {
7777
pythonPath = pythonPath ? this.resolveVariables(pythonPath, resource) : undefined;
7878
// tslint:disable-next-line:no-invalid-template-strings
79-
if (pythonPath === '${config:python.pythonPath}' || !pythonPath) {
79+
if (pythonPath === '${config:python.interpreterPath}' || !pythonPath) {
8080
pythonPath = this.configService.getSettings(resource).pythonPath;
8181
}
8282
if (await this.interpreterHelper.getInterpreterInformation(pythonPath).catch(() => undefined)) {

src/client/application/diagnostics/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ export enum DiagnosticCodes {
1616
LSNotSupportedDiagnostic = 'LSNotSupportedDiagnostic',
1717
PythonPathDeprecatedDiagnostic = 'PythonPathDeprecatedDiagnostic',
1818
JustMyCodeDiagnostic = 'JustMyCodeDiagnostic',
19-
ConsoleTypeDiagnostic = 'ConsoleTypeDiagnostic'
19+
ConsoleTypeDiagnostic = 'ConsoleTypeDiagnostic',
20+
ConfigPythonPathDiagnostic = 'ConfigPythonPathDiagnostic'
2021
}

src/client/application/diagnostics/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface IDiagnostic {
2525
readonly scope: DiagnosticScope;
2626
readonly resource: Resource;
2727
readonly invokeHandler: 'always' | 'default';
28+
readonly shouldShowPrompt?: boolean;
2829
}
2930

3031
export const IDiagnosticsService = Symbol('IDiagnosticsService');

src/client/debugger/extension/configuration/resolvers/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
9191
if (!debugConfiguration) {
9292
return;
9393
}
94-
if (debugConfiguration.pythonPath === '${config:python.pythonPath}' || !debugConfiguration.pythonPath) {
94+
if (debugConfiguration.pythonPath === '${config:python.interpreterPath}' || !debugConfiguration.pythonPath) {
9595
const pythonPath = this.configurationService.getSettings(workspaceFolder).pythonPath;
9696
debugConfiguration.pythonPath = pythonPath;
9797
this.pythonPathSource = PythonPathSource.settingsJson;

src/test/application/diagnostics/checks/invalidLaunchJsonDebugger.unit.test.ts

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => {
162162
const diagnostics = await diagnosticService.diagnose(undefined);
163163
expect(diagnostics).to.be.deep.equal(
164164
[new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.InvalidDebuggerTypeDiagnostic, undefined)],
165-
'not the same'
165+
'Diagnostics returned are not as expected'
166166
);
167167
workspaceService.verifyAll();
168168
fs.verifyAll();
@@ -187,7 +187,32 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => {
187187
const diagnostics = await diagnosticService.diagnose(undefined);
188188
expect(diagnostics).to.be.deep.equal(
189189
[new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.JustMyCodeDiagnostic, undefined)],
190-
'not the same'
190+
'Diagnostics returned are not as expected'
191+
);
192+
workspaceService.verifyAll();
193+
fs.verifyAll();
194+
});
195+
196+
test('Should return ConfigPythonPathDiagnostic if file launch.json contains string "{config:python.pythonPath}"', async () => {
197+
const fileContents = 'Hello I am launch.json, I contain string {config:python.pythonPath}';
198+
workspaceService
199+
.setup((w) => w.hasWorkspaceFolders)
200+
.returns(() => true)
201+
.verifiable(TypeMoq.Times.once());
202+
workspaceService
203+
.setup((w) => w.workspaceFolders)
204+
.returns(() => [workspaceFolder])
205+
.verifiable(TypeMoq.Times.once());
206+
fs.setup((w) => w.fileExists(TypeMoq.It.isAny()))
207+
.returns(() => Promise.resolve(true))
208+
.verifiable(TypeMoq.Times.once());
209+
fs.setup((w) => w.readFile(TypeMoq.It.isAny()))
210+
.returns(() => Promise.resolve(fileContents))
211+
.verifiable(TypeMoq.Times.once());
212+
const diagnostics = await diagnosticService.diagnose(undefined);
213+
expect(diagnostics).to.be.deep.equal(
214+
[new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.ConfigPythonPathDiagnostic, undefined, false)],
215+
'Diagnostics returned are not as expected'
191216
);
192217
workspaceService.verifyAll();
193218
fs.verifyAll();
@@ -215,13 +240,13 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => {
215240
new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.InvalidDebuggerTypeDiagnostic, undefined),
216241
new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.JustMyCodeDiagnostic, undefined)
217242
],
218-
'not the same'
243+
'Diagnostics returned are not as expected'
219244
);
220245
workspaceService.verifyAll();
221246
fs.verifyAll();
222247
});
223248

224-
test('All InvalidLaunchJsonDebugger diagnostics should display 2 options to with one command', async () => {
249+
test('All InvalidLaunchJsonDebugger diagnostics with `shouldShowPrompt` set to `true` should display a prompt with 2 buttons where clicking the first button will invoke a command', async () => {
225250
for (const code of [
226251
DiagnosticCodes.InvalidDebuggerTypeDiagnostic,
227252
DiagnosticCodes.JustMyCodeDiagnostic,
@@ -233,6 +258,10 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => {
233258
.setup((d) => d.code)
234259
.returns(() => code)
235260
.verifiable(TypeMoq.Times.atLeastOnce());
261+
diagnostic
262+
.setup((d) => d.shouldShowPrompt)
263+
.returns(() => true)
264+
.verifiable(TypeMoq.Times.atLeastOnce());
236265
messageHandler
237266
.setup((m) => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
238267
.callback((_, opts: MessageCommandPrompt) => (options = opts))
@@ -254,6 +283,39 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => {
254283
}
255284
});
256285

286+
test('All InvalidLaunchJsonDebugger diagnostics with `shouldShowPrompt` set to `false` should directly fix launch.json', async () => {
287+
for (const code of [DiagnosticCodes.ConfigPythonPathDiagnostic]) {
288+
let called = false;
289+
(diagnosticService as any).fixLaunchJson = () => {
290+
called = true;
291+
};
292+
const diagnostic = TypeMoq.Mock.ofType<IDiagnostic>();
293+
diagnostic
294+
.setup((d) => d.code)
295+
.returns(() => code)
296+
.verifiable(TypeMoq.Times.atLeastOnce());
297+
diagnostic
298+
.setup((d) => d.shouldShowPrompt)
299+
.returns(() => false)
300+
.verifiable(TypeMoq.Times.atLeastOnce());
301+
messageHandler
302+
.setup((m) => m.handle(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
303+
.verifiable(TypeMoq.Times.never());
304+
baseWorkspaceService
305+
.setup((c) => c.getWorkspaceFolder(TypeMoq.It.isAny()))
306+
.returns(() => workspaceFolder)
307+
.verifiable(TypeMoq.Times.atLeastOnce());
308+
309+
await diagnosticService.handle([diagnostic.object]);
310+
311+
diagnostic.verifyAll();
312+
commandFactory.verifyAll();
313+
messageHandler.verifyAll();
314+
baseWorkspaceService.verifyAll();
315+
expect(called).to.equal(true, '');
316+
}
317+
});
318+
257319
test('All InvalidLaunchJsonDebugger diagnostics should display message twice if invoked twice', async () => {
258320
for (const code of [
259321
DiagnosticCodes.InvalidDebuggerTypeDiagnostic,
@@ -407,4 +469,29 @@ suite('Application Diagnostics - Checks if launch.json is invalid', () => {
407469
workspaceService.verifyAll();
408470
fs.verifyAll();
409471
});
472+
473+
test('File launch.json is fixed correctly when code equals ConfigPythonPathDiagnostic ', async () => {
474+
const launchJson = 'This string contains {config:python.pythonPath}';
475+
const correctedlaunchJson = 'This string contains {config:python.interpreterPath}';
476+
workspaceService
477+
.setup((w) => w.hasWorkspaceFolders)
478+
.returns(() => true)
479+
.verifiable(TypeMoq.Times.once());
480+
workspaceService
481+
.setup((w) => w.workspaceFolders)
482+
.returns(() => [workspaceFolder])
483+
.verifiable(TypeMoq.Times.once());
484+
fs.setup((w) => w.fileExists(TypeMoq.It.isAny()))
485+
.returns(() => Promise.resolve(true))
486+
.verifiable(TypeMoq.Times.once());
487+
fs.setup((w) => w.readFile(TypeMoq.It.isAny()))
488+
.returns(() => Promise.resolve(launchJson))
489+
.verifiable(TypeMoq.Times.atLeastOnce());
490+
fs.setup((w) => w.writeFile(TypeMoq.It.isAnyString(), correctedlaunchJson))
491+
.returns(() => Promise.resolve())
492+
.verifiable(TypeMoq.Times.once());
493+
await (diagnosticService as any).fixLaunchJson(DiagnosticCodes.ConfigPythonPathDiagnostic);
494+
workspaceService.verifyAll();
495+
fs.verifyAll();
496+
});
410497
});

src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => {
227227
expect(options!.commandPrompts).to.be.lengthOf(1);
228228
expect(options!.commandPrompts[0].prompt).to.be.equal('Open launch.json');
229229
});
230-
test('Ensure we get python path from config when path = ${config:python.pythonPath}', async () => {
231-
const pythonPath = '${config:python.pythonPath}';
230+
test('Ensure we get python path from config when path = ${config:python.interpreterPath}', async () => {
231+
const pythonPath = '${config:python.interpreterPath}';
232232

233233
const settings = typemoq.Mock.ofType<IPythonSettings>();
234234
settings

0 commit comments

Comments
 (0)