diff --git a/docs/benefits-over-pyright/baseline.md b/docs/benefits-over-pyright/baseline.md index ec01e6f1e..ced9d392b 100644 --- a/docs/benefits-over-pyright/baseline.md +++ b/docs/benefits-over-pyright/baseline.md @@ -2,19 +2,27 @@ have you ever wanted to adopt a new tool or enable new checks in an existing project, only to be immediately bombarded with thousands of errors you'd have to fix? baseline solves this problem by allowing you to only report errors on new or modified code. it works by generating a baseline file keeping track of the existing errors in your project so that only errors in newly written or modified code get reported. -to enable baseline, run `basedpyright --writebaseline` in your terminal or run the _"basedpyright: Write new errors to baseline"_ task in your editor. this will generate a baseline file at `./.basedpyright/baseline.json` your project. you should commit this file so others working on your project can benefit from it too. +to enable baseline, run `basedpyright --writebaseline` in your terminal or run the _"basedpyright: Write new errors to baseline"_ task in your editor. this will generate a baseline file at `./.basedpyright/baseline.json` in your project. you should commit this file so others working on your project can benefit from it too. you can customize the baseline file path [using the `baselineFile` setting](../configuration/config-files.md#baselineFile) or [using the `--baselinefile` CLI argument](../configuration/command-line.md#command-line). ## how often do i need to update the baseline file? -this file gets automatically updated as errors are removed over time in both the CLI and the language server. you should only manually run the write baseline command in the following scenarios: +by default, this file gets automatically updated as errors are removed over time in both the CLI and the language server. you should only manually run the write baseline command in the following scenarios: - a baselined error incorrectly resurfaces when updating unrelated code - you're enabling a new diagnostic rule and want to baseline all the new errors it reported if you need to suppress a diagnostic for another reason, consider using [a `# pyright: ignore` comment](../configuration/comments.md#prefer-pyrightignore-comments) instead. +## disabling automatic updates for baselined error removals + +if you want more control over when the baseline file is updated, use the `baselineMode` setting in either the [language server](../configuration/language-server-settings.md) or [the CLI](../configuration/command-line.md#option-2-baselinemode-experimental). for example, using the `discard` mode will prevent the baseline file from being automatically updated when baselined errors are removed. + +!!! tip + + if you disable automatic baseline updates in the language server, a potential alternative workflow for still having the baseline file updated with removed errors is to set up a [prek hook](../installation/prek-hook.md) in your project to run the basedpyright CLI. this would take care of error removals at commit time instead of during editor saves. + ## how does it work? each baselined error is stored and matched by the following details: diff --git a/docs/configuration/language-server-settings.md b/docs/configuration/language-server-settings.md index 0c1fafab1..7f06c2976 100644 --- a/docs/configuration/language-server-settings.md +++ b/docs/configuration/language-server-settings.md @@ -65,6 +65,8 @@ the following settings are exclusive to basedpyright **basedpyright.analysis.configFilePath** [path]: Path to the directory or file containing the Pyright configuration (`pyrightconfig.json` or `pyproject.toml`). If a directory is specified, basedpyright will search for the config file in that directory. This is useful for monorepo structures where the config file is in a subdirectory rather than the workspace root. For example, if your Python code is in a `backend/` subdirectory with its own `pyproject.toml`, you can set this to `${workspaceFolder}/backend` to make basedpyright use that configuration file instead of searching from the workspace root. +**basedpyright.analysis.baselineMode** ["auto", "discard"]: Controls how the baseline file is updated when files are saved. Use `auto` to automatically remove fixed errors from the baseline (default), or `discard` to prevent automatic updates. [more info](../benefits-over-pyright/baseline.md) + ### discouraged settings diff --git a/packages/pyright-internal/src/baseline.ts b/packages/pyright-internal/src/baseline.ts index aa63d8e05..ca8bad0a4 100644 --- a/packages/pyright-internal/src/baseline.ts +++ b/packages/pyright-internal/src/baseline.ts @@ -27,8 +27,12 @@ export interface BaselinedDiagnostic { }; } +// baseline modes allowed in LSP server settings // TODO: add 'ignore' mode https://github.com/DetachHead/basedpyright/issues/1524 -export const baselineModes = ['discard', 'auto', 'lock'] as const; +export const serverBaselineModes = ['discard', 'auto'] as const; +export type ServerBaselineMode = (typeof serverBaselineModes)[number]; + +export const baselineModes = [...serverBaselineModes, 'lock'] as const; // 'force' is not a real value for `--baselinemode`. we just use it to represent `--writebaseline` export type BaselineMode = (typeof baselineModes)[number] | 'force'; diff --git a/packages/pyright-internal/src/common/languageServerInterface.ts b/packages/pyright-internal/src/common/languageServerInterface.ts index 6a942253a..8a3756590 100644 --- a/packages/pyright-internal/src/common/languageServerInterface.ts +++ b/packages/pyright-internal/src/common/languageServerInterface.ts @@ -8,6 +8,7 @@ import { MaxAnalysisTime } from '../analyzer/program'; import { IBackgroundAnalysis } from '../backgroundAnalysisBase'; +import { ServerBaselineMode } from '../baseline'; import { InlayHintSettings, Workspace } from '../workspaceFactory'; import { DiagnosticBooleanOverridesMap, DiagnosticSeverityOverridesMap } from './commandLineOptions'; import { SignatureDisplayType } from './configOptions'; @@ -28,6 +29,7 @@ export interface ServerSettings { typeCheckingMode?: string | undefined; useLibraryCodeForTypes?: boolean | undefined; baselineFile?: Uri | undefined; + baselineMode?: ServerBaselineMode | undefined; configFilePath?: Uri | undefined; disableLanguageServices?: boolean | undefined; disableTaggedHints?: boolean | undefined; diff --git a/packages/pyright-internal/src/languageServerBase.ts b/packages/pyright-internal/src/languageServerBase.ts index 3e0d57153..755209e7a 100644 --- a/packages/pyright-internal/src/languageServerBase.ts +++ b/packages/pyright-internal/src/languageServerBase.ts @@ -444,6 +444,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis workspace.useTypingExtensions = serverSettings.useTypingExtensions ?? false; workspace.fileEnumerationTimeoutInSec = serverSettings.fileEnumerationTimeoutInSec ?? 10; workspace.autoFormatStrings = serverSettings.autoFormatStrings ?? true; + workspace.baselineMode = serverSettings.baselineMode ?? 'auto'; } finally { // Don't use workspace.isInitialized directly since it might have been // reset due to pending config change event. @@ -1864,10 +1865,14 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis filesRequiringBaselineUpdate.get(workspace)!.push(fileDiagnostics); } for (const [workspace, files] of filesRequiringBaselineUpdate.entries()) { - if (!workspace.rootUri) { + if (!workspace.rootUri || workspace.baselineMode === 'discard') { continue; } - const baselineDiffSummary = workspace.service.backgroundAnalysisProgram.writeBaseline('auto', false, files); + const baselineDiffSummary = workspace.service.backgroundAnalysisProgram.writeBaseline( + workspace.baselineMode, + false, + files + ); if (baselineDiffSummary) { this.console.info( `${baselineDiffSummary}. files: ${files.map((file) => file.fileUri.toString()).join(', ')}` diff --git a/packages/pyright-internal/src/realLanguageServer.ts b/packages/pyright-internal/src/realLanguageServer.ts index e482a16b0..cbdeb8869 100644 --- a/packages/pyright-internal/src/realLanguageServer.ts +++ b/packages/pyright-internal/src/realLanguageServer.ts @@ -21,6 +21,7 @@ import { AnalysisResults } from './analyzer/analysis'; import { CacheManager } from './analyzer/cacheManager'; import { ImportResolver } from './analyzer/importResolver'; import { isPythonBinary } from './analyzer/pythonPathUtils'; +import { serverBaselineModes, ServerBaselineMode } from './baseline'; import { CommandController } from './commands/commandController'; import { ConfigOptions, SignatureDisplayType } from './common/configOptions'; import { ConsoleWithLogLevel, LogLevel, convertLogLevel } from './common/console'; @@ -173,6 +174,11 @@ export abstract class RealLanguageServer extends LanguageServerBase { serverSettings.baselineFile = resolvePathWithEnvVariables(workspace, baselineFile, workspaces); } + const baselineMode = pythonAnalysisSection.baselineMode; + if (serverBaselineModes.includes(baselineMode)) { + serverSettings.baselineMode = baselineMode as ServerBaselineMode; + } + const configFilePath = pythonAnalysisSection.configFilePath; if (configFilePath && isString(configFilePath)) { serverSettings.configFilePath = resolvePathWithEnvVariables(workspace, configFilePath, workspaces); diff --git a/packages/pyright-internal/src/tests/envVarUtils.test.ts b/packages/pyright-internal/src/tests/envVarUtils.test.ts index 19b7caded..2924f9d7e 100644 --- a/packages/pyright-internal/src/tests/envVarUtils.test.ts +++ b/packages/pyright-internal/src/tests/envVarUtils.test.ts @@ -227,5 +227,6 @@ function createWorkspace(rootUri: Uri | undefined): Workspace { useTypingExtensions: false, fileEnumerationTimeoutInSec: 10, autoFormatStrings: true, + baselineMode: 'auto', }; } diff --git a/packages/pyright-internal/src/tests/harness/fourslash/testLanguageService.ts b/packages/pyright-internal/src/tests/harness/fourslash/testLanguageService.ts index 2e6b55616..978db695b 100644 --- a/packages/pyright-internal/src/tests/harness/fourslash/testLanguageService.ts +++ b/packages/pyright-internal/src/tests/harness/fourslash/testLanguageService.ts @@ -129,6 +129,7 @@ export class TestLanguageService implements LanguageServerInterface { useTypingExtensions: false, fileEnumerationTimeoutInSec: 10, autoFormatStrings: true, + baselineMode: 'auto', }; } /** unlike the real one, this test implementation doesn't support notebook cells. TODO: language server tests for notebook cells */ diff --git a/packages/pyright-internal/src/tests/harness/fourslash/testState.ts b/packages/pyright-internal/src/tests/harness/fourslash/testState.ts index e45a3f820..66d0cb231 100644 --- a/packages/pyright-internal/src/tests/harness/fourslash/testState.ts +++ b/packages/pyright-internal/src/tests/harness/fourslash/testState.ts @@ -217,6 +217,7 @@ export class TestState { useTypingExtensions: false, fileEnumerationTimeoutInSec: 10, autoFormatStrings: true, + baselineMode: 'auto', }; if (!delayFileInitialization) { diff --git a/packages/pyright-internal/src/workspaceFactory.ts b/packages/pyright-internal/src/workspaceFactory.ts index 88d84563f..092589dce 100644 --- a/packages/pyright-internal/src/workspaceFactory.ts +++ b/packages/pyright-internal/src/workspaceFactory.ts @@ -11,6 +11,7 @@ import { } from 'vscode-languageserver'; import { AnalyzerService } from './analyzer/service'; +import { ServerBaselineMode } from './baseline'; import { ConsoleInterface } from './common/console'; import { createDeferred } from './common/deferred'; import { ServiceProvider } from './common/serviceProvider'; @@ -103,6 +104,7 @@ export interface Workspace extends WorkspaceFolder { useTypingExtensions: boolean; fileEnumerationTimeoutInSec: number; autoFormatStrings: boolean; + baselineMode: ServerBaselineMode; } export interface NormalWorkspace extends Workspace { @@ -305,6 +307,7 @@ export class WorkspaceFactory implements IWorkspaceFactory { useTypingExtensions: false, fileEnumerationTimeoutInSec: 10, autoFormatStrings: true, + baselineMode: 'auto', }; // Stick in our map diff --git a/packages/vscode-pyright/package.json b/packages/vscode-pyright/package.json index 1cc04d733..4a42cfa6f 100644 --- a/packages/vscode-pyright/package.json +++ b/packages/vscode-pyright/package.json @@ -170,6 +170,20 @@ "markdownDescription": "Path to the baseline file. Defaults to `./.basedpyright/baseline.json`", "scope": "resource" }, + "basedpyright.analysis.baselineMode": { + "type": "string", + "default": "auto", + "enum": [ + "auto", + "discard" + ], + "enumDescriptions": [ + "Automatically update the baseline file when baselined errors are fixed.", + "Never update the baseline file automatically." + ], + "markdownDescription": "Controls how the baseline file is updated when files are saved.", + "scope": "resource" + }, "basedpyright.analysis.configFilePath": { "type": "string", "default": "",