Reapply the compiler flags change but separate out the 'language server' specific settings (#8726)

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

* Make sure config options cannot be overridden by settings.json

* Make sure extraPaths and python env information is updated from the command line

* Change fromVsCodeExtension to fromLanguageServer

* Separate command line options into json ones and language server ones

* Fix build error
This commit is contained in:
Rich Chiodo 2024-08-09 16:15:38 -07:00 committed by GitHub
parent 152350997d
commit 2aa9bf4c20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 606 additions and 304 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.
## Main Configuration Options
## 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.
- **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 @@ Relative paths specified within the config file are relative to the config file
- **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.
- **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.
- **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.
- **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.
- **verboseOutput** [boolean]: Specifies whether output logs should be verbose. This is useful when diagnosing certain problems like import resolution issues.
@ -38,13 +38,12 @@ Relative paths specified within the config file are relative to the config file
- **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.
- <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`.
- <a name="strictDictionaryInference"></a> **strictDictionaryInference** [boolean]: When inferring the type of a dictionarys keys and values, use strict type assumptions. For example, the expression `{'a': 1, 'b': 'a'}` could be inferred to be of type `dict[str, Any]` or `dict[str, int | str]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is `false`.
@ -65,9 +64,16 @@ Relative paths specified within the config file are relative to the config file
- <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). 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).
- **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.
- <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"`.
@ -435,6 +441,11 @@ 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

