Added support for python.analysis.include, `python.analysis.exclude… (#6251)

* Added support for `python.analysis.include`, `python.analysis.exclude` and `python.analysis.ignore` settings. These have been implemented in pylance for a while now, but they were never implemented previously in pyright. Also fixed a bug in the original implementation that caused the config file not to override the language server settings for `python.analysis.exclude` and `python.analysis.ignore` as it should. This addresses #6250.

* Removed references to pylance in the help text.
This commit is contained in:
Eric Traut 2023-10-29 08:54:14 -07:00 committed by GitHub
parent 05fa7d06c0
commit 08a295a261
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 28 deletions

View File

@ -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.

View File

@ -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!);

View File

@ -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') {

View File

@ -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();

View File

@ -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[];
}
}

View File

@ -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.",