From 4b84651950efa2be1b4a0fa67a70d11132716acb Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Thu, 6 Jun 2024 15:25:01 -0700 Subject: [PATCH] Added the ability to override type checking configuration settings for each execution environment. This allows, for example, a test directory to use different settings than the directories that contain production code. This addresses #4059. --- docs/configuration.md | 1 + .../src/analyzer/sourceFile.ts | 2 +- .../src/common/configOptions.ts | 25 ++ .../pyright-internal/src/tests/config.test.ts | 4 +- .../pyright-internal/src/tests/testUtils.ts | 3 +- .../schemas/pyrightconfig.schema.json | 270 ++++++++++++++++++ 6 files changed, 302 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7e408276a..d792d9060 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -239,6 +239,7 @@ The following settings can be specified for each execution environment. - **pythonPlatform** [string, optional]: Specifies the target platform that will be used for this execution environment. If not specified, the global `pythonPlatform` setting is used instead. +In addition, any of the [type check diagnostics settings](configuration.md/type-check-diagnostics-settings) listed above can be specified. These settings act as overrides for the files in this execution environment. ## Sample Config File The following is an example of a pyright config file: diff --git a/packages/pyright-internal/src/analyzer/sourceFile.ts b/packages/pyright-internal/src/analyzer/sourceFile.ts index 54e79be63..550d824b1 100644 --- a/packages/pyright-internal/src/analyzer/sourceFile.ts +++ b/packages/pyright-internal/src/analyzer/sourceFile.ts @@ -723,7 +723,7 @@ export class SourceFile { this._diagnosticRuleSet = CommentUtils.getFileLevelDirectives( parseFileResults.tokenizerOutput.tokens, parseFileResults.tokenizerOutput.lines, - configOptions.diagnosticRuleSet, + execEnvironment.diagnosticRuleSet, useStrict, commentDiags ); diff --git a/packages/pyright-internal/src/common/configOptions.ts b/packages/pyright-internal/src/common/configOptions.ts index d0766ad92..97794e525 100644 --- a/packages/pyright-internal/src/common/configOptions.ts +++ b/packages/pyright-internal/src/common/configOptions.ts @@ -48,10 +48,14 @@ export class ExecutionEnvironment { // Default to no extra paths. extraPaths: Uri[] = []; + // Diagnostic rules with overrides. + diagnosticRuleSet: DiagnosticRuleSet; + // Default to "." which indicates every file in the project. constructor( name: string, root: Uri, + defaultDiagRuleSet: DiagnosticRuleSet, defaultPythonVersion: PythonVersion | undefined, defaultPythonPlatform: string | undefined, defaultExtraPaths: Uri[] | undefined @@ -61,6 +65,7 @@ export class ExecutionEnvironment { this.pythonVersion = defaultPythonVersion ?? latestStablePythonVersion; this.pythonPlatform = defaultPythonPlatform; this.extraPaths = Array.from(defaultExtraPaths ?? []); + this.diagnosticRuleSet = { ...defaultDiagRuleSet }; } } @@ -1070,6 +1075,7 @@ export class ConfigOptions { return new ExecutionEnvironment( this._getEnvironmentName(), this.projectRoot, + this.diagnosticRuleSet, this.defaultPythonVersion, this.defaultPythonPlatform, this.defaultExtraPaths @@ -1575,6 +1581,7 @@ export class ConfigOptions { const newExecEnv = new ExecutionEnvironment( this._getEnvironmentName(), configDirUri, + this.diagnosticRuleSet, this.defaultPythonVersion, this.defaultPythonPlatform, this.defaultExtraPaths @@ -1652,6 +1659,24 @@ export class ConfigOptions { } } + // Apply overrides from the config file for the boolean overrides. + getBooleanDiagnosticRules(/* includeNonOverridable */ true).forEach((ruleName) => { + (newExecEnv.diagnosticRuleSet as any)[ruleName] = this._convertBoolean( + envObj[ruleName], + ruleName, + newExecEnv.diagnosticRuleSet[ruleName] as boolean + ); + }); + + // Apply overrides from the config file for the diagnostic level overrides. + getDiagLevelDiagnosticRules().forEach((ruleName) => { + (newExecEnv.diagnosticRuleSet as any)[ruleName] = this._convertDiagnosticLevel( + envObj[ruleName], + ruleName, + newExecEnv.diagnosticRuleSet[ruleName] as DiagnosticLevel + ); + }); + return newExecEnv; } catch { console.error(`Config executionEnvironments index ${index} is not accessible.`); diff --git a/packages/pyright-internal/src/tests/config.test.ts b/packages/pyright-internal/src/tests/config.test.ts index ca6f624bb..f3f2f9dc9 100644 --- a/packages/pyright-internal/src/tests/config.test.ts +++ b/packages/pyright-internal/src/tests/config.test.ts @@ -12,7 +12,7 @@ import assert from 'assert'; import { AnalyzerService } from '../analyzer/service'; import { deserialize, serialize } from '../backgroundThreadBase'; import { CommandLineOptions } from '../common/commandLineOptions'; -import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions'; +import { ConfigOptions, ExecutionEnvironment, getStandardDiagnosticRuleSet } from '../common/configOptions'; import { ConsoleInterface, NullConsole } from '../common/console'; import { NoAccessHost } from '../common/host'; import { combinePaths, normalizePath, normalizeSlashes } from '../common/pathUtils'; @@ -170,6 +170,7 @@ test('FindExecEnv1', () => { const execEnv1 = new ExecutionEnvironment( 'python', cwd.resolvePaths('src/foo'), + getStandardDiagnosticRuleSet(), /* defaultPythonVersion */ undefined, /* defaultPythonPlatform */ undefined, /* defaultExtraPaths */ undefined @@ -178,6 +179,7 @@ test('FindExecEnv1', () => { const execEnv2 = new ExecutionEnvironment( 'python', cwd.resolvePaths('src'), + getStandardDiagnosticRuleSet(), /* defaultPythonVersion */ undefined, /* defaultPythonPlatform */ undefined, /* defaultExtraPaths */ undefined diff --git a/packages/pyright-internal/src/tests/testUtils.ts b/packages/pyright-internal/src/tests/testUtils.ts index 6927f5b0f..e68630c00 100644 --- a/packages/pyright-internal/src/tests/testUtils.ts +++ b/packages/pyright-internal/src/tests/testUtils.ts @@ -15,7 +15,7 @@ import { ImportResolver } from '../analyzer/importResolver'; import { Program } from '../analyzer/program'; import { NameTypeWalker } from '../analyzer/testWalker'; import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes'; -import { ConfigOptions, ExecutionEnvironment } from '../common/configOptions'; +import { ConfigOptions, ExecutionEnvironment, getStandardDiagnosticRuleSet } from '../common/configOptions'; import { ConsoleWithLogLevel, NullConsole } from '../common/console'; import { fail } from '../common/debug'; import { Diagnostic, DiagnosticCategory } from '../common/diagnostic'; @@ -74,6 +74,7 @@ export function parseSampleFile( execEnvironment = new ExecutionEnvironment( 'python', UriEx.file('.'), + getStandardDiagnosticRuleSet(), /* defaultPythonVersion */ undefined, /* defaultPythonPlatform */ undefined, /* defaultExtraPaths */ undefined diff --git a/packages/vscode-pyright/schemas/pyrightconfig.schema.json b/packages/vscode-pyright/schemas/pyrightconfig.schema.json index d3911e34a..ed72cf334 100644 --- a/packages/vscode-pyright/schemas/pyrightconfig.schema.json +++ b/packages/vscode-pyright/schemas/pyrightconfig.schema.json @@ -898,6 +898,276 @@ "default": "", "pattern": "^(.*)$" }, + "disableBytesTypePromotions": { + "$ref": "#/definitions/disableBytesTypePromotions" + }, + "strictListInference": { + "$ref": "#/definitions/strictListInference" + }, + "strictSetInference": { + "$ref": "#/definitions/strictSetInference" + }, + "strictDictionaryInference": { + "$ref": "#/definitions/strictDictionaryInference" + }, + "analyzeUnannotatedFunctions": { + "$ref": "#/definitions/analyzeUnannotatedFunctions" + }, + "strictParameterNoneValue": { + "$ref": "#/definitions/strictParameterNoneValue" + }, + "enableExperimentalFeatures": { + "$ref": "#/definitions/enableExperimentalFeatures" + }, + "enableTypeIgnoreComments": { + "$ref": "#/definitions/enableTypeIgnoreComments" + }, + "deprecateTypingAliases": { + "$ref": "#/definitions/deprecateTypingAliases" + }, + "reportGeneralTypeIssues": { + "$ref": "#/definitions/reportGeneralTypeIssues" + }, + "reportPropertyTypeMismatch": { + "$ref": "#/definitions/reportPropertyTypeMismatch" + }, + "reportFunctionMemberAccess": { + "$ref": "#/definitions/reportFunctionMemberAccess" + }, + "reportMissingImports": { + "$ref": "#/definitions/reportMissingImports" + }, + "reportMissingModuleSource": { + "$ref": "#/definitions/reportMissingModuleSource" + }, + "reportInvalidTypeForm": { + "$ref": "#/definitions/reportInvalidTypeForm" + }, + "reportMissingTypeStubs": { + "$ref": "#/definitions/reportMissingTypeStubs" + }, + "reportImportCycles": { + "$ref": "#/definitions/reportImportCycles" + }, + "reportUnusedImport": { + "$ref": "#/definitions/reportUnusedImport" + }, + "reportUnusedClass": { + "$ref": "#/definitions/reportUnusedClass" + }, + "reportUnusedFunction": { + "$ref": "#/definitions/reportUnusedFunction" + }, + "reportUnusedVariable": { + "$ref": "#/definitions/reportUnusedVariable" + }, + "reportDuplicateImport": { + "$ref": "#/definitions/reportDuplicateImport" + }, + "reportWildcardImportFromLibrary": { + "$ref": "#/definitions/reportWildcardImportFromLibrary" + }, + "reportAbstractUsage": { + "$ref": "#/definitions/reportAbstractUsage" + }, + "reportArgumentType": { + "$ref": "#/definitions/reportArgumentType" + }, + "reportAssertTypeFailure": { + "$ref": "#/definitions/reportAssertTypeFailure" + }, + "reportAssignmentType": { + "$ref": "#/definitions/reportAssignmentType" + }, + "reportAttributeAccessIssue": { + "$ref": "#/definitions/reportAttributeAccessIssue" + }, + "reportCallIssue": { + "$ref": "#/definitions/reportCallIssue" + }, + "reportInconsistentOverload": { + "$ref": "#/definitions/reportInconsistentOverload" + }, + "reportIndexIssue": { + "$ref": "#/definitions/reportIndexIssue" + }, + "reportInvalidTypeArguments": { + "$ref": "#/definitions/reportInvalidTypeArguments" + }, + "reportNoOverloadImplementation": { + "$ref": "#/definitions/reportNoOverloadImplementation" + }, + "reportOperatorIssue": { + "$ref": "#/definitions/reportOperatorIssue" + }, + "reportOptionalSubscript": { + "$ref": "#/definitions/reportOptionalSubscript" + }, + "reportOptionalMemberAccess": { + "$ref": "#/definitions/reportOptionalMemberAccess" + }, + "reportOptionalCall": { + "$ref": "#/definitions/reportOptionalCall" + }, + "reportOptionalIterable": { + "$ref": "#/definitions/reportOptionalIterable" + }, + "reportOptionalContextManager": { + "$ref": "#/definitions/reportOptionalContextManager" + }, + "reportOptionalOperand": { + "$ref": "#/definitions/reportOptionalOperand" + }, + "reportRedeclaration": { + "$ref": "#/definitions/reportRedeclaration" + }, + "reportReturnType": { + "$ref": "#/definitions/reportReturnType" + }, + "reportTypedDictNotRequiredAccess": { + "$ref": "#/definitions/reportTypedDictNotRequiredAccess" + }, + "reportUntypedFunctionDecorator": { + "$ref": "#/definitions/reportUntypedFunctionDecorator" + }, + "reportUntypedClassDecorator": { + "$ref": "#/definitions/reportUntypedClassDecorator" + }, + "reportUntypedBaseClass": { + "$ref": "#/definitions/reportUntypedBaseClass" + }, + "reportUntypedNamedTuple": { + "$ref": "#/definitions/reportUntypedNamedTuple" + }, + "reportPrivateUsage": { + "$ref": "#/definitions/reportPrivateUsage" + }, + "reportTypeCommentUsage": { + "$ref": "#/definitions/reportTypeCommentUsage" + }, + "reportPrivateImportUsage": { + "$ref": "#/definitions/reportPrivateImportUsage" + }, + "reportConstantRedefinition": { + "$ref": "#/definitions/reportConstantRedefinition" + }, + "reportDeprecated": { + "$ref": "#/definitions/reportDeprecated" + }, + "reportIncompatibleMethodOverride": { + "$ref": "#/definitions/reportIncompatibleMethodOverride" + }, + "reportIncompatibleVariableOverride": { + "$ref": "#/definitions/reportIncompatibleVariableOverride" + }, + "reportInconsistentConstructor": { + "$ref": "#/definitions/reportInconsistentConstructor" + }, + "reportOverlappingOverload": { + "$ref": "#/definitions/reportOverlappingOverload" + }, + "reportPossiblyUnboundVariable": { + "$ref": "#/definitions/reportPossiblyUnboundVariable" + }, + "reportMissingSuperCall": { + "$ref": "#/definitions/reportMissingSuperCall" + }, + "reportUninitializedInstanceVariable": { + "$ref": "#/definitions/reportUninitializedInstanceVariable" + }, + "reportInvalidStringEscapeSequence": { + "$ref": "#/definitions/reportInvalidStringEscapeSequence" + }, + "reportUnknownParameterType": { + "$ref": "#/definitions/reportUnknownParameterType" + }, + "reportUnknownArgumentType": { + "$ref": "#/definitions/reportUnknownArgumentType" + }, + "reportUnknownLambdaType": { + "$ref": "#/definitions/reportUnknownLambdaType" + }, + "reportUnknownVariableType": { + "$ref": "#/definitions/reportUnknownVariableType" + }, + "reportUnknownMemberType": { + "$ref": "#/definitions/reportUnknownMemberType" + }, + "reportMissingParameterType": { + "$ref": "#/definitions/reportMissingParameterType" + }, + "reportMissingTypeArgument": { + "$ref": "#/definitions/reportMissingTypeArgument" + }, + "reportInvalidTypeVarUse": { + "$ref": "#/definitions/reportInvalidTypeVarUse" + }, + "reportCallInDefaultInitializer": { + "$ref": "#/definitions/reportCallInDefaultInitializer" + }, + "reportUnnecessaryIsInstance": { + "$ref": "#/definitions/reportUnnecessaryIsInstance" + }, + "reportUnnecessaryCast": { + "$ref": "#/definitions/reportUnnecessaryCast" + }, + "reportUnnecessaryComparison": { + "$ref": "#/definitions/reportUnnecessaryComparison" + }, + "reportUnnecessaryContains": { + "$ref": "#/definitions/reportUnnecessaryContains" + }, + "reportAssertAlwaysTrue": { + "$ref": "#/definitions/reportAssertAlwaysTrue" + }, + "reportSelfClsParameterName": { + "$ref": "#/definitions/reportSelfClsParameterName" + }, + "reportImplicitStringConcatenation": { + "$ref": "#/definitions/reportImplicitStringConcatenation" + }, + "reportUnboundVariable": { + "$ref": "#/definitions/reportUnboundVariable" + }, + "reportUnhashable": { + "$ref": "#/definitions/reportUnhashable" + }, + "reportUndefinedVariable": { + "$ref": "#/definitions/reportUndefinedVariable" + }, + "reportInvalidStubStatement": { + "$ref": "#/definitions/reportInvalidStubStatement" + }, + "reportIncompleteStub": { + "$ref": "#/definitions/reportIncompleteStub" + }, + "reportUnsupportedDunderAll": { + "$ref": "#/definitions/reportUnsupportedDunderAll" + }, + "reportUnusedCallResult": { + "$ref": "#/definitions/reportUnusedCallResult" + }, + "reportUnusedCoroutine": { + "$ref": "#/definitions/reportUnusedCoroutine" + }, + "reportUnusedExcept": { + "$ref": "#/definitions/reportUnusedExcept" + }, + "reportUnusedExpression": { + "$ref": "#/definitions/reportUnusedExpression" + }, + "reportUnnecessaryTypeIgnoreComment": { + "$ref": "#/definitions/reportUnnecessaryTypeIgnoreComment" + }, + "reportMatchNotExhaustive": { + "$ref": "#/definitions/reportMatchNotExhaustive" + }, + "reportShadowedImports": { + "$ref": "#/definitions/reportShadowedImports" + }, + "reportImplicitOverride": { + "$ref": "#/definitions/reportImplicitOverride" + }, "extraPaths": { "$ref": "#/definitions/extraPaths" },