Skip to content

Commit 4b34f7d

Browse files
authored
Fix linters to make use of the new python code execution framework (#360)
Fixes #351 * Refactored linter code to make use of new execution framework * Reverted to two output windows with hardcoded names
1 parent bed6d17 commit 4b34f7d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3418
-2398
lines changed

package-lock.json

Lines changed: 1007 additions & 95 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1570 additions & 1588 deletions
Large diffs are not rendered by default.

src/client/common/configSettings.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ export interface IUnitTestSettings {
4444
pyTestArgs: string[];
4545
unittestEnabled: boolean;
4646
unittestArgs: string[];
47-
outputWindow: string;
4847
cwd?: string;
4948
}
5049
export interface IPylintCategorySeverity {
@@ -96,7 +95,6 @@ export interface ILintingSettings {
9695
pylamaPath: string;
9796
flake8Path: string;
9897
pydocstylePath: string;
99-
outputWindow: string;
10098
mypyEnabled: boolean;
10199
mypyArgs: string[];
102100
mypyPath: string;
@@ -107,7 +105,6 @@ export interface IFormattingSettings {
107105
autopep8Args: string[];
108106
yapfPath: string;
109107
yapfArgs: string[];
110-
outputWindow: string;
111108
}
112109
export interface IAutoCompeteSettings {
113110
addBrackets: boolean;
@@ -244,7 +241,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
244241
flake8Args: [], flake8Enabled: false, flake8Path: 'flake',
245242
lintOnSave: false, maxNumberOfProblems: 100,
246243
mypyArgs: [], mypyEnabled: false, mypyPath: 'mypy',
247-
outputWindow: 'python', pep8Args: [], pep8Enabled: false, pep8Path: 'pep8',
244+
pep8Args: [], pep8Enabled: false, pep8Path: 'pep8',
248245
pylamaArgs: [], pylamaEnabled: false, pylamaPath: 'pylama',
249246
prospectorArgs: [], prospectorEnabled: false, prospectorPath: 'prospector',
250247
pydocstyleArgs: [], pydocstyleEnabled: false, pydocstylePath: 'pydocstyle',
@@ -288,7 +285,6 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
288285
// Support for travis.
289286
this.formatting = this.formatting ? this.formatting : {
290287
autopep8Args: [], autopep8Path: 'autopep8',
291-
outputWindow: 'python',
292288
provider: 'autopep8',
293289
yapfArgs: [], yapfPath: 'yapf'
294290
};
@@ -339,7 +335,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
339335
nosetestArgs: [], pyTestArgs: [], unittestArgs: [],
340336
promptToConfigure: true, debugPort: 3000,
341337
nosetestsEnabled: false, pyTestEnabled: false, unittestEnabled: false,
342-
nosetestPath: 'nosetests', pyTestPath: 'py.test', outputWindow: 'Python Test Log'
338+
nosetestPath: 'nosetests', pyTestPath: 'py.test'
343339
} as IUnitTestSettings;
344340
}
345341
}
@@ -349,7 +345,6 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
349345
promptToConfigure: true,
350346
debugPort: 3000,
351347
nosetestArgs: [], nosetestPath: 'nosetest', nosetestsEnabled: false,
352-
outputWindow: 'python',
353348
pyTestArgs: [], pyTestEnabled: false, pyTestPath: 'pytest',
354349
unittestArgs: [], unittestEnabled: false
355350
};

src/client/common/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ export namespace LinterErrors {
5757
export const InvalidSyntax = 'E999';
5858
}
5959
}
60+
61+
export const STANDARD_OUTPUT_CHANNEL = 'STANDARD_OUTPUT_CHANNEL';

