Skip to content

Commit

Permalink
Remove DI from debug configuration providers (microsoft/vscode-python…
Browse files Browse the repository at this point in the history
  • Loading branch information
paulacamargo25 authored and wesm committed Mar 28, 2024
1 parent 7383a86 commit 3f8f2f1
Show file tree
Hide file tree
Showing 27 changed files with 600 additions and 914 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,22 @@ import { cloneDeep } from 'lodash';
import { CancellationToken, DebugConfiguration, QuickPickItem, WorkspaceFolder } from 'vscode';
import { DebugConfigStrings } from '../../../common/utils/localize';
import {
IMultiStepInput,
IMultiStepInputFactory,
InputStep,
IQuickPickParameters,
MultiStepInput,
} from '../../../common/utils/multiStepInput';
import { AttachRequestArguments, DebugConfigurationArguments, LaunchRequestArguments } from '../../types';
import { DebugConfigurationState, DebugConfigurationType, IDebugConfigurationService } from '../types';
import { IDebugConfigurationProviderFactory, IDebugConfigurationResolver } from './types';
import { buildDjangoLaunchDebugConfiguration } from './providers/djangoLaunch';
import { buildFastAPILaunchDebugConfiguration } from './providers/fastapiLaunch';
import { buildFileLaunchDebugConfiguration } from './providers/fileLaunch';
import { buildFlaskLaunchDebugConfiguration } from './providers/flaskLaunch';
import { buildModuleLaunchConfiguration } from './providers/moduleLaunch';
import { buildPidAttachConfiguration } from './providers/pidAttach';
import { buildPyramidLaunchConfiguration } from './providers/pyramidLaunch';
import { buildRemoteAttachConfiguration } from './providers/remoteAttach';
import { IDebugConfigurationResolver } from './types';

@injectable()
export class PythonDebugConfigurationService implements IDebugConfigurationService {
Expand All @@ -27,8 +35,6 @@ export class PythonDebugConfigurationService implements IDebugConfigurationServi
@inject(IDebugConfigurationResolver)
@named('launch')
private readonly launchResolver: IDebugConfigurationResolver<LaunchRequestArguments>,
@inject(IDebugConfigurationProviderFactory)
private readonly providerFactory: IDebugConfigurationProviderFactory,
@inject(IMultiStepInputFactory) private readonly multiStepFactory: IMultiStepInputFactory,
) {}

Expand Down Expand Up @@ -102,7 +108,7 @@ export class PythonDebugConfigurationService implements IDebugConfigurationServi
}

protected async pickDebugConfiguration(
input: IMultiStepInput<DebugConfigurationState>,
input: MultiStepInput<DebugConfigurationState>,
state: DebugConfigurationState,
): Promise<InputStep<DebugConfigurationState> | void> {
type DebugConfigurationQuickPickItem = QuickPickItem & { type: DebugConfigurationType };
Expand Down Expand Up @@ -148,6 +154,22 @@ export class PythonDebugConfigurationService implements IDebugConfigurationServi
description: DebugConfigStrings.pyramid.selectConfiguration.description,
},
];
const debugConfigurations = new Map<
DebugConfigurationType,
(
input: MultiStepInput<DebugConfigurationState>,
state: DebugConfigurationState,
) => Promise<void | InputStep<DebugConfigurationState>>
>();
debugConfigurations.set(DebugConfigurationType.launchDjango, buildDjangoLaunchDebugConfiguration);
debugConfigurations.set(DebugConfigurationType.launchFastAPI, buildFastAPILaunchDebugConfiguration);
debugConfigurations.set(DebugConfigurationType.launchFile, buildFileLaunchDebugConfiguration);
debugConfigurations.set(DebugConfigurationType.launchFlask, buildFlaskLaunchDebugConfiguration);
debugConfigurations.set(DebugConfigurationType.launchModule, buildModuleLaunchConfiguration);
debugConfigurations.set(DebugConfigurationType.pidAttach, buildPidAttachConfiguration);
debugConfigurations.set(DebugConfigurationType.remoteAttach, buildRemoteAttachConfiguration);
debugConfigurations.set(DebugConfigurationType.launchPyramid, buildPyramidLaunchConfiguration);

