Skip to content

Commit 75f66aa

Browse files
authored
fix #1229 disable installer prompts via do not show again option (#1243)
* disable installer prompts via do not show again option * removed unwanted resolved promise * updated message * removed redundant semicolon
1 parent aa3c717 commit 75f66aa

File tree

6 files changed

+74
-35
lines changed

6 files changed

+74
-35
lines changed

package.json

+19
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,25 @@
956956
"type": "string"
957957
}
958958
},
959+
"python.disablePromptForFeatures": {
960+
"type": "array",
961+
"default": [],
962+
"description": "Do not display a prompt to install these features",
963+
"items": {
964+
"type": "string",
965+
"default": "pylint",
966+
"description": "Feature",
967+
"enum": [
968+
"flake8",
969+
"mypy",
970+
"pep8",
971+
"pylama",
972+
"prospector",
973+
"pydocstyle",
974+
"pylint"
975+
]
976+
}
977+
},
959978
"python.linting.enabled": {
960979
"type": "boolean",
961980
"default": true,

src/client/common/configSettings.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface IPythonSettings {
2323
sortImports: ISortImportSettings;
2424
workspaceSymbols: IWorkspaceSymbolSettings;
2525
envFile: string;
26+
disablePromptForFeatures: string[];
2627
}
2728
export interface ISortImportSettings {
2829
path: string;
@@ -166,6 +167,8 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
166167
this.devOptions = systemVariables.resolveAny(pythonSettings.get<any[]>('devOptions'))!;
167168
this.devOptions = Array.isArray(this.devOptions) ? this.devOptions : [];
168169
let lintingSettings = systemVariables.resolveAny(pythonSettings.get<ILintingSettings>('linting'))!;
170+
this.disablePromptForFeatures = pythonSettings.get<string[]>('disablePromptForFeatures')!;
171+
this.disablePromptForFeatures = Array.isArray(this.disablePromptForFeatures) ? this.disablePromptForFeatures : [];
169172
if (this.linting) {
170173
Object.assign<ILintingSettings, ILintingSettings>(this.linting, lintingSettings);
171174
}
@@ -353,6 +356,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
353356
}
354357
public jediPath: string;
355358
public envFile: string;
359+
public disablePromptForFeatures: string[];
356360
public venvPath: string;
357361
public devOptions: string[];
358362
public linting: ILintingSettings;
@@ -422,4 +426,4 @@ function isValidPythonPath(pythonPath: string): boolean {
422426
catch (ex) {
423427
return false;
424428
}
425-
}
429+
}

src/client/common/installer.ts

+46-30
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as vscode from 'vscode';
22
import * as settings from './configSettings';
3-
import { createDeferred, isNotInstalledError } from './helpers';
4-
import { execPythonFile, getFullyQualifiedPythonInterpreterPath } from './utils';
53
import * as os from 'os';
4+
import { isNotInstalledError } from './helpers';
5+
import { execPythonFile, getFullyQualifiedPythonInterpreterPath } from './utils';
66
import { Documentation } from './constants';
77

