Skip to content
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

Show warning and allow user to turn off smart send for deprecated Python code #22353

Merged
merged 31 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
faebdb6
detect user is on file with deprecated Python code
anthonykim1 Oct 26, 2023
14f9b64
send warning message if smart send deprecated py
anthonykim1 Oct 26, 2023
e1d203f
add setting
anthonykim1 Oct 26, 2023
c5ced54
switch repl and code order to normal
anthonykim1 Oct 26, 2023
bc74433
comply with linting error
anthonykim1 Oct 26, 2023
d0ad1fb
fix for test failing
anthonykim1 Nov 13, 2023
be70c44
comment out unused variables
anthonykim1 Nov 13, 2023
d0fd80d
try adding configService and new REPL setting
anthonykim1 Nov 13, 2023
59805ad
quit unused var warning
anthonykim1 Nov 13, 2023
d911459
add activeResourceService
anthonykim1 Nov 13, 2023
a29ecdd
clean up
anthonykim1 Nov 13, 2023
924f9e8
respect deprecated
anthonykim1 Nov 14, 2023
fe80e93
make a button that leed to change setting
anthonykim1 Nov 14, 2023
e23c504
Change message wording
anthonykim1 Nov 14, 2023
f6f8a4a
Change wording
anthonykim1 Nov 14, 2023
91ffe64
use commandManager rather than calling vs code
anthonykim1 Nov 15, 2023
93ab499
add commandManager
anthonykim1 Nov 15, 2023
972f144
remove systemVariable resolve
anthonykim1 Nov 15, 2023
05d4e7f
add warning message test
anthonykim1 Nov 15, 2023
4ebc502
fix typo
anthonykim1 Nov 16, 2023
9283d80
fix smoke test error
anthonykim1 Nov 16, 2023
4682545
button will actually change setting
anthonykim1 Nov 16, 2023
d3efb7c
Update description according to suggestion
anthonykim1 Nov 16, 2023
f4deb62
add traceInfo message
anthonykim1 Nov 16, 2023
4ba135f
Update src/client/terminals/codeExecution/terminalCodeExecution.ts
anthonykim1 Nov 17, 2023
1794700
touch up on warning message
anthonykim1 Nov 17, 2023
13eeb80
check condition on Disable button
anthonykim1 Nov 17, 2023
4fc0357
Update src/client/terminals/codeExecution/terminalCodeExecution.ts
anthonykim1 Nov 17, 2023
956ee20
put message and button in localize
anthonykim1 Nov 17, 2023
16021ee
lowercase enable
anthonykim1 Nov 18, 2023
7063648
enable for test
anthonykim1 Nov 18, 2023
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
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,12 @@
"scope": "resource",
"type": "array"
},
"python.REPL.EnableREPLSmartSend": {
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
"default": true,
"description": "%python.EnableREPLSmartSend.description%",
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
"scope": "resource",
"type": "boolean"
},
"python.testing.autoTestDiscoverOnSaveEnabled": {
"default": true,
"description": "%python.testing.autoTestDiscoverOnSaveEnabled.description%",
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"python.missingPackage.severity.description": "Set severity of missing packages in requirements.txt or pyproject.toml",
"python.pipenvPath.description": "Path to the pipenv executable to use for activation.",
"python.poetryPath.description": "Path to the poetry executable.",
"python.EnableREPLSmartSend.description": "Toggle smart send for the Python REPL.",
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
"python.tensorBoard.logDirectory.description": "Set this setting to your preferred TensorBoard log directory to skip log directory prompt when starting TensorBoard.",
"python.tensorBoard.logDirectory.markdownDeprecationMessage": "Tensorboard support has been moved to the extension [Tensorboard extension](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.tensorboard). Instead use the setting `tensorBoard.logDirectory`.",
"python.tensorBoard.logDirectory.deprecationMessage": "Tensorboard support has been moved to the extension Tensorboard extension. Instead use the setting `tensorBoard.logDirectory`.",
Expand Down
27 changes: 22 additions & 5 deletions pythonFiles/normalizeSelection.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,18 @@ def traverse_file(wholeFileContent, start_line, end_line, was_highlighted):
or a multiline dictionary, or differently styled multi-line list comprehension, etc.
Then call the normalize_lines function to normalize our smartly selected code block.
"""
parsed_file_content = None

try:
parsed_file_content = ast.parse(wholeFileContent)
except Exception:
# Handle case where user is attempting to run code where file contains deprecated Python code.
# Let typescript side know and show warning message.
return {
"normalized_smart_result": "deprecated",
"which_line_next": 0,
}

parsed_file_content = ast.parse(wholeFileContent)
smart_code = ""
should_run_top_blocks = []

Expand Down Expand Up @@ -267,7 +277,11 @@ def get_next_block_lineno(which_line_next):
data = None
which_line_next = 0

if empty_Highlight and contents.get("smartSendExperimentEnabled"):
if (
empty_Highlight
and contents.get("smartSendExperimentEnabled")
and contents.get("smartSendSettingsEnabled")
):
result = traverse_file(
contents["wholeFileContent"],
vscode_start_line,
Expand All @@ -276,9 +290,12 @@ def get_next_block_lineno(which_line_next):
)
normalized = result["normalized_smart_result"]
which_line_next = result["which_line_next"]
data = json.dumps(
{"normalized": normalized, "nextBlockLineno": result["which_line_next"]}
)
if normalized == "deprecated":
data = json.dumps({"normalized": normalized})
else:
data = json.dumps(
{"normalized": normalized, "nextBlockLineno": result["which_line_next"]}
)
else:
normalized = normalize_lines(contents["code"])
data = json.dumps({"normalized": normalized})
Expand Down
4 changes: 4 additions & 0 deletions src/client/common/configSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
IInterpreterPathService,
IInterpreterSettings,
IPythonSettings,
IREPLSettings,
ITensorBoardSettings,
ITerminalSettings,
Resource,
Expand Down Expand Up @@ -114,6 +115,8 @@ export class PythonSettings implements IPythonSettings {

public globalModuleInstallation = false;

public REPL!: IREPLSettings;

public experiments!: IExperiments;

public languageServer: LanguageServerType = LanguageServerType.Node;
Expand Down Expand Up @@ -363,6 +366,7 @@ export class PythonSettings implements IPythonSettings {
activateEnvInCurrentTerminal: false,
};

this.REPL = systemVariables.resolveAny(pythonSettings.get<IREPLSettings>('REPL'))!;
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
const experiments = systemVariables.resolveAny(pythonSettings.get<IExperiments>('experiments'))!;
if (this.experiments) {
Object.assign<IExperiments, IExperiments>(this.experiments, experiments);
Expand Down
5 changes: 5 additions & 0 deletions src/client/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export interface IPythonSettings {
readonly languageServerIsDefault: boolean;
readonly defaultInterpreterPath: string;
readonly tensorBoard: ITensorBoardSettings | undefined;
readonly REPL: IREPLSettings;
register(): void;
}

Expand All @@ -197,6 +198,10 @@ export interface ITerminalSettings {
readonly activateEnvInCurrentTerminal: boolean;
}

export interface IREPLSettings {
readonly EnableREPLSmartSend: boolean;
}

export interface IExperiments {
/**
* Return `true` if experiments are enabled, else `false`.
Expand Down
19 changes: 18 additions & 1 deletion src/client/terminals/codeExecution/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { inject, injectable } from 'inversify';
import { l10n, Position, Range, TextEditor, Uri } from 'vscode';

import {
IActiveResourceService,
IApplicationShell,
ICommandManager,
IDocumentManager,
Expand Down Expand Up @@ -36,6 +37,8 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {

private readonly commandManager: ICommandManager;

private activeResourceService: IActiveResourceService;

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error TS6133: 'configSettings' is declared but its value is never read.
private readonly configSettings: IConfigurationService;
Expand All @@ -47,6 +50,7 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
this.interpreterService = serviceContainer.get<IInterpreterService>(IInterpreterService);
this.configSettings = serviceContainer.get<IConfigurationService>(IConfigurationService);
this.commandManager = serviceContainer.get<ICommandManager>(ICommandManager);
this.activeResourceService = this.serviceContainer.get<IActiveResourceService>(IActiveResourceService);
}

public async normalizeLines(code: string, wholeFileContent?: string, resource?: Uri): Promise<string> {
Expand Down Expand Up @@ -90,13 +94,21 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
const endLineVal = activeEditor?.selection?.end.line ?? 0;
const emptyHighlightVal = activeEditor?.selection?.isEmpty ?? true;
const smartSendExperimentEnabledVal = pythonSmartSendEnabled(this.serviceContainer);
let smartSendSettingsEnabledVal = false;
const configuration = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
if (configuration) {
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
const pythonSettings = configuration.getSettings(this.activeResourceService.getActiveResource());
smartSendSettingsEnabledVal = pythonSettings.REPL.EnableREPLSmartSend;
}

const input = JSON.stringify({
code,
wholeFileContent,
startLine: startLineVal,
endLine: endLineVal,
emptyHighlight: emptyHighlightVal,
smartSendExperimentEnabled: smartSendExperimentEnabledVal,
smartSendSettingsEnabled: smartSendSettingsEnabledVal,
});
observable.proc?.stdin?.write(input);
observable.proc?.stdin?.end();
Expand All @@ -105,7 +117,12 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
const result = await normalizeOutput.promise;
const object = JSON.parse(result);

if (activeEditor?.selection) {
if (
activeEditor?.selection &&
smartSendExperimentEnabledVal &&
smartSendSettingsEnabledVal &&
object.normalized !== 'deprecated'
) {
const lineOffset = object.nextBlockLineno - activeEditor!.selection.start.line - 1;
await this.moveToNextBlock(lineOffset, activeEditor);
}
Expand Down
24 changes: 20 additions & 4 deletions src/client/terminals/codeExecution/terminalCodeExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@

import { inject, injectable } from 'inversify';
import * as path from 'path';
import { Disposable, Uri } from 'vscode';
import { Disposable, l10n, Uri } from 'vscode';
import { IWorkspaceService } from '../../common/application/types';
import { Commands } from '../../common/constants';
import '../../common/extensions';
import { IPlatformService } from '../../common/platform/types';
import { ITerminalService, ITerminalServiceFactory } from '../../common/terminal/types';
import { IConfigurationService, IDisposableRegistry, Resource } from '../../common/types';
import { showWarningMessage } from '../../common/vscodeApis/windowApis';
import { IInterpreterService } from '../../interpreter/contracts';
import { buildPythonExecInfo, PythonExecInfo } from '../../pythonEnvironments/exec';
import { ICodeExecutionService } from '../../terminals/types';

import * as vscode from 'vscode';
@injectable()
export class TerminalCodeExecutionProvider implements ICodeExecutionService {
private hasRanOutsideCurrentDrive = false;
Expand Down Expand Up @@ -42,9 +44,23 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService {
if (!code || code.trim().length === 0) {
return;
}

await this.initializeRepl(resource);
await this.getTerminalService(resource).sendText(code);
if (code == 'deprecated') {
// If user is trying to smart send deprecated code show warning
const selection = await showWarningMessage(
l10n.t(
`Python AST cannot parse invalid code provided in the current file, please
turn off Smart Send if you wish to always run line by line or explicitly select code
to force run. [logs](command:${Commands.ViewOutput}) for more details.`,
),
'Change Settings',
);
if (selection === 'Change Settings') {
vscode.commands.executeCommand('workbench.action.openSettings', 'python.REPL.EnableREPLSmartSend');
anthonykim1 marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
await this.getTerminalService(resource).sendText(code);
}
}
public async initializeRepl(resource: Resource) {
const terminalService = this.getTerminalService(resource);
Expand Down
36 changes: 32 additions & 4 deletions src/test/terminals/codeExecution/smartSend.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import * as TypeMoq from 'typemoq';
import * as path from 'path';
import { TextEditor, Selection, Position, TextDocument } from 'vscode';
import { TextEditor, Selection, Position, TextDocument, Uri } from 'vscode';
import * as fs from 'fs-extra';
import { SemVer } from 'semver';
import { assert, expect } from 'chai';
import { IApplicationShell, ICommandManager, IDocumentManager } from '../../../client/common/application/types';
import {
IActiveResourceService,
IApplicationShell,
ICommandManager,
IDocumentManager,
} from '../../../client/common/application/types';
import { IProcessService, IProcessServiceFactory } from '../../../client/common/process/types';
import { IInterpreterService } from '../../../client/interpreter/contracts';
import { IConfigurationService, IExperimentService } from '../../../client/common/types';
import { IConfigurationService, IExperimentService, IPythonSettings } from '../../../client/common/types';
import { CodeExecutionHelper } from '../../../client/terminals/codeExecution/helper';
import { IServiceContainer } from '../../../client/ioc/types';
import { ICodeExecutionHelper } from '../../../client/terminals/types';
Expand Down Expand Up @@ -35,8 +40,11 @@ suite('REPL - Smart Send', () => {
let experimentService: TypeMoq.IMock<IExperimentService>;

let processService: TypeMoq.IMock<IProcessService>;
let activeResourceService: TypeMoq.IMock<IActiveResourceService>;

let document: TypeMoq.IMock<TextDocument>;
let pythonSettings: TypeMoq.IMock<IPythonSettings>;

const workingPython: PythonEnvironment = {
path: PYTHON_PATH,
version: new SemVer('3.6.6-final'),
Expand Down Expand Up @@ -64,7 +72,9 @@ suite('REPL - Smart Send', () => {
serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
experimentService = TypeMoq.Mock.ofType<IExperimentService>();
processService = TypeMoq.Mock.ofType<IProcessService>();

activeResourceService = TypeMoq.Mock.ofType<IActiveResourceService>();
pythonSettings = TypeMoq.Mock.ofType<IPythonSettings>();
const resource = Uri.parse('a');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
processService.setup((x: any) => x.then).returns(() => undefined);
serviceContainer
Expand Down Expand Up @@ -92,6 +102,14 @@ suite('REPL - Smart Send', () => {
interpreterService
.setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny()))
.returns(() => Promise.resolve(workingPython));
serviceContainer
.setup((c) => c.get(TypeMoq.It.isValue(IActiveResourceService)))
.returns(() => activeResourceService.object);
activeResourceService.setup((a) => a.getActiveResource()).returns(() => resource);

pythonSettings.setup((s) => s.REPL).returns(() => ({ EnableREPLSmartSend: true, REPLSmartSend: true }));

configurationService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object);

codeExecutionHelper = new CodeExecutionHelper(serviceContainer.object);
document = TypeMoq.Mock.ofType<TextDocument>();
Expand Down Expand Up @@ -149,6 +167,16 @@ suite('REPL - Smart Send', () => {
.setup((exp) => exp.inExperimentSync(TypeMoq.It.isValue(EnableREPLSmartSend.experiment)))
.returns(() => true);

configurationService
.setup((c) => c.getSettings(TypeMoq.It.isAny()))
.returns({
REPL: {
EnableREPLSmartSend: true,
REPLSmartSend: true,
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any);

const activeEditor = TypeMoq.Mock.ofType<TextEditor>();
const firstIndexPosition = new Position(0, 0);
const selection = TypeMoq.Mock.ofType<Selection>();
Expand Down
Loading