Revert "Document different types of 'compiler' flags and ensure merging of flags is consistent (#8704)"

This reverts commit a86210ccb7.
This commit is contained in:
Eric Traut 2024-08-09 13:20:55 -06:00
parent 421a87fd8a
commit de0386d690
7 changed files with 197 additions and 419 deletions

View File

@ -6,14 +6,14 @@ Pyright settings can also be specified in a `[tool.pyright]` section of a “pyp
Relative paths specified within the config file are relative to the config files location. Paths with shell variables (including `~`) are not supported. Paths within a config file should generally be relative paths so the config file can be shared by other developers who contribute to the project.
## Environment Options
The following settings control the *environment* in which Pyright will check for diagnostics. These settings determine how Pyright finds source files, imports, and what Python version specific rules are applied.
## Main Configuration Options
- **include** [array of paths, optional]: Paths of directories or files that should be considered part of the project. If no paths are specified, pyright defaults to the directory that contains the config 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 include paths are specified, the root path for the workspace is assumed.
- **exclude** [array of paths, optional]: Paths of directories or files that should not be considered part of the project. These override the directories and files that `include` matched, 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__`, `**/.*`. Pylance also excludes any virtual environment directories regardless of the exclude paths specified. For more detail on Python environment specification and discovery, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation.
- **ignore** [array of paths, optional]: 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).
- **strict** [array of paths, optional]: Paths of directories or files that should use “strict” analysis if they are included. This is the same as manually adding a “# pyright: strict” comment. In strict mode, most type-checking rules are enabled. Refer to [this table](configuration.md#diagnostic-settings-defaults) for details about which rules are enabled in strict mode. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character).
- **extends** [path, optional]: Path to another `.json` or `.toml` file that is used as a “base configuration”, allowing this configuration to inherit configuration settings. Top-level keys within this configuration overwrite top-level keys in the base configuration. Multiple levels of inheritance are supported. Relative paths specified in a configuration file are resolved relative to the location of that configuration file.
@ -24,9 +24,9 @@ The following settings control the *environment* in which Pyright will check for
- **stubPath** [path, optional]: Path to a directory that contains custom type stubs. Each package's type stub file(s) are expected to be in its own subdirectory. The default value of this setting is "./typings". (typingsPath is now deprecated)
- **venvPath** [path, optional]: Path to a directory containing one or more subdirectories, each of which contains a virtual environment. When used in conjunction with a **venv** setting (see below), pyright will search for imports in the virtual environments site-packages directory rather than the paths specified by the default Python interpreter. If you are working on a project with other developers, it is best not to specify this setting in the config file, since this path will typically differ for each developer. Instead, it can be specified on the command line or in a per-user setting. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation. This setting is ignored when using Pylance. VS Code's python interpreter path is used instead.
- **venvPath** [path, optional]: Path to a directory containing one or more subdirectories, each of which contains a virtual environment. When used in conjunction with a **venv** setting (see below), pyright will search for imports in the virtual environments site-packages directory rather than the paths specified by the default Python interpreter. If you are working on a project with other developers, it is best not to specify this setting in the config file, since this path will typically differ for each developer. Instead, it can be specified on the command line or in a per-user setting. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation.
- **venv** [string, optional]: Used in conjunction with the venvPath, specifies the virtual environment to use. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation. This setting is ignored when using Pylance.
- **venv** [string, optional]: Used in conjunction with the venvPath, specifies the virtual environment to use. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation.
- **verboseOutput** [boolean]: Specifies whether output logs should be verbose. This is useful when diagnosing certain problems like import resolution issues.
@ -38,11 +38,12 @@ The following settings control the *environment* in which Pyright will check for
- **executionEnvironments** [array of objects, optional]: Specifies a list of execution environments (see [below](configuration.md#execution-environment-options)). Execution environments are searched from start to finish by comparing the path of a source file with the root path specified in the execution environment.
- **typeCheckingMode** ["off", "basic", "standard", "strict"]: Specifies the default rule set to use. Some rules can be overridden using additional configuration flags documented below. The default value for this setting is "standard". If set to "off", all type-checking rules are disabled, but Python syntax and semantic errors are still reported.
- **useLibraryCodeForTypes** [boolean]: Determines whether pyright reads, parses and analyzes library code to extract type information in the absence of type stub files. Type information will typically be incomplete. We recommend using type stubs where possible. The default value for this option is true.
## Type Evaluation Settings
The following settings determine how different types should be evaluated.
## Type Evaluation Settings
- <a name="strictListInference"></a> **strictListInference** [boolean]: When inferring the type of a list, use strict type assumptions. For example, the expression `[1, 'a', 3.4]` could be inferred to be of type `list[Any]` or `list[int | str | float]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is `false`.
@ -64,16 +65,9 @@ The following settings determine how different types should be evaluated.
- <a name="disableBytesTypePromotions"></a> **disableBytesTypePromotions** [boolean]: Disables legacy behavior where `bytearray` and `memoryview` are considered subtypes of `bytes`. [PEP 688](https://peps.python.org/pep-0688/#no-special-meaning-for-bytes) deprecates this behavior, but this switch is provided to restore the older behavior. The default value for this setting is `false`.
## Type Check Diagnostics Settings
The following settings control pyrights diagnostic output (warnings or errors).
- **typeCheckingMode** ["off", "basic", "standard", "strict"]: Specifies the default rule set to use. Some rules can be overridden using additional configuration flags documented below. The default value for this setting is "standard". If set to "off", all type-checking rules are disabled, but Python syntax and semantic errors are still reported.
- **ignore** [array of paths, optional]: 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). This setting can be overridden in VS code in your settings.json.
### Type Check Rule Overrides
The following settings allow more fine grained control over the **typeCheckingMode**. Unless otherwise specified, each diagnostic setting can specify a boolean value (`false` indicating that no error is generated and `true` indicating that an error is generated). Alternatively, a string value of `"none"`, `"warning"`, `"information"`, or `"error"` can be used to specify the diagnostic level.
The following settings control pyrights diagnostic output (warnings or errors). Unless otherwise specified, each diagnostic setting can specify a boolean value (`false` indicating that no error is generated and `true` indicating that an error is generated). Alternatively, a string value of `"none"`, `"warning"`, `"information"`, or `"error"` can be used to specify the diagnostic level.
- <a name="reportGeneralTypeIssues"></a> **reportGeneralTypeIssues** [boolean or string, optional]: Generate or suppress diagnostics for general type inconsistencies, unsupported operations, argument/parameter mismatches, etc. This covers all of the basic type-checking rules not covered by other rules. It does not include syntax errors. The default value for this setting is `"error"`.
@ -441,11 +435,6 @@ The following table lists the default severity levels for each diagnostic rule w
| reportUnnecessaryTypeIgnoreComment | "none" | "none" | "none" | "none" |
| reportUnusedCallResult | "none" | "none" | "none" | "none" |
## Overriding settings (in VS Code)
If a pyproject.toml (with a pyright section) or a pyrightconfig.json exists, any pyright settings in a VS code setttings.json will be ignored. Pyrightconfig.json is prescribing the environment to be used for a particular project. Changing the environment per user is not supported.
If a pyproject.toml (with a pyright section) or a pyrightconfig.json does not exist, then the VS Code settings.json settings apply.
## Locale Configuration

