Skip to content

Commit 6389ad3

Browse files
authored
🐛 resolve pythonPath in debug config
Fixes microsoft#691
1 parent 6015468 commit 6389ad3

11 files changed

Lines changed: 396 additions & 114 deletions

File tree

src/client/common/variables/systemVariables.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,6 @@ import * as Types from './sysTypes';
33
import { IStringDictionary, ISystemVariables } from './types';
44
/* tslint:disable:rule1 no-any no-unnecessary-callback-wrapper jsdoc-format no-for-in prefer-const no-increment-decrement */
55

6-
export abstract class Parser {
7-
8-
protected static merge<T>(destination: T, source: T, overwrite: boolean): void {
9-
Object.keys(source).forEach((key) => {
10-
const destValue = (destination as any as { [key: string]: string })[key];
11-
const sourceValue = (source as any as { [key: string]: string })[key];
12-
if (Types.isUndefined(sourceValue)) {
13-
return;
14-
}
15-
if (Types.isUndefined(destValue)) {
16-
(destination as any as { [key: string]: string })[key] = sourceValue;
17-
} else {
18-
if (overwrite) {
19-
if (Types.isObject(destValue) && Types.isObject(sourceValue)) {
20-
this.merge(destValue, sourceValue, overwrite);
21-
} else {
22-
(destination as any as { [key: string]: string })[key] = sourceValue;
23-
}
24-
}
25-
}
26-
});
27-
}
28-
29-
// tslint:disable-next-line:no-empty
30-
protected log(message: string): void { }
31-
32-
// tslint:disable-next-line:no-any
33-
protected is(value: any, func: (value: any) => boolean, wrongTypeState?: any, wrongTypeMessage?: string, undefinedState?: any, undefinedMessage?: string): boolean {
34-
if (Types.isUndefined(value)) {
35-
return false;
36-
}
37-
if (!func(value)) {
38-
return false;
39-
}
40-
return true;
41-
}
42-
}
43-
446
export abstract class AbstractSystemVariables implements ISystemVariables {
457

468
public resolve(value: string): string;

src/client/debugger/Common/Contracts.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ export interface ExceptionHandling {
4444
unhandled: string[];
4545
}
4646

47+
export type DebuggerType = 'python' | 'pythonExperimental';
48+
4749
export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
48-
type?: 'python' | 'pythonExperimental';
50+
type?: DebuggerType;
4951
/** An absolute path to the program to debug. */
5052
module?: string;
5153
program: string;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { injectable, unmanaged } from 'inversify';
7+
import * as path from 'path';
8+
import { CancellationToken, DebugConfiguration, DebugConfigurationProvider, ProviderResult, Uri, WorkspaceFolder } from 'vscode';
9+
import { IDocumentManager, IWorkspaceService } from '../../common/application/types';
10+
import { PythonLanguage } from '../../common/constants';
11+
import { IConfigurationService } from '../../common/types';
12+
import { IServiceContainer } from '../../ioc/types';
13+
import { DebuggerType, LaunchRequestArguments } from '../Common/Contracts';
14+
15+
// tslint:disable:no-invalid-template-strings
16+
17+
export type PythonDebugConfiguration = DebugConfiguration & LaunchRequestArguments;
18+
19+
@injectable()
20+
export abstract class BaseConfigurationProvider implements DebugConfigurationProvider {
21+
constructor(@unmanaged() public debugType: DebuggerType, private serviceContainer: IServiceContainer) { }
22+
public resolveDebugConfiguration(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugConfiguration> {
23+
const config = debugConfiguration as PythonDebugConfiguration;
24+
const numberOfSettings = Object.keys(config);
25+
const provideDefaultConfigSettings = (config.noDebug === true && numberOfSettings.length === 1) || numberOfSettings.length === 0;
26+
const workspaceFolder = this.getWorkspaceFolder(folder, config);
27+
if (!provideDefaultConfigSettings) {
28+
this.resolveAndUpdatePythonPath(workspaceFolder, config);
29+
return config;
30+
}
31+
32+
const configService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
33+
const pythonPath = configService.getSettings(workspaceFolder).pythonPath;
34+
const defaultProgram = this.getProgram(config);
35+
const envFile = workspaceFolder ? path.join(workspaceFolder.fsPath, '.env') : '';
36+
37+
config.name = 'Launch';
38+
config.type = this.debugType;
39+
config.request = 'launch';
40+
config.pythonPath = pythonPath;
41+
config.program = defaultProgram ? defaultProgram : '';
42+
config.cwd = workspaceFolder ? workspaceFolder.fsPath : undefined;
43+
config.envFile = envFile;
44+
config.env = {};
45+
config.debugOptions = [];
46+
47+
this.provideDefaults(config);
48+
return config;
49+
}
50+
protected abstract provideDefaults(debugConfiguration: PythonDebugConfiguration): void;
51+
private getWorkspaceFolder(folder: WorkspaceFolder | undefined, config: PythonDebugConfiguration): Uri | undefined {
52+
if (folder) {
53+
return folder.uri;
54+
}
55+
const program = this.getProgram(config);
56+
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
57+
if (!Array.isArray(workspaceService.workspaceFolders) || workspaceService.workspaceFolders.length === 0) {
58+
return program ? Uri.file(path.dirname(program)) : undefined;
59+
}
60+
if (workspaceService.workspaceFolders.length === 1) {
61+
return workspaceService.workspaceFolders[0].uri;
62+
}
63+
if (program) {
64+
const workspaceFolder = workspaceService.getWorkspaceFolder(Uri.file(program));
65+
if (workspaceFolder) {
66+
return workspaceFolder.uri;
67+
}
68+
}
69+
}
70+
private getProgram(config: PythonDebugConfiguration): string | undefined {
71+
const documentManager = this.serviceContainer.get<IDocumentManager>(IDocumentManager);
72+
const editor = documentManager.activeTextEditor;
73+
if (editor && editor.document.languageId === PythonLanguage.language) {
74+
return editor.document.fileName;
75+
}
76+
}
77+
private resolveAndUpdatePythonPath(workspaceFolder: Uri | undefined, debugConfiguration: PythonDebugConfiguration): void {
78+
if (!debugConfiguration || debugConfiguration.pythonPath !== '${config:python.pythonPath}') {
79+
return;
80+
}
81+
const configService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
82+
const pythonPath = configService.getSettings(workspaceFolder).pythonPath;
83+
debugConfiguration.pythonPath = pythonPath;
84+
}
85+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { inject, injectable } from 'inversify';
7+
import { IServiceContainer } from '../../ioc/types';
8+
import { BaseConfigurationProvider, PythonDebugConfiguration } from './baseProvider';
9+
10+
@injectable()
11+
export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvider {
12+
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
13+
super('pythonExperimental', serviceContainer);
14+
}
15+
protected provideDefaults(debugConfiguration: PythonDebugConfiguration): void {
16+
debugConfiguration.stopOnEntry = false;
17+
debugConfiguration.console = 'integratedTerminal';
18+
}
19+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { inject, injectable } from 'inversify';
7+
import { IServiceContainer } from '../../ioc/types';
8+
import { BaseConfigurationProvider, PythonDebugConfiguration } from './baseProvider';
9+
10+
@injectable()
11+
export class PythonDebugConfigurationProvider extends BaseConfigurationProvider {
12+
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
13+
super('python', serviceContainer);
14+
}
15+
protected provideDefaults(debugConfiguration: PythonDebugConfiguration): void {
16+
debugConfiguration.stopOnEntry = true;
17+
debugConfiguration.debugOptions = [
18+
'RedirectOutput'
19+
];
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License.
4+
5+
'use strict';
6+
7+
import { DebugConfigurationProvider } from 'vscode';
8+
import { PythonDebugConfigurationProvider, PythonV2DebugConfigurationProvider } from '..';
9+
import { IServiceManager } from '../../ioc/types';
10+
import { IDebugConfigurationProvider } from '../types';
11+
12+
export function registerTypes(serviceManager: IServiceManager) {
13+
serviceManager.addSingleton<DebugConfigurationProvider>(IDebugConfigurationProvider, PythonDebugConfigurationProvider);
14+
serviceManager.addSingleton<DebugConfigurationProvider>(IDebugConfigurationProvider, PythonV2DebugConfigurationProvider);
15+
}

src/client/debugger/configProviders/simpleProvider.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.

src/client/debugger/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export * from './configProviders/simpleProvider';
1+
export * from './configProviders/pythonProvider';
2+
export * from './configProviders/pyhtonV2Provider';

src/client/debugger/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ export const IProtocolMessageWriter = Symbol('IProtocolMessageWriter');
3636
export interface IProtocolMessageWriter {
3737
write(stream: Socket | NodeJS.WriteStream, message: Message): void;
3838
}
39+
40+
export const IDebugConfigurationProvider = Symbol('DebugConfigurationProvider');

src/client/extension.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ if ((Reflect as any).metadata === undefined) {
66
require('reflect-metadata');
77
}
88
import { Container } from 'inversify';
9-
import * as os from 'os';
109
import * as vscode from 'vscode';
1110
import { Disposable, Memento, OutputChannel, window } from 'vscode';
1211
import { BannerService } from './banner';
@@ -23,7 +22,9 @@ import { IProcessService } from './common/process/types';
2322
import { registerTypes as commonRegisterTypes } from './common/serviceRegistry';
2423
import { GLOBAL_MEMENTO, IDisposableRegistry, ILogger, IMemento, IOutputChannel, IPersistentStateFactory, WORKSPACE_MEMENTO } from './common/types';
2524
import { registerTypes as variableRegisterTypes } from './common/variables/serviceRegistry';
26-
import { SimpleConfigurationProvider } from './debugger';
25+
import { BaseConfigurationProvider } from './debugger/configProviders/baseProvider';
26+
import { registerTypes as debugConfigurationRegisterTypes } from './debugger/configProviders/serviceRegistry';
27+
import { IDebugConfigurationProvider } from './debugger/types';
2728
import { registerTypes as formattersRegisterTypes } from './formatters/serviceRegistry';
2829
import { InterpreterSelector } from './interpreter/configuration/interpreterSelector';
2930
import { ICondaService, IInterpreterService, IInterpreterVersionService } from './interpreter/contracts';
@@ -92,6 +93,7 @@ export async function activate(context: vscode.ExtensionContext) {
9293
platformRegisterTypes(serviceManager);
9394
installerRegisterTypes(serviceManager);
9495
commonRegisterTerminalTypes(serviceManager);
96+
debugConfigurationRegisterTypes(serviceManager);
9597

9698
serviceManager.get<ICodeExecutionManager>(ICodeExecutionManager).registerCommands();
9799

@@ -193,11 +195,9 @@ export async function activate(context: vscode.ExtensionContext) {
193195
context.subscriptions.push(vscode.languages.registerOnTypeFormattingEditProvider(PYTHON, new BlockFormatProviders(), ':'));
194196
context.subscriptions.push(vscode.languages.registerOnTypeFormattingEditProvider(PYTHON, new OnEnterFormatter(), '\n'));
195197

196-
// In case we have CR LF
197-
const triggerCharacters: string[] = os.EOL.split('');
198-
triggerCharacters.shift();
199-
200-
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('python', new SimpleConfigurationProvider()));
198+
serviceContainer.getAll<BaseConfigurationProvider>(IDebugConfigurationProvider).forEach(debugConfig => {
199+
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(debugConfig.debugType, debugConfig));
200+
});
201201
activationDeferred.resolve();
202202

203203
// tslint:disable-next-line:no-unused-expression

0 commit comments

Comments
 (0)