Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/1 Enhancements/12462.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Replaced "pythonPath" debug configuration property with "python".
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1571,9 +1571,9 @@
"description": "Absolute path to the program.",
"default": "${file}"
},
"pythonPath": {
"python": {
"type": "string",
"description": "Path (fully qualified) to python executable. Defaults to the value in settings",
"description": "Absolute path to the Python interpreter executable; overrides workspace configuration if set.",
"default": "${command:python.interpreterPath}"
},
"pythonArgs": {
Expand Down Expand Up @@ -3685,4 +3685,4 @@
"publisherDisplayName": "Microsoft",
"publisherId": "998b010b-e2af-44a5-a6cd-0b5fd3b9b6f8"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
true
);
}

public async diagnose(resource: Resource): Promise<IDiagnostic[]> {
if (!this.workspaceService.hasWorkspaceFolders) {
return [];
Expand All @@ -80,9 +81,11 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
: this.workspaceService.workspaceFolders![0];
return this.diagnoseWorkspace(workspaceFolder, resource);
}

protected async onHandle(diagnostics: IDiagnostic[]): Promise<void> {
diagnostics.forEach((diagnostic) => this.handleDiagnostic(diagnostic));
}

protected async fixLaunchJson(code: DiagnosticCodes) {
if (!this.workspaceService.hasWorkspaceFolders) {
return;
Expand All @@ -94,6 +97,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
)
);
}

private async diagnoseWorkspace(workspaceFolder: WorkspaceFolder, resource: Resource) {
const launchJson = this.getLaunchJsonFile(workspaceFolder);
if (!(await this.fs.fileExists(launchJson))) {
Expand All @@ -114,6 +118,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
diagnostics.push(new InvalidLaunchJsonDebuggerDiagnostic(DiagnosticCodes.ConsoleTypeDiagnostic, resource));
}
if (
fileContents.indexOf('"pythonPath":') > 0 ||
fileContents.indexOf('{config:python.pythonPath}') > 0 ||
fileContents.indexOf('{config:python.interpreterPath}') > 0
) {
Expand All @@ -123,6 +128,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
}
return diagnostics;
}

