Skip to content

Implement issue report #260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 12, 2024
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
19 changes: 18 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"version": "2024.3.0-dev",
"publisher": "ms-python",
"enabledApiProposals": [
"portsAttributes"
"portsAttributes",
"contribIssueReporter"
],
"license": "MIT",
"homepage": "https://github.com/Microsoft/vscode-python-debugger",
Expand Down Expand Up @@ -63,9 +64,19 @@
"light": "resources/light/repl.svg"
},
"title": "%debugpy.command.viewOutput.title%"
},
{
"category": "Python Debugger",
"command": "debugpy.reportIssue",
"title": "%debugpy.command.reportIssue.title%"
}
],
"menus": {
"issue/reporter": [
{
"command": "debugpy.reportIssue"
}
],
"commandPalette": [
{
"category": "Python Debugger",
Expand All @@ -90,6 +101,12 @@
"category": "Python Debugger",
"command": "debugpy.viewOutput",
"title": "%debugpy.command.viewOutput.title%"
},
{
"category": "Python Debugger",
"command": "debugpy.reportIssue",
"title": "%debugpy.command.reportIssue.title%",
"when": "!virtualWorkspace && shellExecutionSupported"
}
],
"editor/title/run": [
Expand Down
3 changes: 2 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"debugpy.command.clearCacheAndReload.title": "Clear Cache and Reload Window",
"debugpy.command.debugInTerminal.title": "Python Debugger: Debug Python File",
"debugpy.command.debugUsingLaunchConfig.title": "Python Debugger: Debug using launch.json",
"debugpy.command.clearCacheAndReload.title": "Clear Cache and Reload Window",
"debugpy.command.reportIssue.title": "Report Issue...",
"debugpy.command.viewOutput.title": "Show Output",
"debugpy.debugJustMyCode": "When debugging only step through user-written code. Disable this to allow stepping into library code."
}
56 changes: 56 additions & 0 deletions resources/report_issue_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!-- Please fill in all XXX markers -->
# Behaviour

XXX

## Steps to reproduce:

1. XXX

<!--
**After** creating the issue on GitHub, you can add screenshots and GIFs of what is happening. Consider tools like https://www.cockos.com/licecap/, https://github.com/phw/peek or https://www.screentogif.com/ for GIF creation.
-->

<!-- **NOTE**: Please do provide logs from the Python and Python Debugger Output panel. -->
# Diagnostic data
<details>

<summary><code>launch.json</code> configuration
</summary>

<p>

```
XXX
```

</p>
</details>

<details>

<summary>Output for <code>Python</code> in the <code>Output</code> panel (<code>View</code>→<code>Output</code>, change the drop-down the upper-right of the <code>Output</code> panel to <code>Python</code>)
</summary>

<p>

```
XXX
```

</p>
</details>

<details>

<summary>Output for <code>Python Debugger</code> in the <code>Output</code> panel (<code>View</code>→<code>Output</code>, change the drop-down the upper-right of the <code>Output</code> panel to <code>Python Debugger</code>)
</summary>

<p>

```
XXX
```

</p>
</details>
2 changes: 2 additions & 0 deletions resources/report_issue_user_data_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Python version (& distribution if applicable, e.g. Anaconda): {0}
- Type of virtual environment used (e.g. conda, venv, virtualenv, etc.): {1}
34 changes: 34 additions & 0 deletions src/extension/common/application/commands/reportIssueCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

import * as fs from 'fs-extra';
import * as path from 'path';
import { executeCommand } from '../../vscodeapi';
import { getActiveEnvironmentPath, resolveEnvironment } from '../../python';
import { EXTENSION_ROOT_DIR } from '../../constants';
import { getRawVersion } from '../../settings';
import { sendTelemetryEvent } from '../../../telemetry';
import { EventName } from '../../../telemetry/constants';

/**
* Allows the user to report an issue related to the Python Debugger extension using our template.
*/
export async function openReportIssue(): Promise<void> {
const templatePath = path.join(EXTENSION_ROOT_DIR, 'resources', 'report_issue_template.md');
const userDataTemplatePath = path.join(EXTENSION_ROOT_DIR, 'resources', 'report_issue_user_data_template.md');
const template = await fs.readFile(templatePath, 'utf8');
const userTemplate = await fs.readFile(userDataTemplatePath, 'utf8');
const interpreterPath = await getActiveEnvironmentPath();
const interpreter = await resolveEnvironment(interpreterPath);
const virtualEnvKind = interpreter?.environment?.type || 'Unknown';

const pythonVersion = getRawVersion(interpreter?.version);
await executeCommand('workbench.action.openIssueReporter', {
extensionId: 'ms-python.debugpy',
issueBody: template,
data: userTemplate.replace('{0}', pythonVersion).replace('{1}', virtualEnvKind),
});
sendTelemetryEvent(EventName.USE_REPORT_ISSUE_COMMAND, undefined, {});
}
3 changes: 2 additions & 1 deletion src/extension/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as path from 'path';
export const PYTHON_LANGUAGE = 'python';
const folderName = path.basename(__dirname);
export const EXTENSION_ROOT_DIR =
folderName === 'common' ? path.dirname(path.dirname(__dirname)) : path.dirname(__dirname);
folderName === 'common' ? path.dirname(path.dirname(path.dirname(__dirname))) : path.dirname(__dirname);
export const BUNDLED_PYTHON_SCRIPTS_DIR = path.join(EXTENSION_ROOT_DIR, 'bundled');
export const SERVER_SCRIPT_PATH = path.join(BUNDLED_PYTHON_SCRIPTS_DIR, 'tool', `server.py`);
export const DEBUG_SERVER_SCRIPT_PATH = path.join(BUNDLED_PYTHON_SCRIPTS_DIR, 'tool', `_debug_server.py`);
Expand Down Expand Up @@ -39,6 +39,7 @@ export namespace Commands {
export const Enable_SourceMap_Support = 'debugpy.enableSourceMapSupport';
export const SelectDebugConfig = 'debugpy.SelectAndInsertDebugConfiguration';
export const Set_Interpreter = 'python.setInterpreter';
export const ReportIssue = 'debugpy.reportIssue';
}

export type Channel = 'stable' | 'insiders';
9 changes: 8 additions & 1 deletion src/extension/common/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ConfigurationChangeEvent, ConfigurationTarget, Uri, WorkspaceConfigurat
import { getInterpreterDetails } from './python';
import { getConfiguration, getWorkspaceFolder, getWorkspaceFolders } from './vscodeapi';
import { isUnitTestExecution } from './constants';
import { VersionInfo } from '@vscode/python-extension';

export interface ISettings {
workspace: string;
Expand All @@ -15,7 +16,6 @@ export interface ISettings {
export async function getExtensionSettings(namespace: string, includeInterpreter?: boolean): Promise<ISettings[]> {
const settings: ISettings[] = [];
const workspaces = getWorkspaceFolders();

for (const workspace of workspaces) {
const workspaceSetting = await getWorkspaceSettings(namespace, workspace, includeInterpreter);
settings.push(workspaceSetting);
Expand Down Expand Up @@ -149,3 +149,10 @@ export async function verifySetting(
} while (retries < 20);
}
}

export function getRawVersion(version: VersionInfo | undefined) {
if (version) {
return `${version.major}.${version.minor}.${version.micro}`;
}
return ``;
}
3 changes: 3 additions & 0 deletions src/extension/extensionInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { pickArgsInput } from './common/utils/localize';
import { DebugPortAttributesProvider } from './debugger/debugPort/portAttributesProvider';
import { getConfigurationsByUri } from './debugger/configuration/launch.json/launchJsonReader';
import { DebugpySocketsHandler } from './debugger/hooks/debugpySocketsHandler';
import { openReportIssue } from './common/application/commands/reportIssueCommand';

export async function registerDebugger(context: IExtensionContext): Promise<void> {
const childProcessAttachService = new ChildProcessAttachService();
Expand Down Expand Up @@ -63,6 +64,8 @@ export async function registerDebugger(context: IExtensionContext): Promise<void
),
);

context.subscriptions.push(registerCommand(Commands.ReportIssue, () => openReportIssue()));

context.subscriptions.push(
registerCommand(Commands.Debug_In_Terminal, async (file?: Uri) => {
sendTelemetryEvent(EventName.DEBUG_IN_TERMINAL_BUTTON);
Expand Down
1 change: 1 addition & 0 deletions src/extension/telemetry/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export enum EventName {
DEBUGGER_CONFIGURATION_PROMPTS = 'DEBUGGER.CONFIGURATION.PROMPTS',
DEBUGGER_CONFIGURATION_PROMPTS_IN_LAUNCH_JSON = 'DEBUGGER.CONFIGURATION.PROMPTS.IN.LAUNCH.JSON',
ENVFILE_VARIABLE_SUBSTITUTION = 'ENVFILE_VARIABLE_SUBSTITUTION',
USE_REPORT_ISSUE_COMMAND = 'USE_REPORT_ISSUE_COMMAND',
}
7 changes: 7 additions & 0 deletions src/extension/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,4 +660,11 @@ export interface IEventNamePropertyMapping {
"envfile_variable_substitution" : { "owner": "karthiknadig" }
*/
[EventName.ENVFILE_VARIABLE_SUBSTITUTION]: never | undefined;
/**
* Telemetry event sent when the user use the report issue command.
*/
/* __GDPR__
"use_report_issue_command" : { "owner": "paulacamargo25" }
*/
[EventName.USE_REPORT_ISSUE_COMMAND]: unknown;
}
56 changes: 56 additions & 0 deletions src/test/resources/issueTemplate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!-- Please fill in all XXX markers -->
# Behaviour

XXX

## Steps to reproduce:

1. XXX

<!--
**After** creating the issue on GitHub, you can add screenshots and GIFs of what is happening. Consider tools like https://www.cockos.com/licecap/, https://github.com/phw/peek or https://www.screentogif.com/ for GIF creation.
-->

<!-- **NOTE**: Please do provide logs from the Python and Python Debugger Output panel. -->
# Diagnostic data
<details>

<summary><code>launch.json</code> configuration
</summary>

<p>

```
XXX
```

</p>
</details>

<details>

<summary>Output for <code>Python</code> in the <code>Output</code> panel (<code>View</code>→<code>Output</code>, change the drop-down the upper-right of the <code>Output</code> panel to <code>Python</code>)
</summary>

<p>

```
XXX
```

</p>
</details>

<details>

<summary>Output for <code>Python Debugger</code> in the <code>Output</code> panel (<code>View</code>→<code>Output</code>, change the drop-down the upper-right of the <code>Output</code> panel to <code>Python Debugger</code>)
</summary>

<p>

```
XXX
```

</p>
</details>
2 changes: 2 additions & 0 deletions src/test/resources/issueUserDataTemplate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Python version (& distribution if applicable, e.g. Anaconda): 3.9.0
- Type of virtual environment used (e.g. conda, venv, virtualenv, etc.): Venv
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* eslint-disable global-require */
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

import * as sinon from 'sinon';
import * as fs from 'fs-extra';
import * as path from 'path';
import { expect } from 'chai';
import * as Telemetry from '../../../../../extension/telemetry/index';
import { EventName } from '../../../../../extension/telemetry/constants';
import * as vscodeapi from '../../../../../extension/common/vscodeapi';
import * as pythonApi from '../../../../../extension/common/python';
import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../../../../constants';
import { openReportIssue } from '../../../../../extension/common/application/commands/reportIssueCommand';
import { PythonEnvironment } from '../../../../../extension/debugger/adapter/types';

suite('Report Issue Command', () => {
let executeCommandStub: sinon.SinonStub;
let resolveEnvironmentStub: sinon.SinonStub;

setup(async () => {
executeCommandStub = sinon.stub(vscodeapi, 'executeCommand');
resolveEnvironmentStub = sinon.stub(pythonApi, 'resolveEnvironment');
const interpreter = {
environment: {
type: 'Venv',
},
version: {
major: 3,
minor: 9,
micro: 0,
},
} as unknown as PythonEnvironment;
resolveEnvironmentStub.resolves(interpreter);
});

teardown(() => {
sinon.restore();
});

test('Test if issue body is filled correctly when including all the settings', async () => {
await openReportIssue();

const issueTemplatePath = path.join(
EXTENSION_ROOT_DIR_FOR_TESTS,
'src',
'test',
'resources',
'issueTemplate.md',
);
const expectedIssueBody = fs.readFileSync(issueTemplatePath, 'utf8');

const userDataTemplatePath = path.join(
EXTENSION_ROOT_DIR_FOR_TESTS,
'src',
'test',
'resources',
'issueUserDataTemplate.md',
);
const expectedData = fs.readFileSync(userDataTemplatePath, 'utf8');

executeCommandStub.withArgs('workbench.action.openIssueReporter', sinon.match.any).resolves();

sinon.assert.calledOnceWithExactly(executeCommandStub, 'workbench.action.openIssueReporter', sinon.match.any);

const { issueBody, data } = executeCommandStub.getCall(0).args[1];
expect(issueBody).to.be.equal(expectedIssueBody);
expect(data).to.be.equal(expectedData);
});

test('Should send telemetry event when run Report Issue Command', async () => {
const sendTelemetryStub = sinon.stub(Telemetry, 'sendTelemetryEvent');
await openReportIssue();

sinon.assert.calledWith(sendTelemetryStub, EventName.USE_REPORT_ISSUE_COMMAND);
sinon.restore();
});
});
3 changes: 2 additions & 1 deletion src/test/unittest/extensionInit.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ suite('Debugging - register Debugging', () => {
test('Ensure to register all the commands related to the debugger', () => {
registerDebugger(context.object);

sinon.assert.calledWithExactly(registerCommandStub, Commands.ReportIssue, sinon.match.any);
sinon.assert.calledWithExactly(registerCommandStub, Commands.Debug_In_Terminal, sinon.match.any);
sinon.assert.calledWithExactly(registerCommandStub, Commands.Debug_Using_Launch_Config, sinon.match.any);
sinon.assert.calledWithExactly(registerCommandStub, Commands.PickLocalProcess, sinon.match.any);
Expand All @@ -71,7 +72,7 @@ suite('Debugging - register Debugging', () => {
sinon.match.any,
);
sinon.assert.calledWithExactly(registerCommandStub, Commands.ClearStorage, sinon.match.any);
expect(registerCommandStub.callCount).to.be.equal(6);
expect(registerCommandStub.callCount).to.be.equal(7);
});

test('Activation will register the Debug adapter factories', async () => {
Expand Down