@ -88,14 +88,14 @@ export class PackageTypeVerifier {
// Make sure we have a default python platform and version.
// Allow the command-line parameters to override the normal defaults.
if (commandLineOptions.pythonPlatform) {
this._configOptions.defaultPythonPlatform = commandLineOptions.pythonPlatform;
if (commandLineOptions.configSettings.pythonPlatform) {
this._configOptions.defaultPythonPlatform = commandLineOptions.configSettings.pythonPlatform;
} else {
this._configOptions.ensureDefaultPythonPlatform(host, console);
}
if (commandLineOptions.pythonVersion) {
this._configOptions.defaultPythonVersion = commandLineOptions.pythonVersion;
if (commandLineOptions.configSettings.pythonVersion) {
this._configOptions.defaultPythonVersion = commandLineOptions.configSettings.pythonVersion;
} else {
this._configOptions.ensureDefaultPythonVersion(host, console);
}

View File

@ -14,7 +14,11 @@ import { AbstractCancellationTokenSource, CancellationToken } from 'vscode-langu
import { BackgroundAnalysisBase, RefreshOptions } from '../backgroundAnalysisBase';
import { CancellationProvider, DefaultCancellationProvider } from '../common/cancellationUtils';
import { CommandLineOptions } from '../common/commandLineOptions';
import {
CommandLineConfigOptions,
CommandLineLanguageServerOptions,
CommandLineOptions,
} from '../common/commandLineOptions';
import { ConfigOptions, matchFileSpecs } from '../common/configOptions';
import { ConsoleInterface, LogLevel, StandardConsole, log } from '../common/console';
import { isString } from '../common/core';
@ -501,19 +505,22 @@ export class AnalyzerService {
}
private get _watchForSourceChanges() {
return !!this._commandLineOptions?.watchForSourceChanges;
return !!this._commandLineOptions?.languageServerSettings.watchForSourceChanges;
}
private get _watchForLibraryChanges() {
return !!this._commandLineOptions?.watchForLibraryChanges && !!this._options.libraryReanalysisTimeProvider;
return (
!!this._commandLineOptions?.languageServerSettings.watchForLibraryChanges &&
!!this._options.libraryReanalysisTimeProvider
);
}
private get _watchForConfigChanges() {
return !!this._commandLineOptions?.watchForConfigChanges;
return !!this._commandLineOptions?.languageServerSettings.watchForConfigChanges;
}
private get _typeCheckingMode() {
return this._commandLineOptions?.typeCheckingMode;
return this._commandLineOptions?.configSettings.typeCheckingMode;
}
private get _verboseOutput(): boolean {
@ -521,7 +528,7 @@ export class AnalyzerService {
}
private get _typeStubTargetImportName() {
return this._commandLineOptions?.typeStubTargetImportName;
return this._commandLineOptions?.languageServerSettings.typeStubTargetImportName;
}
// Calculates the effective options based on the command-line options,
@ -571,7 +578,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.fromVsCodeExtension) {
if (!configFilePath && !commandLineOptions.fromLanguageServer) {
configFilePath = findConfigFileHereOrUp(this.fs, projectRoot);
}
@ -587,7 +594,7 @@ export class AnalyzerService {
// See if we can find a pyproject.toml file in this directory.
pyprojectFilePath = findPyprojectTomlFile(this.fs, projectRoot);
if (!pyprojectFilePath && !commandLineOptions.fromVsCodeExtension) {
if (!pyprojectFilePath && !commandLineOptions.fromLanguageServer) {
pyprojectFilePath = findPyprojectTomlFileHereOrUp(this.fs, projectRoot);
}
@ -600,71 +607,63 @@ 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.
for (const config of configs) {
configOptions.initializeFromJson(
config.configFileJsonObj,
config.configFileDirUri,
this.serviceProvider,
host,
commandLineOptions
host
);
}
// When not in language server mode, command line options override config file options.
if (!commandLineOptions.fromLanguageServer) {
this._applyCommandLineOverrides(configOptions, commandLineOptions.configSettings, projectRoot, false);
}
} else {
configOptions.applyDiagnosticOverrides(commandLineOptions.diagnosticSeverityOverrides);
// 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._applyCommandLineOverrides(
configOptions,
commandLineOptions.configSettings,
projectRoot,
commandLineOptions.fromLanguageServer
);
}
// Apply the command line options that are not in the config file. These settings
// only apply to the language server.
this._applyLanguageServerOptions(configOptions, commandLineOptions.languageServerSettings);
// 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) {
@ -685,50 +684,19 @@ export class AnalyzerService {
}
}
// 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.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`
if (!configOptions.defaultExtraPaths) {
configOptions.ensureDefaultExtraPaths(
this.fs,
commandLineOptions.configSettings.autoSearchPaths ?? false,
commandLineOptions.configSettings.extraPaths
);
};
// 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 (configOptions.defaultPythonPlatform === undefined) {
configOptions.defaultPythonPlatform = commandLineOptions.configSettings.pythonPlatform;
}
if (configOptions.defaultPythonVersion === undefined) {
configOptions.defaultPythonVersion = commandLineOptions.configSettings.pythonVersion;
}
// If the caller specified that "typeshedPath" is the root of the project,
@ -752,35 +720,10 @@ 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)) {
@ -846,7 +789,163 @@ export class AnalyzerService {
}
}
return configOptions;
// This is a special case. It can be set in the config file, but if it's set on the command line, we should always
// override it.
if (commandLineOptions.configSettings.verboseOutput !== undefined) {
configOptions.verboseOutput = commandLineOptions.configSettings.verboseOutput;
}
}
private _applyLanguageServerOptions(
configOptions: ConfigOptions,
languageServerOptions: CommandLineLanguageServerOptions
) {
configOptions.disableTaggedHints = !!languageServerOptions.disableTaggedHints;
if (languageServerOptions.checkOnlyOpenFiles !== undefined) {
configOptions.checkOnlyOpenFiles = languageServerOptions.checkOnlyOpenFiles;
}
if (languageServerOptions.autoImportCompletions !== undefined) {
configOptions.autoImportCompletions = languageServerOptions.autoImportCompletions;
}
if (languageServerOptions.indexing !== undefined) {
configOptions.indexing = languageServerOptions.indexing;
}
if (languageServerOptions.taskListTokens) {
configOptions.taskListTokens = languageServerOptions.taskListTokens;
}
if (languageServerOptions.logTypeEvaluationTime !== undefined) {
configOptions.logTypeEvaluationTime = languageServerOptions.logTypeEvaluationTime;
}
configOptions.typeEvaluationTimeThreshold = languageServerOptions.typeEvaluationTimeThreshold;
}
private _applyCommandLineOverrides(
configOptions: ConfigOptions,
commandLineOptions: CommandLineConfigOptions,
projectRoot: Uri,
fromLanguageServer: boolean
) {
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.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 = 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 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());
}
}
}
// Loads the config JSON object from the specified config file along with any
@ -1687,7 +1786,7 @@ export class AnalyzerService {
this._backgroundAnalysisProgram.setImportResolver(importResolver);
if (this._commandLineOptions?.fromVsCodeExtension || this._configOptions.verboseOutput) {
if (this._commandLineOptions?.fromLanguageServer || 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>'}`);
@ -1714,7 +1813,7 @@ export class AnalyzerService {
}
private _scheduleReanalysis(requireTrackedFileUpdate: boolean) {
if (this._disposed || !this._commandLineOptions?.enableAmbientAnalysis) {
if (this._disposed || !this._commandLineOptions?.languageServerSettings.enableAmbientAnalysis) {
// already disposed
return;
}

View File

@ -31,11 +31,9 @@ export function getDiagnosticSeverityOverrides() {
export type DiagnosticSeverityOverridesMap = { [ruleName: string]: DiagnosticSeverityOverrides };
// Some options can be specified from a source other than the pyright config file.
// This can be from command-line parameters or some other settings mechanism, like
// that provided through a language client like the VS Code editor. These options
// are later combined with those from the config file to produce the final configuration.
export class CommandLineOptions {
// Options that can be specified in a JSON config file. This list should match what is
// defined in the pyrightconfig.schema.json file.
export class CommandLineConfigOptions {
// A list of file specs to include in the analysis. Can contain
// directories, in which case all "*.py" files within those directories
// are included.
@ -56,19 +54,6 @@ export class CommandLineOptions {
// if they are included in the transitive closure of included files.
ignoreFileSpecs: string[] = [];
// Watch for changes in workspace source files.
watchForSourceChanges?: boolean | undefined;
// Watch for changes in environment library/search paths.
watchForLibraryChanges?: boolean | undefined;
// Watch for changes in config files.
watchForConfigChanges?: boolean | undefined;
// Path of config file. This option cannot be combined with
// file specs.
configFilePath?: string | undefined;
// Virtual environments directory.
venvPath?: string | undefined;
@ -89,23 +74,10 @@ export class CommandLineOptions {
// Path of typing folder
stubPath?: string | undefined;
// Absolute execution root (current working directory).
executionRoot: string | Uri | undefined;
// Type stub import target (for creation of type stubs).
typeStubTargetImportName?: string | undefined;
// Emit verbose information to console?
verboseOutput?: boolean | undefined;
// In the absence of type stubs, use library implementations
// to extract type information?
useLibraryCodeForTypes?: boolean | undefined;
// Indicates that only open files should be checked.
checkOnlyOpenFiles?: boolean | undefined;
// Look for a common root folders such as 'src' and automatically
// add them as extra paths if the user has not explicitly defined
// execution environments.
@ -119,14 +91,33 @@ export class CommandLineOptions {
// 'basic', 'standard', or 'strict'.
typeCheckingMode?: string | undefined;
// Indicates that the settings came from VS Code rather than
// from the command-line. Useful for providing clearer error
// messages.
fromVsCodeExtension: boolean;
// Indicates diagnostic severity overrides
diagnosticSeverityOverrides?: DiagnosticSeverityOverridesMap | undefined;
// Analyze functions and methods that have no type annotations?
analyzeUnannotatedFunctions?: boolean;
// Emit verbose information to console?
verboseOutput?: boolean | undefined;
}
// Options that are not specified in a JSON config file but apply to a language server.
export class CommandLineLanguageServerOptions {
// Watch for changes in workspace source files.
watchForSourceChanges?: boolean | undefined;
// Watch for changes in environment library/search paths.
watchForLibraryChanges?: boolean | undefined;
// Watch for changes in config files.
watchForConfigChanges?: boolean | undefined;
// Type stub import target (for creation of type stubs).
typeStubTargetImportName?: string | undefined;
// Indicates that only open files should be checked.
checkOnlyOpenFiles?: boolean | undefined;
// Offer auto-import completions.
autoImportCompletions?: boolean | undefined;
@ -145,14 +136,35 @@ export class CommandLineOptions {
// Run ambient analysis.
enableAmbientAnalysis = true;
// Analyze functions and methods that have no type annotations?
analyzeUnannotatedFunctions?: boolean;
// Disable reporting of hint diagnostics with tags?
disableTaggedHints?: boolean;
}
constructor(executionRoot: string | Uri | undefined, fromVsCodeExtension: boolean) {
// Some options can be specified from a source other than the pyright config file.
// This can be from command-line parameters or some other settings mechanism, like
// that provided through a language client like the VS Code editor. These options
// are later combined with those from the config file to produce the final configuration.
export class CommandLineOptions {
// Settings that are possible to set in a config.json file.
configSettings: CommandLineConfigOptions = new CommandLineConfigOptions();
// Settings that are not possible to set in a config.json file.
languageServerSettings: CommandLineLanguageServerOptions = new CommandLineLanguageServerOptions();
// Path of config file. This option cannot be combined with
// file specs.
configFilePath?: string | undefined;
// Absolute execution root (current working directory).
executionRoot: string | Uri | undefined;
// Indicates that the settings came from a language server rather than
// from the command-line. Useful for providing clearer error
// messages.
fromLanguageServer: boolean;
constructor(executionRoot: string | Uri | undefined, fromLanguageServer: boolean) {
this.executionRoot = executionRoot;
this.fromVsCodeExtension = fromVsCodeExtension;
this.fromLanguageServer = fromLanguageServer;
}
}

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

@ -95,30 +95,31 @@ function getEffectiveCommandLineOptions(
pythonEnvironmentName?: string
) {
const commandLineOptions = new CommandLineOptions(workspaceRootUri, true);
commandLineOptions.checkOnlyOpenFiles = serverSettings.openFilesOnly;
commandLineOptions.useLibraryCodeForTypes = serverSettings.useLibraryCodeForTypes;
commandLineOptions.typeCheckingMode = serverSettings.typeCheckingMode;
commandLineOptions.autoImportCompletions = serverSettings.autoImportCompletions;
commandLineOptions.indexing = serverSettings.indexing;
commandLineOptions.taskListTokens = serverSettings.taskListTokens;
commandLineOptions.logTypeEvaluationTime = serverSettings.logTypeEvaluationTime ?? false;
commandLineOptions.typeEvaluationTimeThreshold = serverSettings.typeEvaluationTimeThreshold ?? 50;
commandLineOptions.enableAmbientAnalysis = trackFiles;
commandLineOptions.pythonEnvironmentName = pythonEnvironmentName;
commandLineOptions.disableTaggedHints = serverSettings.disableTaggedHints;
commandLineOptions.languageServerSettings.checkOnlyOpenFiles = serverSettings.openFilesOnly;
commandLineOptions.configSettings.useLibraryCodeForTypes = serverSettings.useLibraryCodeForTypes;
commandLineOptions.configSettings.typeCheckingMode = serverSettings.typeCheckingMode;
commandLineOptions.languageServerSettings.autoImportCompletions = serverSettings.autoImportCompletions;
commandLineOptions.languageServerSettings.indexing = serverSettings.indexing;
commandLineOptions.languageServerSettings.taskListTokens = serverSettings.taskListTokens;
commandLineOptions.languageServerSettings.logTypeEvaluationTime = serverSettings.logTypeEvaluationTime ?? false;
commandLineOptions.languageServerSettings.typeEvaluationTimeThreshold =
serverSettings.typeEvaluationTimeThreshold ?? 50;
commandLineOptions.languageServerSettings.enableAmbientAnalysis = trackFiles;
commandLineOptions.configSettings.pythonEnvironmentName = pythonEnvironmentName;
commandLineOptions.languageServerSettings.disableTaggedHints = serverSettings.disableTaggedHints;
if (!trackFiles) {
commandLineOptions.watchForSourceChanges = false;
commandLineOptions.watchForLibraryChanges = false;
commandLineOptions.watchForConfigChanges = false;
commandLineOptions.languageServerSettings.watchForSourceChanges = false;
commandLineOptions.languageServerSettings.watchForLibraryChanges = false;
commandLineOptions.languageServerSettings.watchForConfigChanges = false;
} else {
commandLineOptions.watchForSourceChanges = serverSettings.watchForSourceChanges;
commandLineOptions.watchForLibraryChanges = serverSettings.watchForLibraryChanges;
commandLineOptions.watchForConfigChanges = serverSettings.watchForConfigChanges;
commandLineOptions.languageServerSettings.watchForSourceChanges = serverSettings.watchForSourceChanges;
commandLineOptions.languageServerSettings.watchForLibraryChanges = serverSettings.watchForLibraryChanges;
commandLineOptions.languageServerSettings.watchForConfigChanges = serverSettings.watchForConfigChanges;
}
if (serverSettings.venvPath) {
commandLineOptions.venvPath = serverSettings.venvPath.getFilePath();
commandLineOptions.configSettings.venvPath = serverSettings.venvPath.getFilePath();
}
if (serverSettings.pythonPath) {
@ -126,7 +127,7 @@ function getEffectiveCommandLineOptions(
// the local python interpreter should be used rather than interpreting the
// setting value as a path to the interpreter. We'll simply ignore it in this case.
if (!isPythonBinary(serverSettings.pythonPath.getFilePath())) {
commandLineOptions.pythonPath = serverSettings.pythonPath.getFilePath();
commandLineOptions.configSettings.pythonPath = serverSettings.pythonPath.getFilePath();
}
}
@ -134,30 +135,30 @@ function getEffectiveCommandLineOptions(
// Pyright supports only one typeshed path currently, whereas the
// official VS Code Python extension supports multiple typeshed paths.
// We'll use the first one specified and ignore the rest.
commandLineOptions.typeshedPath = serverSettings.typeshedPath.getFilePath();
commandLineOptions.configSettings.typeshedPath = serverSettings.typeshedPath.getFilePath();
}
if (serverSettings.stubPath) {
commandLineOptions.stubPath = serverSettings.stubPath.getFilePath();
commandLineOptions.configSettings.stubPath = serverSettings.stubPath.getFilePath();
}
if (serverSettings.logLevel === LogLevel.Log) {
// When logLevel is "Trace", turn on verboseOutput as well
// so we can get detailed log from analysis service.
commandLineOptions.verboseOutput = true;
commandLineOptions.configSettings.verboseOutput = true;
}
if (typeStubTargetImportName) {
commandLineOptions.typeStubTargetImportName = typeStubTargetImportName;
commandLineOptions.languageServerSettings.typeStubTargetImportName = typeStubTargetImportName;
}
commandLineOptions.autoSearchPaths = serverSettings.autoSearchPaths;
commandLineOptions.extraPaths = serverSettings.extraPaths?.map((e) => e.getFilePath()) ?? [];
commandLineOptions.diagnosticSeverityOverrides = serverSettings.diagnosticSeverityOverrides;
commandLineOptions.configSettings.autoSearchPaths = serverSettings.autoSearchPaths;
commandLineOptions.configSettings.extraPaths = serverSettings.extraPaths?.map((e) => e.getFilePath()) ?? [];
commandLineOptions.configSettings.diagnosticSeverityOverrides = serverSettings.diagnosticSeverityOverrides;
commandLineOptions.includeFileSpecs = serverSettings.includeFileSpecs ?? [];
commandLineOptions.excludeFileSpecs = serverSettings.excludeFileSpecs ?? [];
commandLineOptions.ignoreFileSpecs = serverSettings.ignoreFileSpecs ?? [];
commandLineOptions.configSettings.includeFileSpecs = serverSettings.includeFileSpecs ?? [];
commandLineOptions.configSettings.excludeFileSpecs = serverSettings.excludeFileSpecs ?? [];
commandLineOptions.configSettings.ignoreFileSpecs = serverSettings.ignoreFileSpecs ?? [];
return commandLineOptions;
}

View File

@ -267,13 +267,15 @@ async function processArgs(): Promise<ExitStatus> {
}
}
options.includeFileSpecsOverride = fileSpecList;
options.includeFileSpecsOverride = options.includeFileSpecsOverride.map((f) => combinePaths(process.cwd(), f));
options.configSettings.includeFileSpecsOverride = fileSpecList;
options.configSettings.includeFileSpecsOverride = options.configSettings.includeFileSpecsOverride.map((f) =>
combinePaths(process.cwd(), f)
);
// Verify the specified file specs to make sure their wildcard roots exist.
const tempFileSystem = new PyrightFileSystem(createFromRealFileSystem(tempFile));
for (const fileDesc of options.includeFileSpecsOverride) {
for (const fileDesc of options.configSettings.includeFileSpecsOverride) {
const includeSpec = getFileSpec(Uri.file(process.cwd(), tempFile), fileDesc);
try {
const stat = tryStat(tempFileSystem, includeSpec.wildcardRoot);
@ -293,7 +295,7 @@ async function processArgs(): Promise<ExitStatus> {
if (args.pythonplatform) {
if (args.pythonplatform === 'Darwin' || args.pythonplatform === 'Linux' || args.pythonplatform === 'Windows') {
options.pythonPlatform = args.pythonplatform;
options.configSettings.pythonPlatform = args.pythonplatform;
} else {
console.error(
`'${args.pythonplatform}' is not a supported Python platform; specify Darwin, Linux, or Windows`
@ -305,7 +307,7 @@ async function processArgs(): Promise<ExitStatus> {
if (args.pythonversion) {
const version = PythonVersion.fromString(args.pythonversion);
if (version) {
options.pythonVersion = version;
options.configSettings.pythonVersion = version;
} else {
console.error(`'${args.pythonversion}' is not a supported Python version; specify 3.3, 3.4, etc.`);
return ExitStatus.ParameterError;
@ -321,41 +323,41 @@ async function processArgs(): Promise<ExitStatus> {
}
}
options.pythonPath = combinePaths(process.cwd(), normalizePath(args['pythonpath']));
options.configSettings.pythonPath = combinePaths(process.cwd(), normalizePath(args['pythonpath']));
}
if (args['venv-path']) {
console.warn(`'venv-path' option is deprecated; use 'venvpath' instead`);
options.venvPath = combinePaths(process.cwd(), normalizePath(args['venv-path']));
options.configSettings.venvPath = combinePaths(process.cwd(), normalizePath(args['venv-path']));
}
if (args['venvpath']) {
options.venvPath = combinePaths(process.cwd(), normalizePath(args['venvpath']));
options.configSettings.venvPath = combinePaths(process.cwd(), normalizePath(args['venvpath']));
}
if (args['typeshed-path']) {
console.warn(`'typeshed-path' option is deprecated; use 'typeshedpath' instead`);
options.typeshedPath = combinePaths(process.cwd(), normalizePath(args['typeshed-path']));
options.configSettings.typeshedPath = combinePaths(process.cwd(), normalizePath(args['typeshed-path']));
}
if (args['typeshedpath']) {
options.typeshedPath = combinePaths(process.cwd(), normalizePath(args['typeshedpath']));
options.configSettings.typeshedPath = combinePaths(process.cwd(), normalizePath(args['typeshedpath']));
}
if (args.createstub) {
options.typeStubTargetImportName = args.createstub;
options.languageServerSettings.typeStubTargetImportName = args.createstub;
}
if (args.skipunannotated) {
options.analyzeUnannotatedFunctions = false;
options.configSettings.analyzeUnannotatedFunctions = false;
}
if (args.verbose) {
options.verboseOutput = true;
options.configSettings.verboseOutput = true;
}
// Always enable autoSearchPaths when using the command line.
options.autoSearchPaths = true;
options.configSettings.autoSearchPaths = true;
if (args.lib) {
console.warn(`The --lib option is deprecated. Pyright now defaults to using library code to infer types.`);
@ -372,10 +374,10 @@ async function processArgs(): Promise<ExitStatus> {
}
}
options.checkOnlyOpenFiles = false;
options.languageServerSettings.checkOnlyOpenFiles = false;
if (!!args.stats && !!args.verbose) {
options.logTypeEvaluationTime = true;
options.languageServerSettings.logTypeEvaluationTime = true;
}
let logLevel = LogLevel.Error;
@ -408,8 +410,8 @@ async function processArgs(): Promise<ExitStatus> {
}
const watch = args.watch !== undefined;
options.watchForSourceChanges = watch;
options.watchForConfigChanges = watch;
options.languageServerSettings.watchForSourceChanges = watch;
options.languageServerSettings.watchForConfigChanges = watch;
const service = new AnalyzerService('<default>', serviceProvider, {
console: output,
@ -554,7 +556,7 @@ async function runMultiThreaded(
// Specify that only open files should be checked. This will allow us
// to control which files are checked by which workers.
options.checkOnlyOpenFiles = true;
options.languageServerSettings.checkOnlyOpenFiles = true;
// This will trigger discovery of files in the project.
service.setOptions(options);
@ -751,7 +753,7 @@ function runWorkerMessageLoop(workerNum: number) {
});
let logLevel = LogLevel.Error;
if (options.verboseOutput) {
if (options.configSettings.verboseOutput) {
logLevel = LogLevel.Info;
}
@ -836,7 +838,7 @@ function verifyPackageTypes(
if (outputJson) {
console.info(JSON.stringify(jsonReport, /* replacer */ undefined, 4));
} else {
printTypeCompletenessReportText(jsonReport, !!options.verboseOutput);
printTypeCompletenessReportText(jsonReport, !!options.configSettings.verboseOutput);
}
return jsonReport.typeCompleteness!.completenessScore < 1 ? ExitStatus.ErrorsReported : ExitStatus.NoErrors;

View File

@ -11,9 +11,10 @@ import assert from 'assert';
import { AnalyzerService } from '../analyzer/service';
import { deserialize, serialize } from '../backgroundThreadBase';
import { CommandLineOptions } from '../common/commandLineOptions';
import { CommandLineOptions, DiagnosticSeverityOverrides } from '../common/commandLineOptions';
import { ConfigOptions, ExecutionEnvironment, getStandardDiagnosticRuleSet } from '../common/configOptions';
import { ConsoleInterface, NullConsole } from '../common/console';
import { TaskListPriority } from '../common/diagnostic';
import { NoAccessHost } from '../common/host';
import { combinePaths, normalizePath, normalizeSlashes } from '../common/pathUtils';
import { pythonVersion3_9 } from '../common/pythonVersion';
@ -35,7 +36,7 @@ function createAnalyzer(console?: ConsoleInterface) {
test('FindFilesWithConfigFile', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
commandLineOptions.configFilePath = 'src/tests/samples/project1';
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -60,7 +61,7 @@ test('FindFilesWithConfigFile', () => {
test('FindFilesVirtualEnvAutoDetectExclude', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
commandLineOptions.configFilePath = 'src/tests/samples/project_with_venv_auto_detect_exclude';
service.setOptions(commandLineOptions);
@ -78,7 +79,7 @@ test('FindFilesVirtualEnvAutoDetectExclude', () => {
test('FindFilesVirtualEnvAutoDetectInclude', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
commandLineOptions.configFilePath = 'src/tests/samples/project_with_venv_auto_detect_include';
service.setOptions(commandLineOptions);
@ -97,7 +98,7 @@ test('FileSpecNotAnArray', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project2';
service.setOptions(commandLineOptions);
@ -111,7 +112,7 @@ test('FileSpecNotAString', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project3';
service.setOptions(commandLineOptions);
@ -125,7 +126,7 @@ test('SomeFileSpecsAreInvalid', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project4';
service.setOptions(commandLineOptions);
@ -152,7 +153,7 @@ test('ConfigBadJson', () => {
const cwd = normalizePath(process.cwd());
const nullConsole = new NullConsole();
const service = createAnalyzer(nullConsole);
const commandLineOptions = new CommandLineOptions(cwd, /* fromVsCodeExtension */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.configFilePath = 'src/tests/samples/project5';
service.setOptions(commandLineOptions);
@ -231,8 +232,8 @@ test('AutoSearchPathsOn', () => {
normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src')),
service.serviceProvider
);
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = true;
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromLanguageServer */ false);
commandLineOptions.configSettings.autoSearchPaths = true;
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -245,8 +246,8 @@ 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, /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = false;
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.configSettings.autoSearchPaths = false;
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -258,8 +259,8 @@ 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, /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = true;
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.configSettings.autoSearchPaths = true;
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -272,9 +273,9 @@ 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, /* fromVsCodeExtension */ false);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.configFilePath = combinePaths(cwd, 'pyrightconfig.json');
commandLineOptions.autoSearchPaths = true;
commandLineOptions.configSettings.autoSearchPaths = true;
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -292,9 +293,9 @@ test('AutoSearchPathsOnAndExtraPaths', () => {
normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_src_with_config_no_extra_paths')),
service.serviceProvider
);
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromVsCodeExtension */ false);
commandLineOptions.autoSearchPaths = true;
commandLineOptions.extraPaths = ['src/_vendored'];
const commandLineOptions = new CommandLineOptions(cwd.getFilePath(), /* fromLanguageServer */ false);
commandLineOptions.configSettings.autoSearchPaths = true;
commandLineOptions.configSettings.extraPaths = ['src/_vendored'];
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -310,7 +311,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, /* fromVsCodeExtension */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
service.setOptions(commandLineOptions);
@ -323,9 +324,9 @@ test('BasicPyprojectTomlParsing', () => {
test('FindFilesInMemoryOnly', () => {
const cwd = normalizePath(process.cwd());
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(undefined, /* fromVsCodeExtension */ true);
const commandLineOptions = new CommandLineOptions(undefined, /* fromLanguageServer */ 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');
commandLineOptions.configSettings.typeshedPath = combinePaths(cwd, 'src', 'tests', 'samples');
service.setOptions(commandLineOptions);
// Open a file that is not backed by the file system.
@ -362,8 +363,8 @@ 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, /* fromVsCodeExtension */ false);
commandLineOptions.extraPaths = ['/extraPaths'];
const commandLineOptions = new CommandLineOptions(undefined, /* fromLanguageServer */ false);
commandLineOptions.configSettings.extraPaths = ['/extraPaths'];
service.setOptions(commandLineOptions);
const configOptions = service.test_getConfigOptions(commandLineOptions);
@ -378,7 +379,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, /* fromVsCodeExtension */ true);
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true);
service.setOptions(commandLineOptions);
@ -389,3 +390,162 @@ 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.configSettings.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.configSettings.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.configSettings.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.configSettings.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.configSettings.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.configSettings.typeCheckingMode = 'strict';
commandLineOptions.configSettings.venvPath = 'test2';
commandLineOptions.configSettings.typeshedPath = 'test2';
commandLineOptions.configSettings.stubPath = 'test2';
commandLineOptions.configSettings.useLibraryCodeForTypes = true;
commandLineOptions.configSettings.includeFileSpecs = ['test2'];
commandLineOptions.configSettings.excludeFileSpecs = ['test2'];
commandLineOptions.configSettings.extraPaths = ['test2'];
commandLineOptions.configSettings.diagnosticSeverityOverrides = {
reportMissingImports: DiagnosticSeverityOverrides.Error,
};
commandLineOptions.configSettings.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);
});
test('Language server specific settings are set whether or not there is a pyproject.toml', () => {
const cwd = normalizePath(combinePaths(process.cwd(), 'src/tests/samples/project_with_all_config'));
const service = createAnalyzer();
const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ false);
commandLineOptions.languageServerSettings.autoImportCompletions = true;
commandLineOptions.languageServerSettings.indexing = true;
commandLineOptions.languageServerSettings.taskListTokens = [{ priority: TaskListPriority.High, text: 'test' }];
commandLineOptions.languageServerSettings.logTypeEvaluationTime = true;
commandLineOptions.languageServerSettings.typeEvaluationTimeThreshold = 1;
commandLineOptions.languageServerSettings.enableAmbientAnalysis = false;
commandLineOptions.languageServerSettings.disableTaggedHints = true;
commandLineOptions.languageServerSettings.watchForSourceChanges = true;
commandLineOptions.languageServerSettings.watchForLibraryChanges = true;
commandLineOptions.languageServerSettings.watchForConfigChanges = true;
commandLineOptions.languageServerSettings.typeStubTargetImportName = 'test';
commandLineOptions.languageServerSettings.checkOnlyOpenFiles = true;
commandLineOptions.languageServerSettings.disableTaggedHints = true;
service.setOptions(commandLineOptions);
let options = service.test_getConfigOptions(commandLineOptions);
assert.strictEqual(options.autoImportCompletions, true);
assert.strictEqual(options.indexing, true);
assert.strictEqual(options.taskListTokens?.length, 1);
assert.strictEqual(options.logTypeEvaluationTime, true);
assert.strictEqual(options.typeEvaluationTimeThreshold, 1);
assert.strictEqual(options.disableTaggedHints, true);
// Test with language server set to true to make sure they are still set.
commandLineOptions.fromLanguageServer = true;
service.setOptions(commandLineOptions);
options = service.test_getConfigOptions(commandLineOptions);
assert.strictEqual(options.autoImportCompletions, true);
assert.strictEqual(options.indexing, true);
assert.strictEqual(options.taskListTokens?.length, 1);
assert.strictEqual(options.logTypeEvaluationTime, true);
assert.strictEqual(options.typeEvaluationTimeThreshold, 1);
assert.strictEqual(options.disableTaggedHints, true);
});

View File

@ -40,6 +40,7 @@ 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';
@ -97,7 +98,6 @@ 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,9 +1505,9 @@ export class TestState {
) {
const commandLineOptions = new CommandLineOptions(
this.configOptions.projectRoot.getFilePath(),
/* fromVsCodeExtension */ false
/* fromLanguageServer */ false
);
commandLineOptions.verboseOutput = verboseOutput;
commandLineOptions.configSettings.verboseOutput = verboseOutput;
const verifier = new PackageTypeVerifier(
this.serviceProvider,
testAccessHost,

View File

@ -0,0 +1,31 @@
{
"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
}