diff --git a/docs/settings.md b/docs/settings.md index 8f0d0e804..7f93f767e 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -18,8 +18,14 @@ The Pyright language server honors the following settings. **python.analysis.diagnosticSeverityOverrides** [map]: Allows a user to override the severity levels for individual diagnostic rules. "reportXXX" rules in the type check diagnostics settings in [configuration](configuration.md#type-check-diagnostics-settings) are supported. Use the rule name as a key and one of "error," "warning," "information," "true," "false," or "none" as value. +**python.analysis.exclude** [array of paths]: Paths of directories or files that should not be included. This can be overridden in the configuration file. + **python.analysis.extraPaths** [array of paths]: Paths to add to the default execution environment extra paths if there are no execution environments defined in the config file. +**python.analysis.ignore** [array of paths]: Paths of directories or files whose diagnostic output (errors and warnings) should be suppressed. This can be overridden in the configuration file. + +**python.analysis.include** [array of paths]: Paths of directories or files that should be included. This can be overridden in the configuration file. + **python.analysis.logLevel** ["Error", "Warning", "Information", or "Trace"]: Level of logging for Output panel. The default value for this option is "Information". **python.analysis.stubPath** [path]: Path to directory containing custom type stub files. diff --git a/packages/pyright-internal/src/analyzer/service.ts b/packages/pyright-internal/src/analyzer/service.ts index 6fbc4c39a..8db5c5cbf 100644 --- a/packages/pyright-internal/src/analyzer/service.ts +++ b/packages/pyright-internal/src/analyzer/service.ts @@ -677,8 +677,7 @@ export class AnalyzerService { this._typeCheckingMode, this.serviceProvider, host, - commandLineOptions.diagnosticSeverityOverrides, - commandLineOptions.fileSpecs.length > 0 + commandLineOptions.diagnosticSeverityOverrides ); const configFileDir = getDirectoryPath(this._configFilePath!); diff --git a/packages/pyright-internal/src/common/configOptions.ts b/packages/pyright-internal/src/common/configOptions.ts index a264c9b67..fa38d977c 100644 --- a/packages/pyright-internal/src/common/configOptions.ts +++ b/packages/pyright-internal/src/common/configOptions.ts @@ -19,16 +19,16 @@ import { DiagnosticRule } from './diagnosticRules'; import { FileSystem } from './fileSystem'; import { Host } from './host'; import { + FileSpec, combinePaths, ensureTrailingDirectorySeparator, - FileSpec, getFileSpec, isDirectory, normalizePath, realCasePath, resolvePaths, } from './pathUtils'; -import { latestStablePythonVersion, PythonVersion, versionFromString, versionToString } from './pythonVersion'; +import { PythonVersion, latestStablePythonVersion, versionFromString, versionToString } from './pythonVersion'; import { ServiceProvider } from './serviceProvider'; import { ServiceKeys } from './serviceProviderExtensions'; @@ -890,39 +890,36 @@ export class ConfigOptions { typeCheckingMode: string | undefined, serviceProvider: ServiceProvider, host: Host, - diagnosticOverrides?: DiagnosticSeverityOverridesMap, - skipIncludeSection = false + diagnosticOverrides?: DiagnosticSeverityOverridesMap ) { this.initializedFromJson = true; const console = serviceProvider.tryGet(ServiceKeys.console) ?? new NullConsole(); // Read the "include" entry. - if (!skipIncludeSection) { - this.include = []; - if (configObj.include !== undefined) { - if (!Array.isArray(configObj.include)) { - console.error(`Config "include" entry must must contain an array.`); - } else { - const filesList = configObj.include as string[]; - filesList.forEach((fileSpec, index) => { - if (typeof fileSpec !== 'string') { - console.error(`Index ${index} of "include" array should be a string.`); - } else if (isAbsolute(fileSpec)) { - console.error(`Ignoring path "${fileSpec}" in "include" array because it is not relative.`); - } else { - this.include.push(getFileSpec(serviceProvider, this.projectRoot, fileSpec)); - } - }); - } + if (configObj.include !== undefined) { + if (!Array.isArray(configObj.include)) { + console.error(`Config "include" entry must must contain an array.`); + } else { + this.include = []; + const filesList = configObj.include as string[]; + filesList.forEach((fileSpec, index) => { + if (typeof fileSpec !== 'string') { + console.error(`Index ${index} of "include" array should be a string.`); + } else if (isAbsolute(fileSpec)) { + console.error(`Ignoring path "${fileSpec}" in "include" array because it is not relative.`); + } else { + this.include.push(getFileSpec(serviceProvider, this.projectRoot, fileSpec)); + } + }); } } // Read the "exclude" entry. - this.exclude = []; if (configObj.exclude !== undefined) { if (!Array.isArray(configObj.exclude)) { console.error(`Config "exclude" entry must contain an array.`); } else { + this.exclude = []; const filesList = configObj.exclude as string[]; filesList.forEach((fileSpec, index) => { if (typeof fileSpec !== 'string') { @@ -937,11 +934,11 @@ export class ConfigOptions { } // Read the "ignore" entry. - this.ignore = []; if (configObj.ignore !== undefined) { if (!Array.isArray(configObj.ignore)) { console.error(`Config "ignore" entry must contain an array.`); } else { + this.ignore = []; const filesList = configObj.ignore as string[]; filesList.forEach((fileSpec, index) => { if (typeof fileSpec !== 'string') { @@ -956,11 +953,11 @@ export class ConfigOptions { } // Read the "strict" entry. - this.strict = []; if (configObj.strict !== undefined) { if (!Array.isArray(configObj.strict)) { console.error(`Config "strict" entry must contain an array.`); } else { + this.strict = []; const filesList = configObj.strict as string[]; filesList.forEach((fileSpec, index) => { if (typeof fileSpec !== 'string') { diff --git a/packages/pyright-internal/src/languageServerBase.ts b/packages/pyright-internal/src/languageServerBase.ts index b83e4dfc3..c7826c983 100644 --- a/packages/pyright-internal/src/languageServerBase.ts +++ b/packages/pyright-internal/src/languageServerBase.ts @@ -1474,7 +1474,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis }); } - // File watcher is pylance wide service. Dispose all existing file watchers and create new ones. + // Dispose all existing file watchers and create new ones. this.connection.client.register(DidChangeWatchedFilesNotification.type, { watchers }).then((d) => { if (this._lastFileWatcherRegistration) { this._lastFileWatcherRegistration.dispose(); diff --git a/packages/pyright-internal/src/server.ts b/packages/pyright-internal/src/server.ts index 70f507e61..0d2e0038b 100644 --- a/packages/pyright-internal/src/server.ts +++ b/packages/pyright-internal/src/server.ts @@ -16,6 +16,7 @@ import { } from 'vscode-languageserver'; import { AnalysisResults } from './analyzer/analysis'; +import { CacheManager } from './analyzer/cacheManager'; import { ImportResolver } from './analyzer/importResolver'; import { isPythonBinary } from './analyzer/pythonPathUtils'; import { BackgroundAnalysis } from './backgroundAnalysis'; @@ -38,7 +39,6 @@ import { LanguageServerBase, ServerSettings } from './languageServerBase'; import { CodeActionProvider } from './languageService/codeActionProvider'; import { PyrightFileSystem } from './pyrightFileSystem'; import { Workspace } from './workspaceFactory'; -import { CacheManager } from './analyzer/cacheManager'; const maxAnalysisTimeInForeground = { openFilesTimeInMs: 50, noOpenFilesTimeInMs: 200 }; @@ -170,6 +170,10 @@ export class PyrightServer extends LanguageServerBase { .map((p) => resolvePaths(workspace.rootPath, expandPathVariables(workspace.rootPath, p))); } + serverSettings.fileSpecs = this._getStringValues(pythonAnalysisSection.include); + serverSettings.excludeFileSpecs = this._getStringValues(pythonAnalysisSection.exclude); + serverSettings.ignoreFileSpecs = this._getStringValues(pythonAnalysisSection.ignore); + if (pythonAnalysisSection.typeCheckingMode !== undefined) { serverSettings.typeCheckingMode = pythonAnalysisSection.typeCheckingMode; } @@ -313,4 +317,12 @@ export class PyrightServer extends LanguageServerBase { }, }; } + + private _getStringValues(values: any) { + if (!values || !Array.isArray(values) || values.length === 0) { + return []; + } + + return values.filter((p) => p && isString(p)) as string[]; + } } diff --git a/packages/vscode-pyright/package.json b/packages/vscode-pyright/package.json index 8541fd46f..1b2c49f0b 100644 --- a/packages/vscode-pyright/package.json +++ b/packages/vscode-pyright/package.json @@ -159,6 +159,33 @@ ], "scope": "resource" }, + "python.analysis.include": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "Paths of directories or files that should be included. If no paths are specified, pyright defaults to the workspace root directory. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character).", + "scope": "resource" + }, + "python.analysis.exclude": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "Paths of directories or files that should not be included. These override the include directories, allowing specific subdirectories to be excluded. Note that files in the exclude paths may still be included in the analysis if they are referenced (imported) by source files that are not excluded. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). If no exclude paths are specified, pyright automatically excludes the following: `**/node_modules`, `**/__pycache__`, `.git` and any virtual environment directories.", + "scope": "resource" + }, + "python.analysis.ignore": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "Paths of directories or files whose diagnostic output (errors and warnings) should be suppressed even if they are an included file or within the transitive closure of an included file. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). If no value is provided, the value of python.linting.ignorePatterns (if set) will be used.", + "scope": "resource" + }, "python.analysis.diagnosticSeverityOverrides": { "type": "object", "description": "Allows a user to override the severity levels for individual diagnostics.",