state.config = {};
const pick = await input.showQuickPick<
DebugConfigurationQuickPickItem,
Expand All @@ -159,8 +181,8 @@ export class PythonDebugConfigurationService implements IDebugConfigurationServi
items: items,
});
if (pick) {
const provider = this.providerFactory.create(pick.type);
return provider.buildConfiguration.bind(provider);
const pickedDebugConfiguration = debugConfigurations.get(pick.type)!;
return pickedDebugConfiguration(input, state);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,93 +3,84 @@

'use strict';

import { inject, injectable } from 'inversify';
import * as vscode from 'vscode';
import * as path from 'path';
import { Uri, WorkspaceFolder } from 'vscode';
import { IWorkspaceService } from '../../../../common/application/types';
import { IFileSystem } from '../../../../common/platform/types';
import { IPathUtils } from '../../../../common/types';
import * as fs from 'fs-extra';
import { DebugConfigStrings } from '../../../../common/utils/localize';
import { MultiStepInput } from '../../../../common/utils/multiStepInput';
import { SystemVariables } from '../../../../common/variables/systemVariables';
import { sendTelemetryEvent } from '../../../../telemetry';
import { EventName } from '../../../../telemetry/constants';
import { DebuggerTypeName } from '../../../constants';
import { LaunchRequestArguments } from '../../../types';
import { DebugConfigurationState, DebugConfigurationType, IDebugConfigurationProvider } from '../../types';
import { DebugConfigurationState, DebugConfigurationType } from '../../types';
import { resolveVariables } from '../utils/common';

const workspaceFolderToken = '${workspaceFolder}';

@injectable()
export class DjangoLaunchDebugConfigurationProvider implements IDebugConfigurationProvider {
constructor(
@inject(IFileSystem) private fs: IFileSystem,
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
@inject(IPathUtils) private pathUtils: IPathUtils,
) {}
public async buildConfiguration(input: MultiStepInput<DebugConfigurationState>, state: DebugConfigurationState) {
const program = await this.getManagePyPath(state.folder);
let manuallyEnteredAValue: boolean | undefined;
const defaultProgram = `${workspaceFolderToken}${this.pathUtils.separator}manage.py`;
const config: Partial<LaunchRequestArguments> = {
name: DebugConfigStrings.django.snippet.name,
type: DebuggerTypeName,
request: 'launch',
program: program || defaultProgram,
args: ['runserver'],
django: true,
justMyCode: true,
};
if (!program) {
const selectedProgram = await input.showInputBox({
title: DebugConfigStrings.django.enterManagePyPath.title,
value: defaultProgram,
prompt: DebugConfigStrings.django.enterManagePyPath.prompt,
validate: (value) => this.validateManagePy(state.folder, defaultProgram, value),
});
if (selectedProgram) {
manuallyEnteredAValue = true;
config.program = selectedProgram;
}
export async function buildDjangoLaunchDebugConfiguration(
input: MultiStepInput<DebugConfigurationState>,
state: DebugConfigurationState,
) {
const program = await getManagePyPath(state.folder);
let manuallyEnteredAValue: boolean | undefined;
const defaultProgram = `${workspaceFolderToken}${path.sep}manage.py`;
const config: Partial<LaunchRequestArguments> = {
name: DebugConfigStrings.django.snippet.name,
type: DebuggerTypeName,
request: 'launch',
program: program || defaultProgram,
args: ['runserver'],
django: true,
justMyCode: true,
};
if (!program) {
const selectedProgram = await input.showInputBox({
title: DebugConfigStrings.django.enterManagePyPath.title,
value: defaultProgram,
prompt: DebugConfigStrings.django.enterManagePyPath.prompt,
validate: (value) => validateManagePy(state.folder, defaultProgram, value),
});
if (selectedProgram) {
manuallyEnteredAValue = true;
config.program = selectedProgram;
}
}

sendTelemetryEvent(EventName.DEBUGGER_CONFIGURATION_PROMPTS, undefined, {
configurationType: DebugConfigurationType.launchDjango,
autoDetectedDjangoManagePyPath: !!program,
manuallyEnteredAValue,
});
Object.assign(state.config, config);
sendTelemetryEvent(EventName.DEBUGGER_CONFIGURATION_PROMPTS, undefined, {
configurationType: DebugConfigurationType.launchDjango,
autoDetectedDjangoManagePyPath: !!program,
manuallyEnteredAValue,
});

Object.assign(state.config, config);
}

export async function validateManagePy(
folder: vscode.WorkspaceFolder | undefined,
defaultValue: string,
selected?: string,
): Promise<string | undefined> {
const error = DebugConfigStrings.django.enterManagePyPath.invalid;
if (!selected || selected.trim().length === 0) {
return error;
}
public async validateManagePy(
folder: WorkspaceFolder | undefined,
defaultValue: string,
selected?: string,
): Promise<string | undefined> {
const error = DebugConfigStrings.django.enterManagePyPath.invalid;
if (!selected || selected.trim().length === 0) {
return error;
}
const resolvedPath = this.resolveVariables(selected, folder ? folder.uri : undefined);
if (selected !== defaultValue && !(await this.fs.fileExists(resolvedPath))) {
return error;
}
if (!resolvedPath.trim().toLowerCase().endsWith('.py')) {
return error;
}
return;
const resolvedPath = resolveVariables(selected, undefined, folder);

if (selected !== defaultValue && !(await fs.pathExists(resolvedPath))) {
return error;
}
protected resolveVariables(pythonPath: string, resource: Uri | undefined): string {
const systemVariables = new SystemVariables(resource, undefined, this.workspace);
return systemVariables.resolveAny(pythonPath);
if (!resolvedPath.trim().toLowerCase().endsWith('.py')) {
return error;
}
return;
}

protected async getManagePyPath(folder: WorkspaceFolder | undefined): Promise<string | undefined> {
if (!folder) {
return;
}
const defaultLocationOfManagePy = path.join(folder.uri.fsPath, 'manage.py');
if (await this.fs.fileExists(defaultLocationOfManagePy)) {
return `${workspaceFolderToken}${this.pathUtils.separator}manage.py`;
}
export async function getManagePyPath(folder: vscode.WorkspaceFolder | undefined): Promise<string | undefined> {
if (!folder) {
return;
}
const defaultLocationOfManagePy = path.join(folder.uri.fsPath, 'manage.py');
if (await fs.pathExists(defaultLocationOfManagePy)) {
return `${workspaceFolderToken}${path.sep}manage.py`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,64 @@

'use strict';

import { inject, injectable } from 'inversify';
import * as path from 'path';
import * as fs from 'fs-extra';
import { WorkspaceFolder } from 'vscode';
import { IFileSystem } from '../../../../common/platform/types';
import { DebugConfigStrings } from '../../../../common/utils/localize';
import { MultiStepInput } from '../../../../common/utils/multiStepInput';
import { sendTelemetryEvent } from '../../../../telemetry';
import { EventName } from '../../../../telemetry/constants';
import { DebuggerTypeName } from '../../../constants';
import { LaunchRequestArguments } from '../../../types';
import { DebugConfigurationState, DebugConfigurationType, IDebugConfigurationProvider } from '../../types';
import { DebugConfigurationState, DebugConfigurationType } from '../../types';

@injectable()
export class FastAPILaunchDebugConfigurationProvider implements IDebugConfigurationProvider {
constructor(@inject(IFileSystem) private fs: IFileSystem) {}
public isSupported(debugConfigurationType: DebugConfigurationType): boolean {
return debugConfigurationType === DebugConfigurationType.launchFastAPI;
}
public async buildConfiguration(input: MultiStepInput<DebugConfigurationState>, state: DebugConfigurationState) {
const application = await this.getApplicationPath(state.folder);
let manuallyEnteredAValue: boolean | undefined;
const config: Partial<LaunchRequestArguments> = {
name: DebugConfigStrings.fastapi.snippet.name,
type: DebuggerTypeName,
request: 'launch',
module: 'uvicorn',
args: ['main:app'],
jinja: true,
justMyCode: true,
};
export async function buildFastAPILaunchDebugConfiguration(
input: MultiStepInput<DebugConfigurationState>,
state: DebugConfigurationState,
) {
const application = await getApplicationPath(state.folder);
let manuallyEnteredAValue: boolean | undefined;
const config: Partial<LaunchRequestArguments> = {
name: DebugConfigStrings.fastapi.snippet.name,
type: DebuggerTypeName,
request: 'launch',
module: 'uvicorn',
args: ['main:app'],
jinja: true,
justMyCode: true,
};

if (!application) {
const selectedPath = await input.showInputBox({
title: DebugConfigStrings.fastapi.enterAppPathOrNamePath.title,
value: 'main.py',
prompt: DebugConfigStrings.fastapi.enterAppPathOrNamePath.prompt,
validate: (value) =>
Promise.resolve(
value && value.trim().length > 0
? undefined
: DebugConfigStrings.fastapi.enterAppPathOrNamePath.invalid,
),
});
if (selectedPath) {
manuallyEnteredAValue = true;
config.args = [`${path.basename(selectedPath, '.py').replace('/', '.')}:app`];
}
if (!application) {
const selectedPath = await input.showInputBox({
title: DebugConfigStrings.fastapi.enterAppPathOrNamePath.title,
value: 'main.py',
prompt: DebugConfigStrings.fastapi.enterAppPathOrNamePath.prompt,
validate: (value) =>
Promise.resolve(
value && value.trim().length > 0
? undefined
: DebugConfigStrings.fastapi.enterAppPathOrNamePath.invalid,
),
});
if (selectedPath) {
manuallyEnteredAValue = true;
config.args = [`${path.basename(selectedPath, '.py').replace('/', '.')}:app`];
}
}

sendTelemetryEvent(EventName.DEBUGGER_CONFIGURATION_PROMPTS, undefined, {
configurationType: DebugConfigurationType.launchFastAPI,
autoDetectedFastAPIMainPyPath: !!application,
manuallyEnteredAValue,
});
Object.assign(state.config, config);
sendTelemetryEvent(EventName.DEBUGGER_CONFIGURATION_PROMPTS, undefined, {
configurationType: DebugConfigurationType.launchFastAPI,
autoDetectedFastAPIMainPyPath: !!application,
manuallyEnteredAValue,
});
Object.assign(state.config, config);
}
export async function getApplicationPath(folder: WorkspaceFolder | undefined): Promise<string | undefined> {
if (!folder) {
return;
}
protected async getApplicationPath(folder: WorkspaceFolder | undefined): Promise<string | undefined> {
if (!folder) {
return;
}
const defaultLocationOfManagePy = path.join(folder.uri.fsPath, 'main.py');
if (await this.fs.fileExists(defaultLocationOfManagePy)) {
return 'main.py';
}
const defaultLocationOfManagePy = path.join(folder.uri.fsPath, 'main.py');
if (await fs.pathExists(defaultLocationOfManagePy)) {
return 'main.py';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,28 @@

'use strict';

import { injectable } from 'inversify';
import { DebugConfigStrings } from '../../../../common/utils/localize';
import { MultiStepInput } from '../../../../common/utils/multiStepInput';
import { captureTelemetry } from '../../../../telemetry';
import { sendTelemetryEvent } from '../../../../telemetry';
import { EventName } from '../../../../telemetry/constants';
import { DebuggerTypeName } from '../../../constants';
import { LaunchRequestArguments } from '../../../types';
import { DebugConfigurationState, DebugConfigurationType, IDebugConfigurationProvider } from '../../types';
import { DebugConfigurationState, DebugConfigurationType } from '../../types';

@injectable()
export class FileLaunchDebugConfigurationProvider implements IDebugConfigurationProvider {
@captureTelemetry(
EventName.DEBUGGER_CONFIGURATION_PROMPTS,
{ configurationType: DebugConfigurationType.launchFile },
false,
)
public async buildConfiguration(_input: MultiStepInput<DebugConfigurationState>, state: DebugConfigurationState) {
const config: Partial<LaunchRequestArguments> = {
name: DebugConfigStrings.file.snippet.name,
type: DebuggerTypeName,
request: 'launch',
program: '${file}',
console: 'integratedTerminal',
justMyCode: true,
};
Object.assign(state.config, config);
}
export async function buildFileLaunchDebugConfiguration(
_input: MultiStepInput<DebugConfigurationState>,
state: DebugConfigurationState,
) {
const config: Partial<LaunchRequestArguments> = {
name: DebugConfigStrings.file.snippet.name,
type: DebuggerTypeName,
request: 'launch',
program: '${file}',
console: 'integratedTerminal',
justMyCode: true,
};
sendTelemetryEvent(EventName.DEBUGGER_CONFIGURATION_PROMPTS, undefined, {
configurationType: DebugConfigurationType.launchFastAPI,
});
Object.assign(state.config, config);
}
Loading

0 comments on commit 3f8f2f1

Please sign in to comment.