diff --git a/.gitignore b/.gitignore index c567171911dc..d3c23ef234fe 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ obj/** .pytest_cache tmp/** .python-version +.vs/ diff --git a/news/1 Enhancements/2463.md b/news/1 Enhancements/2463.md new file mode 100644 index 000000000000..49dbf479f198 --- /dev/null +++ b/news/1 Enhancements/2463.md @@ -0,0 +1 @@ +Added a debugger setting to show return values of functions while stepping. \ No newline at end of file diff --git a/package.json b/package.json index 0937791b622d..43dd619153d9 100644 --- a/package.json +++ b/package.json @@ -490,6 +490,11 @@ "description": "Automatically stop after launch.", "default": false }, + "showReturnValue": { + "type": "boolean", + "description": "Show return value of functions when stepping.", + "default": false + }, "console": { "enum": [ "none", diff --git a/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts b/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts index 72157dd1e4eb..4c724647356f 100644 --- a/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts +++ b/src/client/debugger/debugAdapter/DebugClients/LocalDebugClient.ts @@ -20,7 +20,8 @@ import { DebugClientHelper } from './helper'; const VALID_DEBUG_OPTIONS = [ 'RedirectOutput', 'DebugStdLib', - 'stopOnEntry', + 'StopOnEntry', + 'ShowReturnValue', 'BreakOnSystemExitZero', 'DjangoDebugging', 'Django']; diff --git a/src/client/debugger/extension/configProviders/baseProvider.ts b/src/client/debugger/extension/configProviders/baseProvider.ts index d0ade97aa560..4ad5345ca112 100644 --- a/src/client/debugger/extension/configProviders/baseProvider.ts +++ b/src/client/debugger/extension/configProviders/baseProvider.ts @@ -72,6 +72,9 @@ export abstract class BaseConfigurationProvider implements DebugConfigurationPro if (typeof debugConfiguration.stopOnEntry !== 'boolean') { debugConfiguration.stopOnEntry = false; } + if (typeof debugConfiguration.showReturnValue !== 'boolean') { + debugConfiguration.showReturnValue = false; + } if (!debugConfiguration.console) { debugConfiguration.console = 'integratedTerminal'; } diff --git a/src/client/debugger/extension/configProviders/pythonV2Provider.ts b/src/client/debugger/extension/configProviders/pythonV2Provider.ts index 86cfacf6de37..5b292185ecb7 100644 --- a/src/client/debugger/extension/configProviders/pythonV2Provider.ts +++ b/src/client/debugger/extension/configProviders/pythonV2Provider.ts @@ -28,6 +28,9 @@ export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvide if (debugConfiguration.stopOnEntry) { this.debugOption(debugOptions, DebugOptions.StopOnEntry); } + if (debugConfiguration.showReturnValue) { + this.debugOption(debugOptions, DebugOptions.ShowReturnValue); + } if (debugConfiguration.django) { this.debugOption(debugOptions, DebugOptions.Django); } @@ -136,7 +139,8 @@ export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvide isSudo: !!debugConfiguration.sudo, jinja: !!debugConfiguration.jinja, pyramid: !!debugConfiguration.pyramid, - stopOnEntry: !!debugConfiguration.stopOnEntry + stopOnEntry: !!debugConfiguration.stopOnEntry, + showReturnValue: !!debugConfiguration.showReturnValue }; sendTelemetryEvent(DEBUGGER, undefined, telemetryProps); } diff --git a/src/client/debugger/types.ts b/src/client/debugger/types.ts index a031d3e7e708..a91b825c0c33 100644 --- a/src/client/debugger/types.ts +++ b/src/client/debugger/types.ts @@ -17,7 +17,8 @@ export enum DebugOptions { FixFilePathCase = 'FixFilePathCase', WindowsClient = 'WindowsClient', UnixClient = 'UnixClient', - StopOnEntry = 'StopOnEntry' + StopOnEntry = 'StopOnEntry', + ShowReturnValue = 'ShowReturnValue' } // tslint:disable-next-line:interface-name @@ -30,6 +31,7 @@ interface AdditionalLaunchDebugOptions { sudo?: boolean; pyramid?: boolean; stopOnEntry?: boolean; + showReturnValue?: boolean; } // tslint:disable-next-line:interface-name @@ -50,6 +52,8 @@ export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArgum pythonPath: string; // Automatically stop target after launch. If not specified, target does not stop. stopOnEntry?: boolean; + /** Show return values of functions while stepping. */ + showReturnValue?: boolean; args: string[]; cwd?: string; debugOptions?: DebugOptions[]; diff --git a/src/client/telemetry/types.ts b/src/client/telemetry/types.ts index c7e5b6a0c717..2c0d2b118693 100644 --- a/src/client/telemetry/types.ts +++ b/src/client/telemetry/types.ts @@ -55,6 +55,7 @@ export type DebuggerTelemetryV2 = { isModule: boolean; isSudo: boolean; stopOnEntry: boolean; + showReturnValue: boolean; pyramid: boolean; }; export type DebuggerPerformanceTelemetry = { diff --git a/src/test/debugger/configProvider/provider.unit.test.ts b/src/test/debugger/configProvider/provider.unit.test.ts index 894d22ca88e4..f082d6330329 100644 --- a/src/test/debugger/configProvider/provider.unit.test.ts +++ b/src/test/debugger/configProvider/provider.unit.test.ts @@ -270,8 +270,9 @@ suite('Debugging - Config Provider', () => { expect(debugConfig).to.have.property('console', 'integratedTerminal'); expect(debugConfig).to.have.property('stopOnEntry', false); + expect(debugConfig).to.have.property('showReturnValue', false); expect(debugConfig).to.have.property('debugOptions'); - expect((debugConfig as any).debugOptions).to.be.deep.equal(['RedirectOutput']); + expect((debugConfig as any).debugOptions).to.be.deep.equal([DebugOptions.RedirectOutput]); }); test('Test defaults of python debugger', async () => { if ('python' === DebuggerTypeName) { @@ -286,6 +287,7 @@ suite('Debugging - Config Provider', () => { const debugConfig = await debugProvider.resolveDebugConfiguration!(workspaceFolder, {} as DebugConfiguration); expect(debugConfig).to.have.property('stopOnEntry', false); + expect(debugConfig).to.have.property('showReturnValue', false); expect(debugConfig).to.have.property('debugOptions'); expect((debugConfig as any).debugOptions).to.be.deep.equal([DebugOptions.RedirectOutput]); }); @@ -300,6 +302,7 @@ suite('Debugging - Config Provider', () => { expect(debugConfig).to.have.property('console', 'integratedTerminal'); expect(debugConfig).to.have.property('stopOnEntry', false); + expect(debugConfig).to.have.property('showReturnValue', false); expect(debugConfig).to.have.property('debugOptions'); expect((debugConfig as any).debugOptions).to.be.deep.equal([]); }); diff --git a/src/test/debugger/misc.test.ts b/src/test/debugger/misc.test.ts index e96b174576fc..fc341f37a8c6 100644 --- a/src/test/debugger/misc.test.ts +++ b/src/test/debugger/misc.test.ts @@ -56,13 +56,14 @@ suite(`Standard Debugging - Misc tests: ${debuggerType}`, () => { return new DebugClientEx(testAdapterFilePath, debuggerType, coverageDirectory, { cwd: EXTENSION_ROOT_DIR }); } } - function buildLaunchArgs(pythonFile: string, stopOnEntry: boolean = false): LaunchRequestArguments { + function buildLaunchArgs(pythonFile: string, stopOnEntry: boolean = false, showReturnValue: boolean = false): LaunchRequestArguments { const env = { PYTHONPATH: PTVSD_PATH }; // tslint:disable-next-line:no-unnecessary-local-variable const options = { program: path.join(debugFilesPath, pythonFile), cwd: debugFilesPath, stopOnEntry, + showReturnValue, debugOptions: [DebugOptions.RedirectOutput], pythonPath: PYTHON_PATH, args: [], diff --git a/src/test/debugger/portAndHost.test.ts b/src/test/debugger/portAndHost.test.ts index a5d7532bbf88..abb30c277f82 100644 --- a/src/test/debugger/portAndHost.test.ts +++ b/src/test/debugger/portAndHost.test.ts @@ -44,11 +44,12 @@ suite(`Standard Debugging of ports and hosts: ${debuggerType}`, () => { } catch (ex) { } }); - function buildLauncArgs(pythonFile: string, stopOnEntry: boolean = false, port?: number, host?: string): LaunchRequestArguments { + function buildLaunchArgs(pythonFile: string, stopOnEntry: boolean = false, port?: number, host?: string, showReturnValue: boolean = false): LaunchRequestArguments { return { program: path.join(debugFilesPath, pythonFile), cwd: debugFilesPath, stopOnEntry, + showReturnValue, logToFile: true, debugOptions: [DebugOptions.RedirectOutput], pythonPath: PYTHON_PATH, @@ -64,7 +65,7 @@ suite(`Standard Debugging of ports and hosts: ${debuggerType}`, () => { async function testDebuggingWithProvidedPort(port?: number | undefined, host?: string | undefined) { await Promise.all([ debugClient.configurationSequence(), - debugClient.launch(buildLauncArgs('startAndWait.py', false, port, host)), + debugClient.launch(buildLaunchArgs('startAndWait.py', false, port, host)), debugClient.waitForEvent('initialized') ]); diff --git a/src/test/debugger/run.test.ts b/src/test/debugger/run.test.ts index e43d847880ed..f88dfbbff67c 100644 --- a/src/test/debugger/run.test.ts +++ b/src/test/debugger/run.test.ts @@ -40,12 +40,13 @@ suite('Run without Debugging', () => { } catch (ex) { } await sleep(1000); }); - function buildLauncArgs(pythonFile: string, stopOnEntry: boolean = false): LaunchRequestArguments { + function buildLaunchArgs(pythonFile: string, stopOnEntry: boolean = false, showReturnValue: boolean = false): LaunchRequestArguments { // tslint:disable-next-line:no-unnecessary-local-variable return { program: path.join(debugFilesPath, pythonFile), cwd: debugFilesPath, stopOnEntry, + showReturnValue, noDebug: true, debugOptions: [DebugOptions.RedirectOutput], pythonPath: PYTHON_PATH, @@ -62,7 +63,7 @@ suite('Run without Debugging', () => { test('Should run program to the end', async () => { await Promise.all([ debugClient.configurationSequence(), - debugClient.launch(buildLauncArgs('simplePrint.py', false)), + debugClient.launch(buildLaunchArgs('simplePrint.py', false)), debugClient.waitForEvent('initialized'), debugClient.waitForEvent('terminated') ]); @@ -70,7 +71,7 @@ suite('Run without Debugging', () => { test('test stderr output for Python', async () => { await Promise.all([ debugClient.configurationSequence(), - debugClient.launch(buildLauncArgs('stdErrOutput.py', false)), + debugClient.launch(buildLaunchArgs('stdErrOutput.py', false)), debugClient.waitForEvent('initialized'), debugClient.assertOutput('stderr', 'error output'), debugClient.waitForEvent('terminated') @@ -79,7 +80,7 @@ suite('Run without Debugging', () => { test('Test stdout output', async () => { await Promise.all([ debugClient.configurationSequence(), - debugClient.launch(buildLauncArgs('stdOutOutput.py', false)), + debugClient.launch(buildLaunchArgs('stdOutOutput.py', false)), debugClient.waitForEvent('initialized'), debugClient.assertOutput('stdout', 'normal output'), debugClient.waitForEvent('terminated') @@ -102,7 +103,7 @@ suite('Run without Debugging', () => { }); await Promise.all([ debugClient.configurationSequence(), - debugClient.launch(buildLauncArgs('sampleWithSleep.py', false)), + debugClient.launch(buildLaunchArgs('sampleWithSleep.py', false)), debugClient.waitForEvent('initialized'), processIdOutput ]);