private async handleDiagnostic(diagnostic: IDiagnostic): Promise<void> {
if (!this.canHandle(diagnostic)) {
return;
Expand All @@ -147,6 +153,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {

await this.messageService.handle(diagnostic, { commandPrompts });
}

private async fixLaunchJsonInWorkspace(code: DiagnosticCodes, workspaceFolder: WorkspaceFolder) {
if ((await this.diagnoseWorkspace(workspaceFolder, undefined)).length === 0) {
return;
Expand All @@ -169,6 +176,7 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {
break;
}
case DiagnosticCodes.ConfigPythonPathDiagnostic: {
fileContents = this.findAndReplace(fileContents, '"pythonPath":', '"python":');
fileContents = this.findAndReplace(
fileContents,
'{config:python.pythonPath}',
Expand All @@ -188,10 +196,12 @@ export class InvalidLaunchJsonDebuggerService extends BaseDiagnosticsService {

await this.fs.writeFile(launchJson, fileContents);
}

private findAndReplace(fileContents: string, search: string, replace: string) {
const searchRegex = new RegExp(search, 'g');
return fileContents.replace(searchRegex, replace);
}

private getLaunchJsonFile(workspaceFolder: WorkspaceFolder) {
return path.join(workspaceFolder.uri.fsPath, '.vscode', 'launch.json');
}
Expand Down
10 changes: 7 additions & 3 deletions src/client/debugger/extension/adapter/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac
@inject(IInterpreterService) private readonly interpreterService: IInterpreterService,
@inject(IApplicationShell) private readonly appShell: IApplicationShell
) {}

public async createDebugAdapterDescriptor(
session: DebugSession,
_executable: DebugAdapterExecutable | undefined
Expand Down Expand Up @@ -54,7 +55,7 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac
}
}

const pythonPath = await this.getPythonPath(configuration, session.workspaceFolder);
const pythonPath = await this.getDebugAdapterPython(configuration, session.workspaceFolder);
if (pythonPath.length !== 0) {
if (configuration.request === 'attach' && configuration.processId !== undefined) {
sendTelemetryEvent(EventName.DEBUGGER_ATTACH_TO_LOCAL_PROCESS);
Expand Down Expand Up @@ -96,13 +97,16 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac
* @returns {Promise<string>} Path to the python interpreter for this workspace.
* @memberof DebugAdapterDescriptorFactory
*/
private async getPythonPath(
private async getDebugAdapterPython(
configuration: LaunchRequestArguments | AttachRequestArguments,
workspaceFolder?: WorkspaceFolder
): Promise<string> {
if (configuration.pythonPath) {
if (configuration.debugAdapterPython !== undefined) {
return configuration.debugAdapterPython;
} else if (configuration.pythonPath) {
return configuration.pythonPath;
}

const resourceUri = workspaceFolder ? workspaceFolder.uri : undefined;
const interpreter = await this.interpreterService.getActiveInterpreter(resourceUri);
if (interpreter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class PythonDebugConfigurationService implements IDebugConfigurationServi
private readonly providerFactory: IDebugConfigurationProviderFactory,
@inject(IMultiStepInputFactory) private readonly multiStepFactory: IMultiStepInputFactory
) {}

public async provideDebugConfigurations(
folder: WorkspaceFolder | undefined,
token?: CancellationToken
Expand All @@ -46,6 +47,7 @@ export class PythonDebugConfigurationService implements IDebugConfigurationServi
return [state.config as DebugConfiguration];
}
}

public async resolveDebugConfiguration(
folder: WorkspaceFolder | undefined,
debugConfiguration: DebugConfiguration,
Expand Down Expand Up @@ -76,6 +78,18 @@ export class PythonDebugConfigurationService implements IDebugConfigurationServi
);
}
}

public async resolveDebugConfigurationWithSubstitutedVariables(
folder: WorkspaceFolder | undefined,
debugConfiguration: DebugConfiguration,
token?: CancellationToken
): Promise<DebugConfiguration | undefined> {
function resolve<T extends DebugConfiguration>(resolver: IDebugConfigurationResolver<T>) {
return resolver.resolveDebugConfigurationWithSubstitutedVariables(folder, debugConfiguration as T, token);
}
return debugConfiguration.request === 'attach' ? resolve(this.attachResolver) : resolve(this.launchResolver);
}

protected async pickDebugConfiguration(
input: IMultiStepInput<DebugConfigurationState>,
state: DebugConfigurationState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export class AttachConfigurationResolver extends BaseConfigurationResolver<Attac
) {
super(workspaceService, documentManager, platformService, configurationService);
}
public async resolveDebugConfiguration(

public async resolveDebugConfigurationWithSubstitutedVariables(
folder: WorkspaceFolder | undefined,
debugConfiguration: AttachRequestArguments,
_token?: CancellationToken
Expand All @@ -38,6 +39,7 @@ export class AttachConfigurationResolver extends BaseConfigurationResolver<Attac
}
return debugConfiguration;
}

// tslint:disable-next-line:cyclomatic-complexity
protected async provideAttachDefaults(
workspaceFolder: Uri | undefined,
Expand Down
30 changes: 29 additions & 1 deletion src/client/debugger/extension/configuration/resolvers/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,36 @@ import { IDebugConfigurationResolver } from '../types';
export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
implements IDebugConfigurationResolver<T> {
protected pythonPathSource: PythonPathSource = PythonPathSource.launchJson;

constructor(
protected readonly workspaceService: IWorkspaceService,
protected readonly documentManager: IDocumentManager,
protected readonly platformService: IPlatformService,
protected readonly configurationService: IConfigurationService
) {}
public abstract resolveDebugConfiguration(

// This is a legacy hook used solely for backwards-compatible manual substitution
// of ${command:python.interpreterPath} in "pythonPath", for the sake of other
// existing implementations of resolveDebugConfiguration() that may rely on it.
//
// For all future config variables, expansion should be performed by VSCode itself,
// and validation of debug configuration in derived classes should be performed in
// resolveDebugConfigurationWithSubstitutedVariables() instead, where all variables
// are already substituted.
public async resolveDebugConfiguration(
_folder: WorkspaceFolder | undefined,
debugConfiguration: DebugConfiguration,
_token?: CancellationToken
): Promise<T | undefined> {
return debugConfiguration as T;
}

public abstract resolveDebugConfigurationWithSubstitutedVariables(
folder: WorkspaceFolder | undefined,
debugConfiguration: DebugConfiguration,
token?: CancellationToken
): Promise<T | undefined>;

protected getWorkspaceFolder(folder: WorkspaceFolder | undefined): Uri | undefined {
if (folder) {
return folder.uri;
Expand All @@ -56,19 +75,22 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
}
}
}

protected getProgram(): string | undefined {
const editor = this.documentManager.activeTextEditor;
if (editor && editor.document.languageId === PYTHON_LANGUAGE) {
return editor.document.fileName;
}
}

protected resolveAndUpdatePaths(
workspaceFolder: Uri | undefined,
debugConfiguration: LaunchRequestArguments
): void {
this.resolveAndUpdateEnvFilePath(workspaceFolder, debugConfiguration);
this.resolveAndUpdatePythonPath(workspaceFolder, debugConfiguration);
}

protected resolveAndUpdateEnvFilePath(
workspaceFolder: Uri | undefined,
debugConfiguration: LaunchRequestArguments
Expand All @@ -84,6 +106,7 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
debugConfiguration.envFile = systemVariables.resolveAny(debugConfiguration.envFile);
}
}

protected resolveAndUpdatePythonPath(
workspaceFolder: Uri | undefined,
debugConfiguration: LaunchRequestArguments
Expand All @@ -99,16 +122,19 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
this.pythonPathSource = PythonPathSource.launchJson;
}
}

protected debugOption(debugOptions: DebugOptions[], debugOption: DebugOptions) {
if (debugOptions.indexOf(debugOption) >= 0) {
return;
}
debugOptions.push(debugOption);
}

protected isLocalHost(hostName?: string) {
const LocalHosts = ['localhost', '127.0.0.1', '::1'];
return hostName && LocalHosts.indexOf(hostName.toLowerCase()) >= 0 ? true : false;
}

protected fixUpPathMappings(
pathMappings: PathMapping[],
defaultLocalRoot?: string,
Expand Down Expand Up @@ -153,9 +179,11 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>

return pathMappings;
}

protected isDebuggingFlask(debugConfiguration: Partial<LaunchRequestArguments & AttachRequestArguments>) {
return debugConfiguration.module && debugConfiguration.module.toUpperCase() === 'FLASK' ? true : false;
}

protected sendTelemetry(
trigger: 'launch' | 'attach' | 'test',
debugConfiguration: Partial<LaunchRequestArguments & AttachRequestArguments>
Expand Down
76 changes: 53 additions & 23 deletions src/client/debugger/extension/configuration/resolvers/launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,47 +29,69 @@ export class LaunchConfigurationResolver extends BaseConfigurationResolver<Launc
) {
super(workspaceService, documentManager, platformService, configurationService);
}

public async resolveDebugConfiguration(
folder: WorkspaceFolder | undefined,
debugConfiguration: LaunchRequestArguments,
_token?: CancellationToken
): Promise<LaunchRequestArguments | undefined> {
const workspaceFolder = this.getWorkspaceFolder(folder);

const config = debugConfiguration as LaunchRequestArguments;
const numberOfSettings = Object.keys(config);

if ((config.noDebug === true && numberOfSettings.length === 1) || numberOfSettings.length === 0) {
if (
debugConfiguration.name === undefined &&
debugConfiguration.type === undefined &&
debugConfiguration.request === undefined &&
debugConfiguration.program === undefined &&
debugConfiguration.env === undefined
) {
const defaultProgram = this.getProgram();

config.name = 'Launch';
config.type = DebuggerTypeName;
config.request = 'launch';
config.program = defaultProgram ? defaultProgram : '';
config.env = {};
debugConfiguration.name = 'Launch';
debugConfiguration.type = DebuggerTypeName;
debugConfiguration.request = 'launch';
debugConfiguration.program = defaultProgram ?? '';
debugConfiguration.env = {};
}

await this.provideLaunchDefaults(workspaceFolder, config);
const workspaceFolder = this.getWorkspaceFolder(folder);
this.resolveAndUpdatePaths(workspaceFolder, debugConfiguration);
return debugConfiguration;
}

const isValid = await this.validateLaunchConfiguration(folder, config);
public async resolveDebugConfigurationWithSubstitutedVariables(
folder: WorkspaceFolder | undefined,
debugConfiguration: LaunchRequestArguments,
_token?: CancellationToken
): Promise<LaunchRequestArguments | undefined> {
const workspaceFolder = this.getWorkspaceFolder(folder);
await this.provideLaunchDefaults(workspaceFolder, debugConfiguration);

const isValid = await this.validateLaunchConfiguration(folder, debugConfiguration);
if (!isValid) {
return;
}

const dbgConfig = debugConfiguration;
if (Array.isArray(dbgConfig.debugOptions)) {
dbgConfig.debugOptions = dbgConfig.debugOptions!.filter(
(item, pos) => dbgConfig.debugOptions!.indexOf(item) === pos
if (Array.isArray(debugConfiguration.debugOptions)) {
debugConfiguration.debugOptions = debugConfiguration.debugOptions!.filter(
(item, pos) => debugConfiguration.debugOptions!.indexOf(item) === pos
);
}
return debugConfiguration;
}

// tslint:disable-next-line:cyclomatic-complexity
protected async provideLaunchDefaults(
workspaceFolder: Uri | undefined,
debugConfiguration: LaunchRequestArguments
): Promise<void> {
this.resolveAndUpdatePaths(workspaceFolder, debugConfiguration);
if (debugConfiguration.python === undefined) {
debugConfiguration.python = debugConfiguration.pythonPath;
}
if (debugConfiguration.debugAdapterPython === undefined) {
debugConfiguration.debugAdapterPython = debugConfiguration.pythonPath;
}
if (debugConfiguration.debugLauncherPython === undefined) {
debugConfiguration.debugLauncherPython = debugConfiguration.pythonPath;
}
delete debugConfiguration.pythonPath;

if (typeof debugConfiguration.cwd !== 'string' && workspaceFolder) {
debugConfiguration.cwd = workspaceFolder.fsPath;
}
Expand Down Expand Up @@ -160,10 +182,18 @@ export class LaunchConfigurationResolver extends BaseConfigurationResolver<Launc
debugConfiguration: LaunchRequestArguments
): Promise<boolean> {
const diagnosticService = this.invalidPythonPathInDebuggerService;
return diagnosticService.validatePythonPath(
debugConfiguration.pythonPath,
this.pythonPathSource,
folder ? folder.uri : undefined
return (
diagnosticService.validatePythonPath(debugConfiguration.python, this.pythonPathSource, folder?.uri) &&
diagnosticService.validatePythonPath(
debugConfiguration.debugAdapterPython,
this.pythonPathSource,
folder?.uri
) &&
diagnosticService.validatePythonPath(
debugConfiguration.debugLauncherPython,
this.pythonPathSource,
folder?.uri
)
);
}
}
Loading