src/client/common/extensions.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
/**
5+
* @typedef {Object} SplitLinesOptions
6+
* @property {boolean} [trim=true] - Whether to trim the lines.
7+
* @property {boolean} [removeEmptyEntries=true] - Whether to remove empty entries.
8+
*/
9+
10+
// tslint:disable-next-line:interface-name
11+
interface String {
12+
/**
13+
* Split a string using the cr and lf characters and return them as an array.
14+
* By default lines are trimmed and empty lines are removed.
15+
* @param {SplitLinesOptions=} splitOptions - Options used for splitting the string.
16+
*/
17+
splitLines(splitOptions?: { trim: boolean, removeEmptyEntries: boolean }): string[];
18+
}
19+
20+
/**
21+
* Split a string using the cr and lf characters and return them as an array.
22+
* By default lines are trimmed and empty lines are removed.
23+
* @param {SplitLinesOptions=} splitOptions - Options used for splitting the string.
24+
*/
25+
String.prototype.splitLines = function (this: string, splitOptions: { trim: boolean, removeEmptyEntries: boolean } = { removeEmptyEntries: true, trim: true }): string[] {
26+
let lines = this.split(/\r?\n/g);
27+
if (splitOptions && splitOptions.trim) {
28+
lines = lines.filter(line => line.trim());
29+
}
30+
if (splitOptions && splitOptions.removeEmptyEntries) {
31+
lines = lines.filter(line => line.length > 0);
32+
}
33+
return lines;
34+
};

src/client/common/installer.ts

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
1+
import { inject, injectable, named } from 'inversify';
12
import * as os from 'os';
2-
import * as vscode from 'vscode';
3+
import 'reflect-metadata';
4+
import 'reflect-metadata';
35
import { ConfigurationTarget, Uri, window, workspace } from 'vscode';
6+
import * as vscode from 'vscode';
47
import * as settings from './configSettings';
8+
import { STANDARD_OUTPUT_CHANNEL } from './constants';
59
import { isNotInstalledError } from './helpers';
10+
import { IInstaller, InstallerResponse, IOutputChannel, Product } from './types';
611
import { execPythonFile, getFullyQualifiedPythonInterpreterPath, IS_WINDOWS } from './utils';
712

8-
export enum Product {
9-
pytest = 1,
10-
nosetest = 2,
11-
pylint = 3,
12-
flake8 = 4,
13-
pep8 = 5,
14-
pylama = 6,
15-
prospector = 7,
16-
pydocstyle = 8,
17-
yapf = 9,
18-
autopep8 = 10,
19-
mypy = 11,
20-
unittest = 12,
21-
ctags = 13,
22-
rope = 14
23-
}
13+
export { Product } from './types';
2414

2515
// tslint:disable-next-line:variable-name
2616
const ProductInstallScripts = new Map<Product, string[]>();
@@ -156,15 +146,11 @@ ProductTypes.set(Product.rope, ProductType.RefactoringLibrary);
156146

157147
const IS_POWERSHELL = /powershell.exe$/i;
158148

