From 17eac1ecbe8d69f3489c4bd1454f07a1bb3332a0 Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Mon, 18 Jan 2021 17:54:09 -0800 Subject: [PATCH 01/10] expose a setting to change the working directory of linters --- package.json | 6 ++++++ src/client/common/configSettings.ts | 5 +++++ src/client/common/types.ts | 1 + src/client/linters/baseLinter.ts | 5 ++++- src/test/linters/common.ts | 2 ++ src/test/mockClasses.ts | 1 + 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1fd3eccc220f..078c8850fe92 100644 --- a/package.json +++ b/package.json @@ -1269,6 +1269,12 @@ "description": "Whether to lint Python files.", "scope": "resource" }, + "python.linting.cwd": { + "type": "string", + "default": null, + "description": "Optional working directory for linters.", + "scope": "resource" + }, "python.linting.flake8Args": { "type": "array", "description": "Arguments passed in. Each argument is a separate item in the array.", diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index 779b081363a4..704a9ad4dcfc 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -316,6 +316,10 @@ export class PythonSettings implements IPythonSettings { this.linting = lintingSettings; } + if (this.linting.cwd) { + this.linting.cwd = getAbsolutePath(systemVariables.resolveAny(this.linting.cwd), workspaceRoot); + } + const analysisSettings = systemVariables.resolveAny(pythonSettings.get('analysis'))!; if (this.analysis) { Object.assign(this.analysis, analysisSettings); @@ -339,6 +343,7 @@ export class PythonSettings implements IPythonSettings { ? this.linting : { enabled: false, + cwd: '', ignorePatterns: [], flake8Args: [], flake8Enabled: false, diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 52f153ba163e..10d4bf33f344 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -258,6 +258,7 @@ export interface ILintingSettings { readonly pycodestyleCategorySeverity: IPycodestyleCategorySeverity; readonly flake8CategorySeverity: Flake8CategorySeverity; readonly mypyCategorySeverity: IMypyCategorySeverity; + cwd: string; prospectorPath: string; pylintPath: string; pycodestylePath: string; diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts index 565bdd5a9103..3b9e03b00297 100644 --- a/src/client/linters/baseLinter.ts +++ b/src/client/linters/baseLinter.ts @@ -138,7 +138,10 @@ export abstract class BaseLinter implements ILinter { return []; } const executionInfo = this.info.getExecutionInfo(args, document.uri); - const cwd = this.getWorkspaceRootPath(document); + const cwd = + this._pythonSettings.linting.cwd && this._pythonSettings.linting.cwd.length > 0 + ? this._pythonSettings.linting.cwd + : this.getWorkspaceRootPath(document); const pythonToolsExecutionService = this.serviceContainer.get( IPythonToolExecutionService, ); diff --git a/src/test/linters/common.ts b/src/test/linters/common.ts index 0f8d9bf79e81..e83ca5f4707e 100644 --- a/src/test/linters/common.ts +++ b/src/test/linters/common.ts @@ -70,6 +70,7 @@ export function throwUnknownProduct(product: Product) { export class LintingSettings { public enabled: boolean; + public cwd: string; public ignorePatterns: string[]; public prospectorEnabled: boolean; public prospectorArgs: string[]; @@ -107,6 +108,7 @@ export class LintingSettings { // mostly from configSettings.ts this.enabled = true; + this.cwd = ''; this.ignorePatterns = []; this.lintOnSave = false; this.maxNumberOfProblems = 100; diff --git a/src/test/mockClasses.ts b/src/test/mockClasses.ts index c5fa011b27cc..16e0f13be03e 100644 --- a/src/test/mockClasses.ts +++ b/src/test/mockClasses.ts @@ -54,6 +54,7 @@ export class MockStatusBarItem implements vscode.StatusBarItem { export class MockLintingSettings implements ILintingSettings { public enabled!: boolean; + public cwd!: string; public ignorePatterns!: string[]; public prospectorEnabled!: boolean; public prospectorArgs!: string[]; From 46050837ad82bbe3730dbaffefdd2bbf1202651f Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Mon, 18 Jan 2021 18:10:19 -0800 Subject: [PATCH 02/10] set linting cwd after linters --- src/client/common/configSettings.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index 704a9ad4dcfc..af576ba5a3e2 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -316,10 +316,6 @@ export class PythonSettings implements IPythonSettings { this.linting = lintingSettings; } - if (this.linting.cwd) { - this.linting.cwd = getAbsolutePath(systemVariables.resolveAny(this.linting.cwd), workspaceRoot); - } - const analysisSettings = systemVariables.resolveAny(pythonSettings.get('analysis'))!; if (this.analysis) { Object.assign(this.analysis, analysisSettings); @@ -414,6 +410,10 @@ export class PythonSettings implements IPythonSettings { this.linting.mypyPath = getAbsolutePath(systemVariables.resolveAny(this.linting.mypyPath), workspaceRoot); this.linting.banditPath = getAbsolutePath(systemVariables.resolveAny(this.linting.banditPath), workspaceRoot); + if (this.linting.cwd && this.linting.cwd.length > 0) { + this.linting.cwd = getAbsolutePath(systemVariables.resolveAny(this.linting.cwd), workspaceRoot); + } + const formattingSettings = systemVariables.resolveAny(pythonSettings.get('formatting'))!; if (this.formatting) { Object.assign(this.formatting, formattingSettings); From f954acd5f323ed51db6957816452c71b6c012e37 Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Mon, 18 Jan 2021 18:11:34 -0800 Subject: [PATCH 03/10] address ci failure --- news/1 Enhancements/15170.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/1 Enhancements/15170.md diff --git a/news/1 Enhancements/15170.md b/news/1 Enhancements/15170.md new file mode 100644 index 000000000000..9cf1cd64e2ad --- /dev/null +++ b/news/1 Enhancements/15170.md @@ -0,0 +1 @@ +Added `python.linting.cwd` to change the working directory of the linters \ No newline at end of file From c91d3c382fab5bf641a090d7ccf9c7e82e89571d Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Fri, 2 Apr 2021 12:31:44 -0700 Subject: [PATCH 04/10] default cwd setting should be undefined --- src/client/common/configSettings.ts | 4 ++-- src/client/common/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index af576ba5a3e2..d44ea117e7cb 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -339,7 +339,7 @@ export class PythonSettings implements IPythonSettings { ? this.linting : { enabled: false, - cwd: '', + cwd: undefined, ignorePatterns: [], flake8Args: [], flake8Enabled: false, @@ -410,7 +410,7 @@ export class PythonSettings implements IPythonSettings { this.linting.mypyPath = getAbsolutePath(systemVariables.resolveAny(this.linting.mypyPath), workspaceRoot); this.linting.banditPath = getAbsolutePath(systemVariables.resolveAny(this.linting.banditPath), workspaceRoot); - if (this.linting.cwd && this.linting.cwd.length > 0) { + if (this.linting.cwd) { this.linting.cwd = getAbsolutePath(systemVariables.resolveAny(this.linting.cwd), workspaceRoot); } diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 10d4bf33f344..1070197d7722 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -258,7 +258,7 @@ export interface ILintingSettings { readonly pycodestyleCategorySeverity: IPycodestyleCategorySeverity; readonly flake8CategorySeverity: Flake8CategorySeverity; readonly mypyCategorySeverity: IMypyCategorySeverity; - cwd: string; + cwd?: string; prospectorPath: string; pylintPath: string; pycodestylePath: string; From 52af2e098c80721a8f7c5c4427609f0cb252a0b2 Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Fri, 2 Apr 2021 12:32:47 -0700 Subject: [PATCH 05/10] add method that determines whether to return cwd or workspace root --- src/client/linters/baseLinter.ts | 9 +++++---- src/client/linters/prospector.ts | 2 +- src/client/linters/pylint.ts | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts index 3b9e03b00297..37fa11c843d3 100644 --- a/src/client/linters/baseLinter.ts +++ b/src/client/linters/baseLinter.ts @@ -101,6 +101,10 @@ export abstract class BaseLinter implements ILinter { workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string' ? workspaceFolder.uri.fsPath : undefined; return typeof workspaceRootPath === 'string' ? workspaceRootPath : path.dirname(document.uri.fsPath); } + + protected getWorkingDirectoryPath(document: vscode.TextDocument): string { + return this._pythonSettings.linting.cwd || this.getWorkspaceRootPath(document); + } protected abstract runLinter( document: vscode.TextDocument, cancellation: vscode.CancellationToken, @@ -138,10 +142,7 @@ export abstract class BaseLinter implements ILinter { return []; } const executionInfo = this.info.getExecutionInfo(args, document.uri); - const cwd = - this._pythonSettings.linting.cwd && this._pythonSettings.linting.cwd.length > 0 - ? this._pythonSettings.linting.cwd - : this.getWorkspaceRootPath(document); + const cwd = this.getWorkingDirectoryPath(document); const pythonToolsExecutionService = this.serviceContainer.get( IPythonToolExecutionService, ); diff --git a/src/client/linters/prospector.ts b/src/client/linters/prospector.ts index 106f3bfe9104..50524b2e3b33 100644 --- a/src/client/linters/prospector.ts +++ b/src/client/linters/prospector.ts @@ -30,7 +30,7 @@ export class Prospector extends BaseLinter { } protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - const cwd = this.getWorkspaceRootPath(document); + const cwd = this.getWorkingDirectoryPath(document); const relativePath = path.relative(cwd, document.uri.fsPath); return this.run(['--absolute-paths', '--output-format=json', relativePath], document, cancellation); } diff --git a/src/client/linters/pylint.ts b/src/client/linters/pylint.ts index f235d986fa90..495787568509 100644 --- a/src/client/linters/pylint.ts +++ b/src/client/linters/pylint.ts @@ -39,10 +39,10 @@ export class Pylint extends BaseLinter { this.info.linterArgs(uri).length === 0 && // Check pylintrc next to the file or above up to and including the workspace root !(await Pylint.hasConfigurationFileInWorkspace(this.fileSystem, path.dirname(uri.fsPath), workspaceRoot)) && - // Check for pylintrc at the root and above + // Check for pylintrc at the cwd and above !(await Pylint.hasConfigurationFile( this.fileSystem, - this.getWorkspaceRootPath(document), + this.getWorkingDirectoryPath(document), this.platformService, )) ) { From a565990dee74580ef16bef73fbf94840a939a013 Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Fri, 2 Apr 2021 12:33:13 -0700 Subject: [PATCH 06/10] add functional test that asserts no errors are thrown if cwd is set --- src/test/linters/lint.functional.test.ts | 13 +++++++++++++ src/test/pythonFiles/linting/cwd/.pylintrc | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 src/test/pythonFiles/linting/cwd/.pylintrc diff --git a/src/test/linters/lint.functional.test.ts b/src/test/linters/lint.functional.test.ts index 9263d4571aef..bf0c2f78a390 100644 --- a/src/test/linters/lint.functional.test.ts +++ b/src/test/linters/lint.functional.test.ts @@ -846,4 +846,17 @@ suite('Linting Functional Tests', () => { maxErrors, ); }); + + test('Linters use config in cwd directory', async () => { + const maxErrors = 0; + const fixture = new TestFixture(); + fixture.lintingSettings.cwd = path.join(pythonFilesDir, 'pylintcwd'); + + await testLinterMessageCount( + fixture, + Product.pylint, + path.join(pythonFilesDir, 'threeLineLints.py'), + maxErrors, + ); + }); }); diff --git a/src/test/pythonFiles/linting/cwd/.pylintrc b/src/test/pythonFiles/linting/cwd/.pylintrc new file mode 100644 index 000000000000..8530187c095f --- /dev/null +++ b/src/test/pythonFiles/linting/cwd/.pylintrc @@ -0,0 +1,2 @@ +[MESSAGES CONTROL] +disable=C0326,I0011,I0012,C0304,C0103,W0613,E0001,E1101 From 46968704be4bc682b755663fd30d88646d310871 Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Fri, 2 Apr 2021 12:36:58 -0700 Subject: [PATCH 07/10] add thanks to self --- news/1 Enhancements/15170.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/1 Enhancements/15170.md b/news/1 Enhancements/15170.md index 9cf1cd64e2ad..fde246e92891 100644 --- a/news/1 Enhancements/15170.md +++ b/news/1 Enhancements/15170.md @@ -1 +1 @@ -Added `python.linting.cwd` to change the working directory of the linters \ No newline at end of file +Added `python.linting.cwd` to change the working directory of the linters (thanks [Matthew Shirley](https://github.com/matthewshirley)) \ No newline at end of file From d2ec04f2931e1a06b8125887dd59891e57f6980e Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Fri, 2 Apr 2021 12:41:07 -0700 Subject: [PATCH 08/10] update test runner to force cwd path --- src/test/linters/pylint.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/linters/pylint.unit.test.ts b/src/test/linters/pylint.unit.test.ts index 8764286da897..a642d7400f00 100644 --- a/src/test/linters/pylint.unit.test.ts +++ b/src/test/linters/pylint.unit.test.ts @@ -354,7 +354,7 @@ suite('Pylint - Function runLinter()', () => { ): Promise { return super.runLinter(document, cancellation); } - public getWorkspaceRootPath(_document: vscode.TextDocument): string { + public getWorkingDirectoryPath(_document: vscode.TextDocument): string { return 'path/to/workspaceRoot'; } } From b3608215591e23db165e3e0fdb7a18843c0d488b Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Fri, 2 Apr 2021 13:03:37 -0700 Subject: [PATCH 09/10] update test cwd settings --- src/test/linters/common.ts | 4 ++-- src/test/mockClasses.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/linters/common.ts b/src/test/linters/common.ts index e83ca5f4707e..87a489cbbb31 100644 --- a/src/test/linters/common.ts +++ b/src/test/linters/common.ts @@ -70,7 +70,7 @@ export function throwUnknownProduct(product: Product) { export class LintingSettings { public enabled: boolean; - public cwd: string; + public cwd?: string; public ignorePatterns: string[]; public prospectorEnabled: boolean; public prospectorArgs: string[]; @@ -108,7 +108,7 @@ export class LintingSettings { // mostly from configSettings.ts this.enabled = true; - this.cwd = ''; + this.cwd = undefined; this.ignorePatterns = []; this.lintOnSave = false; this.maxNumberOfProblems = 100; diff --git a/src/test/mockClasses.ts b/src/test/mockClasses.ts index 16e0f13be03e..0404f18910e8 100644 --- a/src/test/mockClasses.ts +++ b/src/test/mockClasses.ts @@ -54,7 +54,7 @@ export class MockStatusBarItem implements vscode.StatusBarItem { export class MockLintingSettings implements ILintingSettings { public enabled!: boolean; - public cwd!: string; + public cwd?: string; public ignorePatterns!: string[]; public prospectorEnabled!: boolean; public prospectorArgs!: string[]; From b16f085a6a834d8c1bb84c555a1002c7cf4bc59a Mon Sep 17 00:00:00 2001 From: Matt Shirley Date: Fri, 2 Apr 2021 13:42:55 -0700 Subject: [PATCH 10/10] update mock to return undefined cwd --- src/test/linters/lint.args.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/linters/lint.args.test.ts b/src/test/linters/lint.args.test.ts index 579d991749da..0a86ca34253c 100644 --- a/src/test/linters/lint.args.test.ts +++ b/src/test/linters/lint.args.test.ts @@ -102,6 +102,7 @@ suite('Linting - Arguments', () => { const lintSettings = TypeMoq.Mock.ofType(); lintSettings.setup((x) => x.enabled).returns(() => true); lintSettings.setup((x) => x.lintOnSave).returns(() => true); + lintSettings.setup((x) => x.cwd).returns(() => undefined); settings = TypeMoq.Mock.ofType(); settings.setup((x) => x.linting).returns(() => lintSettings.object);