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
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# Changelog

## 2019.5.3 (5 June 2019)
## 2019.5.4 (6 June 2019)

### Fixes

1. Disable quoting of paths sent to the debugger as arguments.
([#5861](https://github.com/microsoft/vscode-python/issues/5861))
1. Fixes to activation of Conda environments.
([#5929](https://github.com/microsoft/vscode-python/issues/5929))

## 2019.5.18678 (5 June 2019)

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "python",
"displayName": "Python",
"description": "Linting, Debugging (multi-threaded, remote), Intellisense, code formatting, refactoring, unit tests, snippets, and more.",
"version": "2019.5.3",
"version": "2019.5.4",
"languageServerVersion": "0.2.82",
"publisher": "ms-python",
"author": {
Expand Down
4 changes: 2 additions & 2 deletions src/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
'use strict';

import { traceError } from './common/logger';
import { RemoteDebuggerLauncherScriptProvider } from './debugger/debugAdapter/DebugClients/launcherProvider';
import { RemoteDebuggerExternalLauncherScriptProvider } from './debugger/debugAdapter/DebugClients/launcherProvider';

/*
* Do not introduce any breaking changes to this API.
Expand Down Expand Up @@ -42,7 +42,7 @@ export function buildApi(ready: Promise<any>) {
}),
debug: {
async getRemoteLauncherCommand(host: string, port: number, waitUntilDebuggerAttaches: boolean = true): Promise<string[]> {
return new RemoteDebuggerLauncherScriptProvider().getLauncherArgs({ host, port, waitUntilDebuggerAttaches });
return new RemoteDebuggerExternalLauncherScriptProvider().getLauncherArgs({ host, port, waitUntilDebuggerAttaches });
}
}
};
Expand Down
8 changes: 2 additions & 6 deletions src/client/common/terminal/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,8 @@ export class TerminalHelper implements ITerminalHelper {
this.sendTelemetry(resource, terminalShellType, EventName.PYTHON_INTERPRETER_ACTIVATION_FOR_TERMINAL, promise).ignoreErrors();
return promise;
}
public async getEnvironmentActivationShellCommands(resource: Resource, interpreter?: PythonInterpreter): Promise<string[] | undefined> {
if (this.platform.osType === OSType.Unknown){
return;
}
const shell = this.shellDetector.identifyTerminalShell();
if (!shell) {
public async getEnvironmentActivationShellCommands(resource: Resource, shell: TerminalShellType, interpreter?: PythonInterpreter): Promise<string[] | undefined> {
if (this.platform.osType === OSType.Unknown) {
return;
}
const providers = [this.bashCShellFish, this.commandPromptAndPowerShell];
Expand Down
2 changes: 1 addition & 1 deletion src/client/common/terminal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export interface ITerminalHelper {
identifyTerminalShell(terminal?: Terminal): TerminalShellType;
buildCommandForTerminal(terminalShellType: TerminalShellType, command: string, args: string[]): string;
getEnvironmentActivationCommands(terminalShellType: TerminalShellType, resource?: Uri): Promise<string[] | undefined>;
getEnvironmentActivationShellCommands(resource: Resource, interpreter?: PythonInterpreter): Promise<string[] | undefined>;
getEnvironmentActivationShellCommands(resource: Resource, shell: TerminalShellType, interpreter?: PythonInterpreter): Promise<string[] | undefined>;
}

export const ITerminalActivator = Symbol('ITerminalActivator');
Expand Down
13 changes: 10 additions & 3 deletions src/client/debugger/debugAdapter/DebugClients/launcherProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,26 @@ export class NoDebugLauncherScriptProvider implements IDebugLauncherScriptProvid
constructor(@optional() private script: string = pathToScript) { }
public getLauncherArgs(options: LocalDebugOptions): string[] {
const customDebugger = options.customDebugger ? '--custom' : '--default';
return [this.script.fileToCommandArgument(), customDebugger, '--nodebug', '--client', '--host', options.host, '--port', options.port.toString()];
return [this.script, customDebugger, '--nodebug', '--client', '--host', options.host, '--port', options.port.toString()];
}
}

export class DebuggerLauncherScriptProvider implements IDebugLauncherScriptProvider<LocalDebugOptions> {
constructor(@optional() private script: string = pathToScript) { }
public getLauncherArgs(options: LocalDebugOptions): string[] {
const customDebugger = options.customDebugger ? '--custom' : '--default';
return [this.script.fileToCommandArgument(), customDebugger, '--client', '--host', options.host, '--port', options.port.toString()];
return [this.script, customDebugger, '--client', '--host', options.host, '--port', options.port.toString()];
}
}

export class RemoteDebuggerLauncherScriptProvider implements IRemoteDebugLauncherScriptProvider {
/**
* This class is used to provide the launch scripts so external code can launch the debugger.
* As we're passing command arguments, we need to ensure the file paths are quoted.
* @export
* @class RemoteDebuggerExternalLauncherScriptProvider
* @implements {IRemoteDebugLauncherScriptProvider}
*/
export class RemoteDebuggerExternalLauncherScriptProvider implements IRemoteDebugLauncherScriptProvider {
constructor(@optional() private script: string = pathToScript) { }
public getLauncherArgs(options: RemoteDebugOptions): string[] {
const waitArgs = options.waitUntilDebuggerAttaches ? ['--wait'] : [];
Expand Down
18 changes: 9 additions & 9 deletions src/client/interpreter/activation/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as path from 'path';
import { LogOptions, traceDecorators, traceError, traceVerbose } from '../../common/logger';
import { IPlatformService } from '../../common/platform/types';
import { IProcessServiceFactory } from '../../common/process/types';
import { ITerminalHelper } from '../../common/terminal/types';
import { ITerminalHelper, TerminalShellType } from '../../common/terminal/types';
import { ICurrentProcess, IDisposable, Resource } from '../../common/types';
import {
cacheResourceSpecificInterpreterData,
Expand All @@ -29,9 +29,9 @@ const getEnvironmentTimeout = 30000;

// The shell under which we'll execute activation scripts.
const defaultShells = {
[OSType.Windows]: 'cmd',
[OSType.OSX]: 'bash',
[OSType.Linux]: 'bash',
[OSType.Windows]: { shell: 'cmd', shellType: TerminalShellType.commandPrompt },
[OSType.OSX]: { shell: 'bash', shellType: TerminalShellType.bash },
[OSType.Linux]: { shell: 'bash', shellType: TerminalShellType.bash },
[OSType.Unknown]: undefined
};

Expand All @@ -54,14 +54,14 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi
@captureTelemetry(EventName.PYTHON_INTERPRETER_ACTIVATION_ENVIRONMENT_VARIABLES, { failed: false }, true)
@cacheResourceSpecificInterpreterData('ActivatedEnvironmentVariables', cacheDuration)
public async getActivatedEnvironmentVariables(resource: Resource, interpreter?: PythonInterpreter, allowExceptions?: boolean): Promise<NodeJS.ProcessEnv | undefined> {
const shell = defaultShells[this.platform.osType];
if (!shell) {
const shellInfo = defaultShells[this.platform.osType];
if (!shellInfo) {
return;
}

try {
const activationCommands = await this.helper.getEnvironmentActivationShellCommands(resource, interpreter);
traceVerbose(`Activation Commands received ${activationCommands}`);
const activationCommands = await this.helper.getEnvironmentActivationShellCommands(resource, shellInfo.shellType, interpreter);
traceVerbose(`Activation Commands received ${activationCommands} for shell ${shellInfo.shell}`);
if (!activationCommands || !Array.isArray(activationCommands) || activationCommands.length === 0) {
return;
}
Expand All @@ -84,7 +84,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi
// See the discussion from hidesoon in this issue: https://github.com/Microsoft/vscode-python/issues/4424
// His issue is conda never finishing during activate. This is a conda issue, but we
// should at least tell the user.
const result = await processService.shellExec(command, { env, shell, timeout: getEnvironmentTimeout, maxBuffer: 1000 * 1000 });
const result = await processService.shellExec(command, { env, shell: shellInfo.shell, timeout: getEnvironmentTimeout, maxBuffer: 1000 * 1000 });
if (result.stderr && result.stderr.length > 0) {
throw new Error(`StdErr from ShellExec, ${result.stderr}`);
}
Expand Down
9 changes: 5 additions & 4 deletions src/test/common/terminals/helper.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,10 @@ suite('Terminal Service helpers', () => {
test('Activation command for Shell must be empty for unknown os', async () => {
when(platformService.osType).thenReturn(OSType.Unknown);

const cmd = await helper.getEnvironmentActivationShellCommands(resource, interpreter);

expect(cmd).to.equal(undefined, 'Command must be undefined');
for (const item of getNamesAndValues<TerminalShellType>(TerminalShellType)) {
const cmd = await helper.getEnvironmentActivationShellCommands(resource, item.value, interpreter);
expect(cmd).to.equal(undefined, 'Command must be undefined');
}
});
});
[undefined, pythonInterpreter].forEach(interpreter => {
Expand All @@ -332,7 +333,7 @@ suite('Terminal Service helpers', () => {
when(bashActivationProvider.isShellSupported(shellToExpect)).thenReturn(false);
when(cmdActivationProvider.isShellSupported(shellToExpect)).thenReturn(false);

const cmd = await helper.getEnvironmentActivationShellCommands(resource, interpreter);
const cmd = await helper.getEnvironmentActivationShellCommands(resource, shellToExpect, interpreter);

expect(cmd).to.equal(undefined, 'Command must be undefined');
verify(pythonSettings.terminal).once();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { expect } from 'chai';
import * as fs from 'fs-extra';
import * as path from 'path';
import { EXTENSION_ROOT_DIR } from '../../../../client/common/constants';
import { DebuggerLauncherScriptProvider, NoDebugLauncherScriptProvider, RemoteDebuggerLauncherScriptProvider } from '../../../../client/debugger/debugAdapter/DebugClients/launcherProvider';
import { DebuggerLauncherScriptProvider, NoDebugLauncherScriptProvider, RemoteDebuggerExternalLauncherScriptProvider } from '../../../../client/debugger/debugAdapter/DebugClients/launcherProvider';

const expectedPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'ptvsd_launcher.py');

Expand All @@ -21,12 +21,12 @@ suite('Debugger - Launcher Script Provider', () => {
{
testName: 'When path to ptvsd launcher does not contains spaces',
path: path.join('path', 'to', 'ptvsd_launcher'),
expectedPath: 'path/to/ptvsd_launcher'
expectedPath: path.join('path', 'to', 'ptvsd_launcher')
},
{
testName: 'When path to ptvsd launcher contains spaces',
path: path.join('path', 'to', 'ptvsd_launcher', 'with spaces'),
expectedPath: '"path/to/ptvsd_launcher/with spaces"'
expectedPath: path.join('path', 'to', 'ptvsd_launcher', 'with spaces')
}
];

Expand All @@ -52,15 +52,33 @@ suite('Debugger - Launcher Script Provider', () => {
const expectedArgs = [testParams.expectedPath, '--custom', '--nodebug', '--client', '--host', 'something', '--port', '1234'];
expect(args).to.be.deep.equal(expectedArgs);
});
test('Test remote debug launcher args (and do not wait for debugger to attach)', async () => {
const args = new RemoteDebuggerLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: false });
const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234'];
expect(args).to.be.deep.equal(expectedArgs);
});
test('Test remote debug launcher args (and wait for debugger to attach)', async () => {
const args = new RemoteDebuggerLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: true });
const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234', '--wait'];
expect(args).to.be.deep.equal(expectedArgs);
});
});

suite('External Debug Launcher', () => {
[
{
testName: 'When path to ptvsd launcher does not contains spaces',
path: path.join('path', 'to', 'ptvsd_launcher'),
expectedPath: 'path/to/ptvsd_launcher'
},
{
testName: 'When path to ptvsd launcher contains spaces',
path: path.join('path', 'to', 'ptvsd_launcher', 'with spaces'),
expectedPath: '"path/to/ptvsd_launcher/with spaces"'
}
].forEach(testParams => {
suite(testParams.testName, async () => {
test('Test remote debug launcher args (and do not wait for debugger to attach)', async () => {
const args = new RemoteDebuggerExternalLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: false });
const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234'];
expect(args).to.be.deep.equal(expectedArgs);
});
test('Test remote debug launcher args (and wait for debugger to attach)', async () => {
const args = new RemoteDebuggerExternalLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: true });
const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234', '--wait'];
expect(args).to.be.deep.equal(expectedArgs);
});
});
});
});
Expand Down
Loading