Skip to content

Commit

Permalink
Fix debugging of pyramid applications
Browse files Browse the repository at this point in the history
Fixes #1467
Fixes #737
  • Loading branch information
DonJayamanne authored Apr 26, 2018
1 parent 74c9702 commit 849009b
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 39 deletions.
1 change: 1 addition & 0 deletions news/2 Fixes/737.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix debugging of Pyramid applications on Windows.
23 changes: 6 additions & 17 deletions src/client/debugger/configProviders/baseProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@

import { injectable, unmanaged } from 'inversify';
import * as path from 'path';
import { CancellationToken, DebugConfiguration, DebugConfigurationProvider, ProviderResult, Uri, WorkspaceFolder } from 'vscode';
import { CancellationToken, DebugConfiguration, DebugConfigurationProvider, Uri, WorkspaceFolder } from 'vscode';
import { IDocumentManager, IWorkspaceService } from '../../common/application/types';
import { PythonLanguage } from '../../common/constants';
import { IFileSystem, IPlatformService } from '../../common/platform/types';
import { IConfigurationService } from '../../common/types';
import { IServiceContainer } from '../../ioc/types';
import { BaseAttachRequestArguments, BaseLaunchRequestArguments, DebuggerType, DebugOptions } from '../Common/Contracts';
Expand All @@ -21,11 +20,11 @@ export type PythonAttachDebugConfiguration<T extends BaseAttachRequestArguments>
@injectable()
export abstract class BaseConfigurationProvider<L extends BaseLaunchRequestArguments, A extends BaseAttachRequestArguments> implements DebugConfigurationProvider {
constructor(@unmanaged() public debugType: DebuggerType, protected serviceContainer: IServiceContainer) { }
public resolveDebugConfiguration(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugConfiguration> {
public async resolveDebugConfiguration(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): Promise<DebugConfiguration> {
const workspaceFolder = this.getWorkspaceFolder(folder);

if (debugConfiguration.request === 'attach') {
this.provideAttachDefaults(workspaceFolder, debugConfiguration as PythonAttachDebugConfiguration<A>);
await this.provideAttachDefaults(workspaceFolder, debugConfiguration as PythonAttachDebugConfiguration<A>);
} else {
const config = debugConfiguration as PythonLaunchDebugConfiguration<L>;
const numberOfSettings = Object.keys(config);
Expand All @@ -40,7 +39,7 @@ export abstract class BaseConfigurationProvider<L extends BaseLaunchRequestArgum
config.env = {};
}

this.provideLaunchDefaults(workspaceFolder, config);
await this.provideLaunchDefaults(workspaceFolder, config);
}

const dbgConfig = (debugConfiguration as (BaseLaunchRequestArguments | BaseAttachRequestArguments));
Expand All @@ -49,15 +48,15 @@ export abstract class BaseConfigurationProvider<L extends BaseLaunchRequestArgum
}
return debugConfiguration;
}
protected provideAttachDefaults(workspaceFolder: Uri | undefined, debugConfiguration: PythonAttachDebugConfiguration<A>): void {
protected async provideAttachDefaults(workspaceFolder: Uri | undefined, debugConfiguration: PythonAttachDebugConfiguration<A>): Promise<void> {
if (!Array.isArray(debugConfiguration.debugOptions)) {
debugConfiguration.debugOptions = [];
}
if (!debugConfiguration.host) {
debugConfiguration.host = 'localhost';
}
}
protected provideLaunchDefaults(workspaceFolder: Uri | undefined, debugConfiguration: PythonLaunchDebugConfiguration<L>): void {
protected async provideLaunchDefaults(workspaceFolder: Uri | undefined, debugConfiguration: PythonLaunchDebugConfiguration<L>): Promise<void> {
this.resolveAndUpdatePythonPath(workspaceFolder, debugConfiguration);
if (typeof debugConfiguration.cwd !== 'string' && workspaceFolder) {
debugConfiguration.cwd = workspaceFolder.fsPath;
Expand All @@ -83,16 +82,6 @@ export abstract class BaseConfigurationProvider<L extends BaseLaunchRequestArgum
if (debugConfiguration.debugOptions.indexOf(DebugOptions.RedirectOutput) === -1) {
debugConfiguration.debugOptions.push(DebugOptions.RedirectOutput);
}
if (debugConfiguration.debugOptions.indexOf(DebugOptions.Pyramid) >= 0) {
const platformService = this.serviceContainer.get<IPlatformService>(IPlatformService);
const fs = this.serviceContainer.get<IFileSystem>(IFileSystem);
const pserve = platformService.isWindows ? 'pserve.exe' : 'pserve';
if (fs.fileExistsSync(debugConfiguration.pythonPath)) {
debugConfiguration.program = path.join(path.dirname(debugConfiguration.pythonPath), pserve);
} else {
debugConfiguration.program = pserve;
}
}
}
private getWorkspaceFolder(folder: WorkspaceFolder | undefined): Uri | undefined {
if (folder) {
Expand Down
39 changes: 39 additions & 0 deletions src/client/debugger/configProviders/configurationProviderUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

import { inject, injectable } from 'inversify';
import * as path from 'path';
import { Uri } from 'vscode';
import { IApplicationShell } from '../../common/application/types';
import { IFileSystem } from '../../common/platform/types';
import { IPythonExecutionFactory } from '../../common/process/types';
import { IServiceContainer } from '../../ioc/types';
import { IConfigurationProviderUtils } from './types';

const PSERVE_SCRIPT_FILE_NAME = 'pserve.py';

@injectable()
export class ConfigurationProviderUtils implements IConfigurationProviderUtils {
private readonly executionFactory: IPythonExecutionFactory;
private readonly fs: IFileSystem;
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
this.executionFactory = this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);
this.fs = this.serviceContainer.get<IFileSystem>(IFileSystem);
}
public async getPyramidStartupScriptFilePath(resource?: Uri): Promise<string | undefined> {
try {
const executionService = await this.executionFactory.create(resource);
const output = await executionService.exec(['-c', 'import pyramid;print(pyramid.__file__)'], { throwOnStdErr: true });
const pserveFilePath = path.join(path.dirname(output.stdout.trim()), 'scripts', PSERVE_SCRIPT_FILE_NAME);
return await this.fs.fileExistsAsync(pserveFilePath) ? pserveFilePath : undefined;
} catch (ex) {
const message = 'Unable to locate \'pserve.py\' required for debugging of Pyramid applications.';
console.error(message, ex);
const app = this.serviceContainer.get<IApplicationShell>(IApplicationShell);
app.showErrorMessage(message);
return;
}
}
}
14 changes: 11 additions & 3 deletions src/client/debugger/configProviders/pythonProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ import { inject, injectable } from 'inversify';
import { Uri } from 'vscode';
import { IServiceContainer } from '../../ioc/types';
import { AttachRequestArgumentsV1, DebugOptions, LaunchRequestArgumentsV1 } from '../Common/Contracts';
import { BaseConfigurationProvider, PythonAttachDebugConfiguration } from './baseProvider';
import { BaseConfigurationProvider, PythonAttachDebugConfiguration, PythonLaunchDebugConfiguration } from './baseProvider';
import { IConfigurationProviderUtils } from './types';

@injectable()
export class PythonDebugConfigurationProvider extends BaseConfigurationProvider<LaunchRequestArgumentsV1, AttachRequestArgumentsV1> {
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
super('python', serviceContainer);
}
protected provideAttachDefaults(workspaceFolder: Uri | undefined, debugConfiguration: PythonAttachDebugConfiguration<AttachRequestArgumentsV1>): void {
super.provideAttachDefaults(workspaceFolder, debugConfiguration);
protected async provideLaunchDefaults(workspaceFolder: Uri, debugConfiguration: PythonLaunchDebugConfiguration<LaunchRequestArgumentsV1>): Promise<void> {
await super.provideLaunchDefaults(workspaceFolder, debugConfiguration);
if (debugConfiguration.debugOptions!.indexOf(DebugOptions.Pyramid) >= 0) {
const utils = this.serviceContainer.get<IConfigurationProviderUtils>(IConfigurationProviderUtils);
debugConfiguration.program = (await utils.getPyramidStartupScriptFilePath(workspaceFolder))!;
}
}
protected async provideAttachDefaults(workspaceFolder: Uri | undefined, debugConfiguration: PythonAttachDebugConfiguration<AttachRequestArgumentsV1>): Promise<void> {
await super.provideAttachDefaults(workspaceFolder, debugConfiguration);
const debugOptions = debugConfiguration.debugOptions!;
// Always redirect output.
if (debugOptions.indexOf(DebugOptions.RedirectOutput) === -1) {
Expand Down
13 changes: 9 additions & 4 deletions src/client/debugger/configProviders/pythonV2Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import { IPlatformService } from '../../common/platform/types';
import { IServiceContainer } from '../../ioc/types';
import { AttachRequestArguments, DebugOptions, LaunchRequestArguments } from '../Common/Contracts';
import { BaseConfigurationProvider, PythonAttachDebugConfiguration, PythonLaunchDebugConfiguration } from './baseProvider';
import { IConfigurationProviderUtils } from './types';

@injectable()
export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvider<LaunchRequestArguments, AttachRequestArguments> {
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
super('pythonExperimental', serviceContainer);
}
protected provideLaunchDefaults(workspaceFolder: Uri, debugConfiguration: PythonLaunchDebugConfiguration<LaunchRequestArguments>): void {
super.provideLaunchDefaults(workspaceFolder, debugConfiguration);
protected async provideLaunchDefaults(workspaceFolder: Uri, debugConfiguration: PythonLaunchDebugConfiguration<LaunchRequestArguments>): Promise<void> {
await super.provideLaunchDefaults(workspaceFolder, debugConfiguration);
const debugOptions = debugConfiguration.debugOptions!;
if (debugConfiguration.debugStdLib) {
this.debugOption(debugOptions, DebugOptions.DebugStdLib);
Expand All @@ -41,9 +42,13 @@ export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvide
&& debugConfiguration.jinja !== false) {
this.debugOption(debugOptions, DebugOptions.Jinja);
}
if (debugConfiguration.pyramid) {
const utils = this.serviceContainer.get<IConfigurationProviderUtils>(IConfigurationProviderUtils);
debugConfiguration.program = (await utils.getPyramidStartupScriptFilePath(workspaceFolder))!;
}
}
protected provideAttachDefaults(workspaceFolder: Uri, debugConfiguration: PythonAttachDebugConfiguration<AttachRequestArguments>): void {
super.provideAttachDefaults(workspaceFolder, debugConfiguration);
protected async provideAttachDefaults(workspaceFolder: Uri, debugConfiguration: PythonAttachDebugConfiguration<AttachRequestArguments>): Promise<void> {
await super.provideAttachDefaults(workspaceFolder, debugConfiguration);
const debugOptions = debugConfiguration.debugOptions!;
if (debugConfiguration.debugStdLib) {
this.debugOption(debugOptions, DebugOptions.DebugStdLib);
Expand Down
3 changes: 3 additions & 0 deletions src/client/debugger/configProviders/serviceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import { DebugConfigurationProvider } from 'vscode';
import { PythonDebugConfigurationProvider, PythonV2DebugConfigurationProvider } from '..';
import { IServiceManager } from '../../ioc/types';
import { IDebugConfigurationProvider } from '../types';
import { ConfigurationProviderUtils } from './configurationProviderUtils';
import { IConfigurationProviderUtils } from './types';

export function registerTypes(serviceManager: IServiceManager) {
serviceManager.addSingleton<DebugConfigurationProvider>(IDebugConfigurationProvider, PythonDebugConfigurationProvider);
serviceManager.addSingleton<DebugConfigurationProvider>(IDebugConfigurationProvider, PythonV2DebugConfigurationProvider);
serviceManager.addSingleton<IConfigurationProviderUtils>(IConfigurationProviderUtils, ConfigurationProviderUtils);
}
12 changes: 12 additions & 0 deletions src/client/debugger/configProviders/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

import { Uri } from 'vscode';

export const IConfigurationProviderUtils = Symbol('IConfigurationProviderUtils');

export interface IConfigurationProviderUtils {
getPyramidStartupScriptFilePath(resource?: Uri): Promise<string | undefined>;
}
Loading

0 comments on commit 849009b

Please sign in to comment.