88
export enum Product {
@@ -86,9 +86,6 @@ export const Linters: Product[] = [
8686
Product.pydocstyle
8787
];
8888

89-
const Formatters: Product[] = [Product.autopep8, Product.yapf];
90-
const TestFrameworks: Product[] = [Product.pytest, Product.nosetest, Product.unittest];
91-
9289
const ProductNames = new Map<Product, string>();
9390
ProductNames.set(Product.autopep8, 'autopep8');
9491
ProductNames.set(Product.flake8, 'flake8');
@@ -146,9 +143,9 @@ ProductTypes.set(Product.yapf, ProductType.Formatter);
146143
ProductTypes.set(Product.rope, ProductType.RefactoringLibrary);
147144

148145
export class Installer implements vscode.Disposable {
149-
private static terminal: vscode.Terminal;
146+
private static terminal: vscode.Terminal | undefined | null;
150147
private disposables: vscode.Disposable[] = [];
151-
constructor(private outputChannel: vscode.OutputChannel = null) {
148+
constructor(private outputChannel?: vscode.OutputChannel) {
152149
this.disposables.push(vscode.window.onDidCloseTerminal(term => {
153150
if (term === Installer.terminal) {
154151
Installer.terminal = null;
@@ -158,46 +155,65 @@ export class Installer implements vscode.Disposable {
158155
public dispose() {
159156
this.disposables.forEach(d => d.dispose());
160157
}
158+
public shouldDisplayPrompt(product: Product) {
159+
const productName = ProductNames.get(product)!;
160+
return settings.PythonSettings.getInstance().disablePromptForFeatures.indexOf(productName) === -1;
161+
}
161162

162-
promptToInstall(product: Product): Thenable<any> {
163-
const productType = ProductTypes.get(product);
163+
async promptToInstall(product: Product) {
164+
const productType = ProductTypes.get(product)!;
164165
const productTypeName = ProductTypeNames.get(productType);
165-
const productName = ProductNames.get(product);
166+
const productName = ProductNames.get(product)!;
167+
168+
if (!this.shouldDisplayPrompt(product)) {
169+
const message = `${productTypeName} '${productName}' not installed.`;
170+
if (this.outputChannel) {
171+
this.outputChannel.appendLine(message);
172+
}
173+
else {
174+
console.warn(message);
175+
}
176+
return;
177+
}
166178

167179
const installOption = 'Install ' + productName;
168180
const disableOption = 'Disable ' + productTypeName;
169-
const disableOptionGlobally = `Disable ${productTypeName} globally`;
181+
const dontShowAgain = `Don't show this prompt again`;
170182
const alternateFormatter = product === Product.autopep8 ? 'yapf' : 'autopep8';
171183
const useOtherFormatter = `Use '${alternateFormatter}' formatter`;
172184
const options = [];
173185
options.push(installOption);
174186
if (productType === ProductType.Formatter) {
175-
options.push(...[installOption, useOtherFormatter]);
187+
options.push(...[useOtherFormatter]);
176188
}
177189
if (SettingToDisableProduct.has(product)) {
178-
options.push(...[disableOption, disableOptionGlobally]);
190+
options.push(...[disableOption, dontShowAgain]);
179191
}
180192
return vscode.window.showErrorMessage(`${productTypeName} ${productName} is not installed`, ...options).then(item => {
181193
switch (item) {
182194
case installOption: {
183195
return this.install(product);
184196
}
185-
case disableOptionGlobally:
186197
case disableOption: {
187-
const global = item === disableOptionGlobally;
188198
if (Linters.indexOf(product) >= 0) {
189-
return disableLinter(product, global);
199+
return disableLinter(product);
190200
}
191201
else {
192202
const pythonConfig = vscode.workspace.getConfiguration('python');
193-
const settingToDisable = SettingToDisableProduct.get(product);
194-
return pythonConfig.update(settingToDisable, false, global);
203+
const settingToDisable = SettingToDisableProduct.get(product)!;
204+
return pythonConfig.update(settingToDisable, false);
195205
}
196206
}
197207
case useOtherFormatter: {
198208
const pythonConfig = vscode.workspace.getConfiguration('python');
199209
return pythonConfig.update('formatting.provider', alternateFormatter);
200210
}
211+
case dontShowAgain: {
212+
const pythonConfig = vscode.workspace.getConfiguration('python');
213+
const features = pythonConfig.get('disablePromptForFeatures', [] as string[]);
214+
features.push(productName);
215+
return pythonConfig.update('disablePromptForFeatures', features, true);
216+
}
201217
case 'Help': {
202218
return Promise.resolve();
203219
}
@@ -215,7 +231,7 @@ export class Installer implements vscode.Disposable {
215231
return Promise.resolve();
216232
}
217233

218-
let installArgs = ProductInstallScripts.get(product);
234+
let installArgs = ProductInstallScripts.get(product)!;
219235
let pipIndex = installArgs.indexOf('pip');
220236
if (pipIndex > 0) {
221237
installArgs = installArgs.slice();
@@ -228,8 +244,8 @@ export class Installer implements vscode.Disposable {
228244
if (this.outputChannel && installArgs[0] === '-m') {
229245
// Errors are just displayed to the user
230246
this.outputChannel.show();
231-
return execPythonFile(settings.PythonSettings.getInstance().pythonPath, installArgs, vscode.workspace.rootPath, true, (data) => {
232-
this.outputChannel.append(data);
247+
return execPythonFile(settings.PythonSettings.getInstance().pythonPath, installArgs, vscode.workspace.rootPath!, true, (data) => {
248+
this.outputChannel!.append(data);
233249
});
234250
}
235251
else {
@@ -249,8 +265,8 @@ export class Installer implements vscode.Disposable {
249265
installScript = `${pythonPath} ${installScript}`;
250266
}
251267
}
252-
Installer.terminal.sendText(installScript);
253-
Installer.terminal.show(false);
268+
Installer.terminal!.sendText(installScript);
269+
Installer.terminal!.show(false);
254270
});
255271
}
256272
}
@@ -266,7 +282,7 @@ export class Installer implements vscode.Disposable {
266282

267283
export function disableLinter(product: Product, global?: boolean) {
268284
const pythonConfig = vscode.workspace.getConfiguration('python');
269-
const settingToDisable = SettingToDisableProduct.get(product);
285+
const settingToDisable = SettingToDisableProduct.get(product)!;
270286
if (vscode.workspace.rootPath) {
271287
return pythonConfig.update(settingToDisable, false, global);
272288
}
@@ -275,12 +291,12 @@ export function disableLinter(product: Product, global?: boolean) {
275291
}
276292
}
277293

278-
function isProductInstalled(product: Product): Promise<boolean | undefined> {
294+
async function isProductInstalled(product: Product): Promise<boolean | undefined> {
279295
if (!ProductExecutableAndArgs.has(product)) {
280296
return;
281297
}
282-
const prodExec = ProductExecutableAndArgs.get(product);
283-
return execPythonFile(prodExec.executable, prodExec.args.concat(['--version']), vscode.workspace.rootPath, false)
298+
const prodExec = ProductExecutableAndArgs.get(product)!;
299+
return execPythonFile(prodExec.executable, prodExec.args.concat(['--version']), vscode.workspace.rootPath!, false)
284300
.then(() => {
285301
return true;
286302
}).catch(reason => {
@@ -289,6 +305,6 @@ function isProductInstalled(product: Product): Promise<boolean | undefined> {
289305
}
290306

291307
function uninstallproduct(product: Product): Promise<any> {
292-
const uninstallArgs = ProductUninstallScripts.get(product);
293-
return execPythonFile('python', uninstallArgs, vscode.workspace.rootPath, false);
294-
}
308+
const uninstallArgs = ProductUninstallScripts.get(product)!;
309+
return execPythonFile('python', uninstallArgs, vscode.workspace.rootPath!, false);
310+
}

src/client/common/systemVariables.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export class SystemVariables extends AbstractSystemVariables {
137137

138138
constructor() {
139139
super();
140-
this._workspaceRoot = typeof vscode.workspace.rootPath === 'string' ? vscode.workspace.rootPath : __dirname;;
140+
this._workspaceRoot = typeof vscode.workspace.rootPath === 'string' ? vscode.workspace.rootPath : __dirname;
141141
this._workspaceRootFolderName = Path.basename(this._workspaceRoot);
142142
Object.keys(process.env).forEach(key => {
143143
this[`env:${key}`] = this[`env.${key}`] = process.env[key];
@@ -155,4 +155,4 @@ export class SystemVariables extends AbstractSystemVariables {
155155
public get workspaceRootFolderName(): string {
156156
return this._workspaceRootFolderName;
157157
}
158-
}
158+
}

src/client/interpreter/display/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class InterpreterDisplay implements Disposable {
4848
await Promise.all([interpreterExists, displayName, virtualEnvName])
4949
.then(([interpreterExists, displayName, virtualEnvName]) => {
5050
const dislayNameSuffix = virtualEnvName.length > 0 ? ` (${virtualEnvName})` : '';
51-
this.statusBar.text = `${displayName}${dislayNameSuffix}`;;
51+
this.statusBar.text = `${displayName}${dislayNameSuffix}`;
5252

5353
if (!interpreterExists && displayName === defaultDisplayName && interpreters.length > 0) {
5454
this.statusBar.color = 'yellow';

src/client/providers/jediProxy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ function handleError(source: string, errorMessage: string) {
185185
logger.error(source + ' jediProxy', `Error (${source}) ${errorMessage}`);
186186
}
187187

188-
let spawnRetryAttempts = 0;;
188+
let spawnRetryAttempts = 0;
189189
function spawnProcess(dir: string) {
190190
try {
191191
let environmentVariables = { 'PYTHONUNBUFFERED': '1' };

0 commit comments

Comments
 (0)