View File

@ -571,7 +571,7 @@ export class AnalyzerService {
// If pyright is being executed from the command line, the working
// directory may be deep within a project, and we need to walk up the
// directory hierarchy to find the project root.
if (!configFilePath && !commandLineOptions.fromLanguageServer) {
if (!configFilePath && !commandLineOptions.fromVsCodeExtension) {
configFilePath = findConfigFileHereOrUp(this.fs, projectRoot);
}
@ -587,7 +587,7 @@ export class AnalyzerService {
// See if we can find a pyproject.toml file in this directory.
pyprojectFilePath = findPyprojectTomlFile(this.fs, projectRoot);
if (!pyprojectFilePath && !commandLineOptions.fromLanguageServer) {
if (!pyprojectFilePath && !commandLineOptions.fromVsCodeExtension) {
pyprojectFilePath = findPyprojectTomlFileHereOrUp(this.fs, projectRoot);
}
@ -600,54 +600,71 @@ export class AnalyzerService {
}
const configOptions = new ConfigOptions(projectRoot);
configOptions.initializeTypeCheckingMode(
this._typeCheckingMode,
commandLineOptions.diagnosticSeverityOverrides
);
const defaultExcludes = ['**/node_modules', '**/__pycache__', '**/.*'];
if (commandLineOptions.pythonPath) {
this._console.info(
`Setting pythonPath for service "${this._instanceName}": ` + `"${commandLineOptions.pythonPath}"`
);
configOptions.pythonPath = this.fs.realCasePath(
Uri.file(commandLineOptions.pythonPath, this.serviceProvider, /* checkRelative */ true)
);
}
if (commandLineOptions.pythonEnvironmentName) {
this._console.info(
`Setting environmentName for service "${this._instanceName}": ` +
`"${commandLineOptions.pythonEnvironmentName}"`
);
configOptions.pythonEnvironmentName = commandLineOptions.pythonEnvironmentName;
}
// The pythonPlatform and pythonVersion from the command-line can be overridden
// by the config file, so initialize them upfront.
configOptions.defaultPythonPlatform = commandLineOptions.pythonPlatform;
configOptions.defaultPythonVersion = commandLineOptions.pythonVersion;
configOptions.ensureDefaultExtraPaths(
this.fs,
commandLineOptions.autoSearchPaths ?? false,
commandLineOptions.extraPaths
);
commandLineOptions.includeFileSpecs.forEach((fileSpec) => {
configOptions.include.push(getFileSpec(projectRoot, fileSpec));
});
commandLineOptions.excludeFileSpecs.forEach((fileSpec) => {
configOptions.exclude.push(getFileSpec(projectRoot, fileSpec));
});
commandLineOptions.ignoreFileSpecs.forEach((fileSpec) => {
configOptions.ignore.push(getFileSpec(projectRoot, fileSpec));
});
configOptions.disableTaggedHints = !!commandLineOptions.disableTaggedHints;
configOptions.initializeTypeCheckingMode(commandLineOptions.typeCheckingMode ?? 'standard');
// If we found a config file, load it and apply its settings.
const configs = this._getExtendedConfigurations(configFilePath ?? pyprojectFilePath);
if (configs && configs.length > 0) {
// With a pyrightconfig.json set, we want the typeCheckingMode to always be standard
// as that's what the Pyright CLI will expect. Command line options (if not a language server) and
// the config file can override this.
configOptions.initializeTypeCheckingMode('standard');
// Then we apply the config file settings. This can update the
// the typeCheckingMode.
if (configs && configs.length > 0) {
for (const config of configs) {
configOptions.initializeFromJson(
config.configFileJsonObj,
config.configFileDirUri,
this.serviceProvider,
host
host,
commandLineOptions
);
}
// When not in language server mode, command line options override config file options.
if (!commandLineOptions.fromLanguageServer) {
this._applyCommandLineOptionsToConfig(configOptions, commandLineOptions, projectRoot);
}
} else {
// Initialize the type checking mode based on if this is for a language server or not. Language
// servers default to 'off' when no config file is found.
configOptions.initializeTypeCheckingMode(commandLineOptions.fromLanguageServer ? 'off' : 'standard');
// If there are no config files, we can then directly apply the command line options.
this._applyCommandLineOptionsToConfig(configOptions, commandLineOptions, projectRoot);
configOptions.applyDiagnosticOverrides(commandLineOptions.diagnosticSeverityOverrides);
}
// Ensure that if no command line or config options were applied, we have some defaults.
this._ensureDefaultOptions(host, configOptions, projectRoot, executionRoot, commandLineOptions);
return configOptions;
}
private _ensureDefaultOptions(
host: Host,
configOptions: ConfigOptions,
projectRoot: Uri,
executionRoot: Uri,
commandLineOptions: CommandLineOptions
) {
const defaultExcludes = ['**/node_modules', '**/__pycache__', '**/.*'];
// If no include paths were provided, assume that all files within
// the project should be included.
if (configOptions.include.length === 0) {
@ -668,19 +685,50 @@ export class AnalyzerService {
}
}
if (!configOptions.defaultExtraPaths) {
configOptions.ensureDefaultExtraPaths(
this.fs,
commandLineOptions.autoSearchPaths ?? false,
commandLineOptions.extraPaths
);
// Override the analyzeUnannotatedFunctions setting based on the command-line setting.
if (commandLineOptions.analyzeUnannotatedFunctions !== undefined) {
configOptions.diagnosticRuleSet.analyzeUnannotatedFunctions =
commandLineOptions.analyzeUnannotatedFunctions;
}
if (configOptions.defaultPythonPlatform === undefined) {
configOptions.defaultPythonPlatform = commandLineOptions.pythonPlatform;
// Override the include based on command-line settings.
if (commandLineOptions.includeFileSpecsOverride) {
configOptions.include = [];
commandLineOptions.includeFileSpecsOverride.forEach((include) => {
configOptions.include.push(
getFileSpec(Uri.file(include, this.serviceProvider, /* checkRelative */ true), '.')
);
});
}
if (configOptions.defaultPythonVersion === undefined) {
configOptions.defaultPythonVersion = commandLineOptions.pythonVersion;
const reportDuplicateSetting = (settingName: string, configValue: number | string | boolean) => {
const settingSource = commandLineOptions.fromVsCodeExtension
? 'the client settings'
: 'a command-line option';
this._console.warn(
`The ${settingName} has been specified in both the config file and ` +
`${settingSource}. The value in the config file (${configValue}) ` +
`will take precedence`
);
};
// Apply the command-line options if the corresponding
// item wasn't already set in the config file. Report any
// duplicates.
if (commandLineOptions.venvPath) {
if (!configOptions.venvPath) {
configOptions.venvPath = projectRoot.resolvePaths(commandLineOptions.venvPath);
} else {
reportDuplicateSetting('venvPath', configOptions.venvPath.toUserVisibleString());
}
}
if (commandLineOptions.typeshedPath) {
if (!configOptions.typeshedPath) {
configOptions.typeshedPath = projectRoot.resolvePaths(commandLineOptions.typeshedPath);
} else {
reportDuplicateSetting('typeshedPath', configOptions.typeshedPath.toUserVisibleString());
}
}
// If the caller specified that "typeshedPath" is the root of the project,
@ -704,10 +752,35 @@ export class AnalyzerService {
});
}
configOptions.verboseOutput = commandLineOptions.verboseOutput ?? configOptions.verboseOutput;
configOptions.checkOnlyOpenFiles = !!commandLineOptions.checkOnlyOpenFiles;
configOptions.autoImportCompletions = !!commandLineOptions.autoImportCompletions;
configOptions.indexing = !!commandLineOptions.indexing;
configOptions.taskListTokens = commandLineOptions.taskListTokens;
configOptions.logTypeEvaluationTime = !!commandLineOptions.logTypeEvaluationTime;
configOptions.typeEvaluationTimeThreshold = commandLineOptions.typeEvaluationTimeThreshold;
// If useLibraryCodeForTypes was not specified in the config, allow the settings
// or command line to override it.
if (configOptions.useLibraryCodeForTypes === undefined) {
configOptions.useLibraryCodeForTypes = commandLineOptions.useLibraryCodeForTypes;
} else if (commandLineOptions.useLibraryCodeForTypes !== undefined) {
reportDuplicateSetting('useLibraryCodeForTypes', configOptions.useLibraryCodeForTypes);
}
// If useLibraryCodeForTypes is unspecified, default it to true.
if (configOptions.useLibraryCodeForTypes === undefined) {
configOptions.useLibraryCodeForTypes = true;
}
if (commandLineOptions.stubPath) {
if (!configOptions.stubPath) {
configOptions.stubPath = this.fs.realCasePath(projectRoot.resolvePaths(commandLineOptions.stubPath));
} else {
reportDuplicateSetting('stubPath', configOptions.stubPath.toUserVisibleString());
}
}
if (configOptions.stubPath) {
// If there was a stub path specified, validate it.
if (!this.fs.existsSync(configOptions.stubPath) || !isDirectory(this.fs, configOptions.stubPath)) {
@ -772,157 +845,8 @@ export class AnalyzerService {
);
}
}
}
private _applyCommandLineOptionsToConfig(
configOptions: ConfigOptions,
commandLineOptions: CommandLineOptions,
projectRoot: Uri
) {
if (commandLineOptions.typeCheckingMode) {
configOptions.initializeTypeCheckingMode(commandLineOptions.typeCheckingMode);
}
if (commandLineOptions.extraPaths) {
const oldExtraPaths = configOptions.defaultExtraPaths ? [...configOptions.defaultExtraPaths] : [];
configOptions.ensureDefaultExtraPaths(
this.fs,
commandLineOptions.autoSearchPaths ?? false,
commandLineOptions.extraPaths
);
// Execution environments inherit the default extra paths, so we need to update them as well.
configOptions.executionEnvironments.forEach((env) => {
env.extraPaths = env.extraPaths.filter(
(path) => !oldExtraPaths.some((oldPath) => oldPath.equals(path))
);
env.extraPaths.push(...configOptions.defaultExtraPaths!);
});
}
if (commandLineOptions.pythonVersion || commandLineOptions.pythonPlatform) {
configOptions.defaultPythonVersion = commandLineOptions.pythonVersion ?? configOptions.defaultPythonVersion;
configOptions.defaultPythonPlatform =
commandLineOptions.pythonPlatform ?? configOptions.defaultPythonPlatform;
// This should also override any of the execution environment settings.
configOptions.executionEnvironments.forEach((env) => {
env.pythonVersion = commandLineOptions.pythonVersion ?? env.pythonVersion;
env.pythonPlatform = commandLineOptions.pythonPlatform ?? env.pythonPlatform;
});
}
if (commandLineOptions.pythonPath) {
this._console.info(
`Setting pythonPath for service "${this._instanceName}": ` + `"${commandLineOptions.pythonPath}"`
);
configOptions.pythonPath = this.fs.realCasePath(
Uri.file(commandLineOptions.pythonPath, this.serviceProvider, /* checkRelative */ true)
);
}
if (commandLineOptions.pythonEnvironmentName) {
this._console.info(
`Setting environmentName for service "${this._instanceName}": ` +
`"${commandLineOptions.pythonEnvironmentName}"`
);
configOptions.pythonEnvironmentName = commandLineOptions.pythonEnvironmentName;
}
commandLineOptions.includeFileSpecs.forEach((fileSpec) => {
configOptions.include.push(getFileSpec(projectRoot, fileSpec));
});
commandLineOptions.excludeFileSpecs.forEach((fileSpec) => {
configOptions.exclude.push(getFileSpec(projectRoot, fileSpec));
});
commandLineOptions.ignoreFileSpecs.forEach((fileSpec) => {
configOptions.ignore.push(getFileSpec(projectRoot, fileSpec));
});
configOptions.disableTaggedHints = !!commandLineOptions.disableTaggedHints;
configOptions.applyDiagnosticOverrides(commandLineOptions.diagnosticSeverityOverrides);
// Override the analyzeUnannotatedFunctions setting based on the command-line setting.
if (commandLineOptions.analyzeUnannotatedFunctions !== undefined) {
configOptions.diagnosticRuleSet.analyzeUnannotatedFunctions =
commandLineOptions.analyzeUnannotatedFunctions;
}
// Override the include based on command-line settings.
if (commandLineOptions.includeFileSpecsOverride) {
configOptions.include = [];
commandLineOptions.includeFileSpecsOverride.forEach((include) => {
configOptions.include.push(
getFileSpec(Uri.file(include, this.serviceProvider, /* checkRelative */ true), '.')
);
});
}
const reportDuplicateSetting = (settingName: string, configValue: number | string | boolean) => {
const settingSource = commandLineOptions.fromLanguageServer
? 'the client settings'
: 'a command-line option';
this._console.warn(
`The ${settingName} has been specified in both the config file and ` +
`${settingSource}. The value in the config file (${configValue}) ` +
`will take precedence`
);
};
// Apply the command-line options if the corresponding
// item wasn't already set in the config file. Report any
// duplicates.
if (commandLineOptions.venvPath) {
if (!configOptions.venvPath) {
configOptions.venvPath = projectRoot.resolvePaths(commandLineOptions.venvPath);
} else {
reportDuplicateSetting('venvPath', configOptions.venvPath.toUserVisibleString());
}
}
if (commandLineOptions.typeshedPath) {
if (!configOptions.typeshedPath) {
configOptions.typeshedPath = projectRoot.resolvePaths(commandLineOptions.typeshedPath);
} else {
reportDuplicateSetting('typeshedPath', configOptions.typeshedPath.toUserVisibleString());
}
}
if (commandLineOptions.verboseOutput !== undefined) {
configOptions.verboseOutput = commandLineOptions.verboseOutput;
}
if (commandLineOptions.checkOnlyOpenFiles !== undefined) {
configOptions.checkOnlyOpenFiles = commandLineOptions.checkOnlyOpenFiles;
}
if (commandLineOptions.autoImportCompletions !== undefined) {
configOptions.autoImportCompletions = commandLineOptions.autoImportCompletions;
}
if (commandLineOptions.indexing !== undefined) {
configOptions.indexing = commandLineOptions.indexing;
}
if (commandLineOptions.taskListTokens) {
configOptions.taskListTokens = commandLineOptions.taskListTokens;
}
if (commandLineOptions.logTypeEvaluationTime !== undefined) {
configOptions.logTypeEvaluationTime = commandLineOptions.logTypeEvaluationTime;
}
configOptions.typeEvaluationTimeThreshold = commandLineOptions.typeEvaluationTimeThreshold;
// If useLibraryCodeForTypes was not specified in the config, allow the command line to override it.
if (configOptions.useLibraryCodeForTypes === undefined) {
configOptions.useLibraryCodeForTypes = commandLineOptions.useLibraryCodeForTypes;
} else if (commandLineOptions.useLibraryCodeForTypes !== undefined) {
reportDuplicateSetting('useLibraryCodeForTypes', configOptions.useLibraryCodeForTypes);
}
if (commandLineOptions.stubPath) {
if (!configOptions.stubPath) {
configOptions.stubPath = this.fs.realCasePath(projectRoot.resolvePaths(commandLineOptions.stubPath));
} else {
reportDuplicateSetting('stubPath', configOptions.stubPath.toUserVisibleString());
}
}
return configOptions;
}
// Loads the config JSON object from the specified config file along with any
@ -1763,7 +1687,7 @@ export class AnalyzerService {
this._backgroundAnalysisProgram.setImportResolver(importResolver);
if (this._commandLineOptions?.fromLanguageServer || this._configOptions.verboseOutput) {
if (this._commandLineOptions?.fromVsCodeExtension || this._configOptions.verboseOutput) {
const logLevel = this._configOptions.verboseOutput ? LogLevel.Info : LogLevel.Log;
for (const execEnv of this._configOptions.getExecutionEnvironments()) {
log(this._console, logLevel, `Search paths for ${execEnv.root || '<default>'}`);

View File

@ -119,10 +119,10 @@ export class CommandLineOptions {
// 'basic', 'standard', or 'strict'.
typeCheckingMode?: string | undefined;
// Indicates that the settings came from a language server rather than
// Indicates that the settings came from VS Code rather than
// from the command-line. Useful for providing clearer error
// messages.
fromLanguageServer: boolean;
fromVsCodeExtension: boolean;
// Indicates diagnostic severity overrides
diagnosticSeverityOverrides?: DiagnosticSeverityOverridesMap | undefined;
@ -151,8 +151,8 @@ export class CommandLineOptions {
// Disable reporting of hint diagnostics with tags?
disableTaggedHints?: boolean;
constructor(executionRoot: string | Uri | undefined, fromLanguageServer: boolean) {
constructor(executionRoot: string | Uri | undefined, fromVsCodeExtension: boolean) {
this.executionRoot = executionRoot;
this.fromLanguageServer = fromLanguageServer;
this.fromVsCodeExtension = fromVsCodeExtension;
}
}

View File

@ -12,7 +12,7 @@ import { isAbsolute } from 'path';
import { getPathsFromPthFiles } from '../analyzer/pythonPathUtils';
import * as pathConsts from '../common/pathConsts';
import { appendArray } from './collectionUtils';
import { DiagnosticSeverityOverrides, DiagnosticSeverityOverridesMap } from './commandLineOptions';
import { CommandLineOptions, DiagnosticSeverityOverrides, DiagnosticSeverityOverridesMap } from './commandLineOptions';
import { ConsoleInterface, NullConsole } from './console';
import { TaskListToken } from './diagnostic';
import { DiagnosticRule } from './diagnosticRules';
@ -1123,7 +1123,13 @@ export class ConfigOptions {
}
// Initialize the structure from a JSON object.
initializeFromJson(configObj: any, configDirUri: Uri, serviceProvider: ServiceProvider, host: Host) {
initializeFromJson(
configObj: any,
configDirUri: Uri,
serviceProvider: ServiceProvider,
host: Host,
commandLineOptions?: CommandLineOptions
) {
this.initializedFromJson = true;
const console = serviceProvider.tryGet(ServiceKeys.console) ?? new NullConsole();
@ -1226,24 +1232,22 @@ export class ConfigOptions {
}
// Apply overrides from the config file for the boolean rules.
const configRuleSet = { ...this.diagnosticRuleSet };
getBooleanDiagnosticRules(/* includeNonOverridable */ true).forEach((ruleName) => {
(configRuleSet as any)[ruleName] = this._convertBoolean(
(this.diagnosticRuleSet as any)[ruleName] = this._convertBoolean(
configObj[ruleName],
ruleName,
configRuleSet[ruleName] as boolean
this.diagnosticRuleSet[ruleName] as boolean
);
});
// Apply overrides from the config file for the diagnostic level rules.
getDiagLevelDiagnosticRules().forEach((ruleName) => {
(configRuleSet as any)[ruleName] = this._convertDiagnosticLevel(
(this.diagnosticRuleSet as any)[ruleName] = this._convertDiagnosticLevel(
configObj[ruleName],
ruleName,
configRuleSet[ruleName] as DiagnosticLevel
this.diagnosticRuleSet[ruleName] as DiagnosticLevel
);
});
this.diagnosticRuleSet = { ...configRuleSet };
// Read the "venvPath".
if (configObj.venvPath !== undefined) {
@ -1263,9 +1267,9 @@ export class ConfigOptions {
}
}
// Read the config "extraPaths".
const configExtraPaths: Uri[] = [];
// Read the default "extraPaths".
if (configObj.extraPaths !== undefined) {
this.defaultExtraPaths = [];
if (!Array.isArray(configObj.extraPaths)) {
console.error(`Config "extraPaths" field must contain an array.`);
} else {
@ -1274,20 +1278,17 @@ export class ConfigOptions {
if (typeof path !== 'string') {
console.error(`Config "extraPaths" field ${pathIndex} must be a string.`);
} else {
configExtraPaths!.push(configDirUri.resolvePaths(path));
this.defaultExtraPaths!.push(configDirUri.resolvePaths(path));
}
});
this.defaultExtraPaths = [...configExtraPaths];
}
}
// Read the default "pythonVersion".
let configPythonVersion: PythonVersion | undefined = undefined;
if (configObj.pythonVersion !== undefined) {
if (typeof configObj.pythonVersion === 'string') {
const version = PythonVersion.fromString(configObj.pythonVersion);
if (version) {
configPythonVersion = version;
this.defaultPythonVersion = version;
} else {
console.error(`Config "pythonVersion" field contains unsupported version.`);
@ -1297,19 +1298,26 @@ export class ConfigOptions {
}
}
// Override the default python version if it was specified on the command line.
if (commandLineOptions?.pythonVersion) {
this.defaultPythonVersion = commandLineOptions.pythonVersion;
}
this.ensureDefaultPythonVersion(host, console);
// Read the default "pythonPlatform".
let configPythonPlatform: string | undefined = undefined;
if (configObj.pythonPlatform !== undefined) {
if (typeof configObj.pythonPlatform !== 'string') {
console.error(`Config "pythonPlatform" field must contain a string.`);
} else {
this.defaultPythonPlatform = configObj.pythonPlatform;
configPythonPlatform = configObj.pythonPlatform;
}
}
if (commandLineOptions?.pythonPlatform) {
this.defaultPythonPlatform = commandLineOptions.pythonPlatform;
}
this.ensureDefaultPythonPlatform(host, console);
// Read the "typeshedPath" setting.
@ -1397,10 +1405,7 @@ export class ConfigOptions {
configDirUri,
index,
console,
configRuleSet,
configPythonVersion,
configPythonPlatform,
configExtraPaths
commandLineOptions
);
if (execEnv) {
@ -1578,19 +1583,16 @@ export class ConfigOptions {
configDirUri: Uri,
index: number,
console: ConsoleInterface,
configDiagnosticRuleSet: DiagnosticRuleSet,
configPythonVersion: PythonVersion | undefined,
configPythonPlatform: string | undefined,
configExtraPaths: Uri[]
commandLineOptions?: CommandLineOptions
): ExecutionEnvironment | undefined {
try {
const newExecEnv = new ExecutionEnvironment(
this._getEnvironmentName(),
configDirUri,
configDiagnosticRuleSet,
configPythonVersion,
configPythonPlatform,
configExtraPaths
this.diagnosticRuleSet,
this.defaultPythonVersion,
this.defaultPythonPlatform,
this.defaultExtraPaths
);
// Validate the root.
@ -1635,6 +1637,12 @@ export class ConfigOptions {
}
}
// If the pythonVersion was specified on the command line, it overrides
// the configuration settings for the execution environment.
if (commandLineOptions?.pythonVersion) {
newExecEnv.pythonVersion = commandLineOptions.pythonVersion;
}
// Validate the pythonPlatform.
if (envObj.pythonPlatform) {
if (typeof envObj.pythonPlatform === 'string') {
@ -1644,6 +1652,12 @@ export class ConfigOptions {
}
}
// If the pythonPlatform was specified on the command line, it overrides
// the configuration settings for the execution environment.
if (commandLineOptions?.pythonPlatform) {
newExecEnv.pythonPlatform = commandLineOptions.pythonPlatform;
}
// Validate the name
if (envObj.name) {
if (typeof envObj.name === 'string') {

View File

@ -11,7 +11,7 @@ import assert from 'assert';
import { AnalyzerService } from '../analyzer/service';
import { deserialize, serialize } from '../backgroundThreadBase';
import { CommandLineOptions, DiagnosticSeverityOverrides } from '../common/commandLineOptions';
import { CommandLineOptions } from '../common/commandLineOptions';
import { ConfigOptions, ExecutionEnvironment, getStandardDiagnosticRuleSet } from '../common/configOptions';
import { ConsoleInterface, NullConsole } from '../common/console';
import { NoAccessHost } from '../common/host';
@ -35,7 +35,7 @@ function createAnalyzer(console?: ConsoleInterface) {
test('FindFilesWithConfigFile', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
commandLineOptions.configFilePath = 'src/tests/samples/project1';
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -60,7 +60,7 @@ test('FindFilesWithConfigFile', () => {
test('FindFilesVirtualEnvAutoDetectExclude', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
commandLineOptions.configFilePath = 'src/tests/samples/project_with_venv_auto_detect_exclude';
service.setOptions(commandLineOptions);
@ -78,7 +78,7 @@ test('FindFilesVirtualEnvAutoDetectExclude', () => {
test('FindFilesVirtualEnvAutoDetectInclude', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
commandLineOptions.configFilePath = 'src/tests/samples/project_with_venv_auto_detect_include';
service.setOptions(commandLineOptions);
@ -97,7 +97,7 @@ test('FileSpecNotAnArray', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project2';
service.setOptions(commandLineOptions);
@ -111,7 +111,7 @@ test('FileSpecNotAString', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project3';
service.setOptions(commandLineOptions);
@ -125,7 +125,7 @@ test('SomeFileSpecsAreInvalid', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project4';
service.setOptions(commandLineOptions);
@ -152,7 +152,7 @@ test('ConfigBadJson', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project5';
service.setOptions(commandLineOptions);
@ -231,7 +231,7 @@ test('AutoSearchPathsOn', () => {
normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src')),
service.serviceProvider
);
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = true;
service.setOptions(commandLineOptions);
@ -245,7 +245,7 @@ test('AutoSearchPathsOff', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src'));
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = false;
service.setOptions(commandLineOptions);
@ -258,7 +258,7 @@ test('AutoSearchPathsOnSrcIsPkg', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src_is_pkg'));
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = true;
service.setOptions(commandLineOptions);
@ -272,7 +272,7 @@ test('AutoSearchPathsOnWithConfigExecEnv', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src_with_config_extra_paths'));
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
commandLineOptions.configFilePath = combinePaths(cwd, 'pyrightconfig.json');
commandLineOptions.autoSearchPaths = true;
service.setOptions(commandLineOptions);
@ -292,7 +292,7 @@ test('AutoSearchPathsOnAndExtraPaths', () => {
normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src_with_config_no_extra_paths')),
service.serviceProvider
);
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = true;
commandLineOptions.extraPaths = ['src/_vendored'];
service.setOptions(commandLineOptions);
@ -310,7 +310,7 @@ test('AutoSearchPathsOnAndExtraPaths', () => {
test('BasicPyprojectTomlParsing', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_with_pyproject_toml'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
service.setOptions(commandLineOptions);
@ -323,7 +323,7 @@ test('BasicPyprojectTomlParsing', () => {
test('FindFilesInMemoryOnly', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(undefined, /* fromLanguageServer */ true);
const commandLineOptions = new CommandLineOptions(undefined, /* fromVsCodeExtension */ true);
// Force a lookup of the typeshed path. This causes us to try and generate a module path for the untitled file.
commandLineOptions.typeshedPath = combinePaths(cwd, 'src', 'tests', 'samples');
service.setOptions(commandLineOptions);
@ -362,7 +362,7 @@ test('verify can serialize config options', () => {
test('extra paths on undefined execution root/default workspace', () => {
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(undefined, /* fromLanguageServer */ false);
const commandLineOptions = new CommandLineOptions(undefined, /* fromVsCodeExtension */ false);
commandLineOptions.extraPaths = ['/extraPaths'];
service.setOptions(commandLineOptions);
@ -378,7 +378,7 @@ test('extra paths on undefined execution root/default workspace', () => {
test('Extended config files', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_with_extended_config'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
service.setOptions(commandLineOptions);
@ -389,121 +389,3 @@ test('Extended config files', () => {
const configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.diagnosticRuleSet.strictListInference, true);
});
test('Typechecking mode is standard when just config file is present', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_with_pyproject_toml'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
commandLineOptions.typeCheckingMode = 'off';
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.diagnosticRuleSet.reportPossiblyUnboundVariable, 'error');
});
test('Typechecking mode depends upon if vscode extension or not', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/package1'));
let service = createAnalyzer();
let commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
service.setOptions(commandLineOptions);
let configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.diagnosticRuleSet.reportPossiblyUnboundVariable, 'none');
service = createAnalyzer();
commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
service.setOptions(commandLineOptions);
configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.diagnosticRuleSet.reportPossiblyUnboundVariable, 'error');
commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
commandLineOptions.typeCheckingMode = 'strict';
service = createAnalyzer();
service.setOptions(commandLineOptions);
configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.diagnosticRuleSet.reportPossiblyUnboundVariable, 'error');
});
test('Include file paths are only set in the config file when using extension', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project1'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
commandLineOptions.includeFileSpecs = ['test'];
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.include.length, 1);
assert.ok(configOptions.include[0].regExp.source.includes('/subfolder1)'));
});
test('Include file paths can be added to on the command line with a config', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project1'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.includeFileSpecs = ['test'];
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.include.length, 2);
assert.ok(configOptions.include[1].regExp.source.includes('/test)'));
});
test('Include file paths can be added to by an extension without a config', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/package1'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
commandLineOptions.includeFileSpecs = ['test'];
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
assert.equal(configOptions.include.length, 1);
assert.ok(configOptions.include[0].regExp.source.includes('/test)'));
});
test('Command line options can override config but only when not using extension', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_with_all_config'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
service.setOptions(commandLineOptions);
// First get the default.
const defaultOptions = service.test_getConfigOptions(commandLineOptions);
// Now set all of the different options and make sure the command line options override.
commandLineOptions.typeCheckingMode = 'strict';
commandLineOptions.venvPath = 'test2';
commandLineOptions.typeshedPath = 'test2';
commandLineOptions.stubPath = 'test2';
commandLineOptions.useLibraryCodeForTypes = true;
commandLineOptions.includeFileSpecs = ['test2'];
commandLineOptions.excludeFileSpecs = ['test2'];
commandLineOptions.extraPaths = ['test2'];
commandLineOptions.diagnosticSeverityOverrides = { reportMissingImports: DiagnosticSeverityOverrides.Error };
commandLineOptions.ignoreFileSpecs = ['test2'];
service.setOptions(commandLineOptions);
const overriddenOptions = service.test_getConfigOptions(commandLineOptions);
assert.notDeepStrictEqual(defaultOptions.include, overriddenOptions.include);
assert.notDeepStrictEqual(defaultOptions.exclude, overriddenOptions.exclude);
assert.notDeepStrictEqual(defaultOptions.ignore, overriddenOptions.ignore);
assert.notDeepStrictEqual(defaultOptions.diagnosticRuleSet, overriddenOptions.diagnosticRuleSet);
assert.notDeepStrictEqual(
defaultOptions.executionEnvironments[0].extraPaths,
overriddenOptions.executionEnvironments[0].extraPaths
);
// Venv, typeshed and stub path are an exception, it should just be reported as a dupe.
assert.deepStrictEqual(defaultOptions.venvPath, overriddenOptions.venvPath);
assert.deepStrictEqual(defaultOptions.typeshedPath, overriddenOptions.typeshedPath);
assert.deepStrictEqual(defaultOptions.stubPath, overriddenOptions.stubPath);
// Do the same with an extension based config, but make sure we get the default back.
const commandLineOptions2 = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
service.setOptions(commandLineOptions2);
const overriddenOptions2 = service.test_getConfigOptions(commandLineOptions2);
assert.deepStrictEqual(defaultOptions, overriddenOptions2);
});

View File

@ -40,7 +40,6 @@ import { ConsoleInterface, ConsoleWithLogLevel, NullConsole } from '../../../com
import { Comparison, isNumber, isString, toBoolean } from '../../../common/core';
import * as debug from '../../../common/debug';
import { DiagnosticCategory } from '../../../common/diagnostic';
import { PyrightDocStringService } from '../../../common/docStringService';
import { FileEditAction } from '../../../common/editAction';
import { ReadOnlyFileSystem } from '../../../common/fileSystem';
import { LanguageServerInterface } from '../../../common/languageServerInterface';
@ -98,6 +97,7 @@ import {
import { TestFeatures, TestLanguageService } from './testLanguageService';
import { createVfsInfoFromFourSlashData, getMarkerByName, getMarkerName, getMarkerNames } from './testStateUtils';
import { verifyWorkspaceEdit } from './workspaceEditTestUtils';
import { PyrightDocStringService } from '../../../common/docStringService';
export interface TextChange {
span: TextRange;
@ -1505,7 +1505,7 @@ export class TestState {
) {
const commandLineOptions = new CommandLineOptions(
this.configOptions.projectRoot.getFilePath(),
/* fromLanguageServer */ false
/* fromVsCodeExtension */ false
);
commandLineOptions.verboseOutput = verboseOutput;
const verifier = new PackageTypeVerifier(

View File

@ -1,31 +0,0 @@
{
"analyzeUnannotatedFunctions": false,
"defineConstant": {
"CONSTANT1": "string",
},
"executionEnvironments": [
{
"root": ".",
"python": "python3",
"extraPaths": ["extraPath"]
}
],
"typeCheckingMode": "off",
"exclude": ["test"],
"include": ["test"],
"ignore": ["test"],
"extraPaths": ["test"],
"pythonVersion": "3.7",
"pythonPlatform": "All",
"strict": ["test"],
"deprecateTypingAliases": true,
"disableBytesTypePromotions": true,
"enableExperimentalFeatures": true,
"enableTypeIgnoreComments": false,
"stubPath": "test",
"typeshedPath": "test",
"venv": "test",
"venvPath": "test",
"useLibraryCodeForTypes": false,
"verboseOutput": true
}