159-
export enum InstallerResponse {
160-
Installed,
161-
Disabled,
162-
Ignore
163-
}
164-
export class Installer implements vscode.Disposable {
149+
@injectable()
150+
export class Installer implements IInstaller {
165151
private static terminal: vscode.Terminal | undefined | null;
166152
private disposables: vscode.Disposable[] = [];
167-
constructor(private outputChannel?: vscode.OutputChannel) {
153+
constructor( @inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private outputChannel?: vscode.OutputChannel) {
168154
this.disposables.push(vscode.window.onDidCloseTerminal(term => {
169155
if (term === Installer.terminal) {
170156
Installer.terminal = null;

src/client/common/logger.ts

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,23 @@
1-
import * as vscode from "vscode";
2-
import * as settings from "./configSettings";
1+
import { injectable } from 'inversify';
2+
import 'reflect-metadata';
3+
import { ILogger } from './types';
34

4-
let outChannel: vscode.OutputChannel;
5+
const PREFIX = 'Python Extension: ';
56

6-
class Logger {
7-
public static IsDebug: boolean;
8-
static initializeChannel() {
9-
if (settings.PythonSettings.getInstance().devOptions.indexOf("DEBUG") >= 0) {
10-
Logger.IsDebug = true;
11-
outChannel = vscode.window.createOutputChannel("PythonExtLog");
12-
}
7+
@injectable()
8+
export class Logger implements ILogger {
9+
public logError(message: string, ex?: Error) {
10+
console.error(`${PREFIX}${message}`, error);
1311
}
14-
15-
static write(category: string = "log", title: string = "", message: any) {
16-
Logger.initializeChannel();
17-
if (title.length > 0) {
18-
Logger.writeLine(category, "---------------------------");
19-
Logger.writeLine(category, title);
20-
}
21-
22-
Logger.writeLine(category, message);
12+
public logWarning(message: string, ex?: Error) {
13+
console.warn(`${PREFIX}${message}`, ex);
2314
}
24-
static writeLine(category: string = "log", line: any) {
25-
if (process.env['VSC_PYTHON_CI_TEST'] !== '1') {
26-
console[category](line);
27-
}
28-
if (outChannel) {
29-
outChannel.appendLine(line);
30-
}
31-
}
32-
}
33-
export function error(title: string = "", message: any) {
34-
Logger.write.apply(Logger, ["error", title, message]);
3515
}
36-
export function warn(title: string = "", message: any) {
37-
Logger.write.apply(Logger, ["warn", title, message]);
16+
// tslint:disable-next-line:no-any
17+
export function error(title: string = '', message: any) {
18+
new Logger().logError(`${title}, ${message}`);
3819
}
39-
export function log(title: string = "", message: any) {
40-
if (!Logger.IsDebug) return;
41-
Logger.write.apply(Logger, ["log", title, message]);
20+
// tslint:disable-next-line:no-any
21+
export function warn(title: string = '', message: any) {
22+
new Logger().logWarning(`${title}, ${message}`);
4223
}

src/client/common/serviceRegistry.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22
// Licensed under the MIT License.
33

44
import 'reflect-metadata';
5+
import { Disposable } from 'vscode';
56
import { IServiceManager } from '../ioc/types';
7+
import { Installer } from './installer';
8+
import { Logger } from './logger';
69
import { PersistentStateFactory } from './persistentState';
710
import { IS_WINDOWS as isWindows } from './platform/constants';
8-
import { IPersistentStateFactory, IsWindows } from './types';
11+
import { IDiposableRegistry, IInstaller, ILogger, IPersistentStateFactory, IsWindows } from './types';
912

1013
export function registerTypes(serviceManager: IServiceManager) {
1114
serviceManager.addSingletonInstance<boolean>(IsWindows, isWindows);
1215
serviceManager.addSingleton<IPersistentStateFactory>(IPersistentStateFactory, PersistentStateFactory);
16+
serviceManager.addSingleton<IInstaller>(IInstaller, Installer);
17+
serviceManager.addSingleton<ILogger>(ILogger, Logger);
18+
19+
const disposableRegistry = serviceManager.get<Disposable[]>(IDiposableRegistry);
20+
disposableRegistry.push(serviceManager.get<IInstaller>(IInstaller));
1321
}

src/client/common/types.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
import { Disposable, Uri } from 'vscode';
5+
46
export const IOutputChannel = Symbol('IOutputChannel');
57
export const IDocumentSymbolProvider = Symbol('IDocumentSymbolProvider');
68
export const IsWindows = Symbol('IS_WINDOWS');
@@ -19,3 +21,49 @@ export interface IPersistentStateFactory {
1921
createGlobalPersistentState<T>(key: string, defaultValue: T): IPersistentState<T>;
2022
createWorkspacePersistentState<T>(key: string, defaultValue: T): IPersistentState<T>;
2123
}
24+
25+
export type ExecutionInfo = {
26+
execPath: string;
27+
moduleName?: string;
28+
args: string[];
29+
product?: Product;
30+
};
31+
32+
export const ILogger = Symbol('ILogger');
33+
34+
export interface ILogger {
35+
logError(message: string, error?: Error);
36+
logWarning(message: string, error?: Error);
37+
}
38+
39+
export enum InstallerResponse {
40+
Installed,
41+
Disabled,
42+
Ignore
43+
}
44+
45+
export enum Product {
46+
pytest = 1,
47+
nosetest = 2,
48+
pylint = 3,
49+
flake8 = 4,
50+
pep8 = 5,
51+
pylama = 6,
52+
prospector = 7,
53+
pydocstyle = 8,
54+
yapf = 9,
55+
autopep8 = 10,
56+
mypy = 11,
57+
unittest = 12,
58+
ctags = 13,
59+
rope = 14
60+
}
61+
62+
export const IInstaller = Symbol('IInstaller');
63+
64+
export interface IInstaller extends Disposable {
65+
promptToInstall(product: Product, resource?: Uri): Promise<InstallerResponse>;
66+
install(product: Product, resource?: Uri): Promise<InstallerResponse>;
67+
isInstalled(product: Product, resource?: Uri): Promise<boolean | undefined>;
68+
disableLinter(product: Product, resource?: Uri): Promise<void>;
69+
}

0 commit comments

Comments
 (0)