Did a pass over pyright source code to improve consistency of file header comments. No functional change.

This commit is contained in:
Eric Traut 2023-03-26 14:05:02 -06:00
parent 47efdfcf2c
commit 5cf4aedbbf
39 changed files with 549 additions and 559 deletions

View File

@ -1,200 +0,0 @@
/*
* aliasDeclarationUtils.ts
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Helper functions around alias declarations.
*/
import { ImportLookup, ImportLookupResult } from './analyzerFileInfo';
import { Declaration, DeclarationType } from './declaration';
import { Symbol } from './symbol';
export interface ResolvedAliasInfo {
declaration: Declaration | undefined;
isPrivate: boolean;
privatePyTypedImported?: string;
privatePyTypedImporter?: string;
}
// If the specified declaration is an alias declaration that points to a symbol,
// it resolves the alias and looks up the symbol, then returns the a declaration
// (typically the last) associated with that symbol. It does this recursively if
// necessary. If a symbol lookup fails, undefined is returned. If resolveLocalNames
// is true, the method resolves aliases through local renames ("as" clauses found
// in import statements).
export function resolveAliasDeclaration(
importLookup: ImportLookup,
declaration: Declaration,
resolveLocalNames: boolean,
allowExternallyHiddenAccess: boolean
): ResolvedAliasInfo | undefined {
let curDeclaration: Declaration | undefined = declaration;
const alreadyVisited: Declaration[] = [];
let isPrivate = false;
// These variables are used to find a transition from a non-py.typed to
// a py.typed resolution chain. In this case, if the imported symbol
// is a private symbol (i.e. not intended to be re-exported), we store
// the name of the importer and imported modules so the caller can
// report an error.
let sawPyTypedTransition = false;
let privatePyTypedImported: string | undefined;
let privatePyTypedImporter: string | undefined;
while (true) {
if (curDeclaration.type !== DeclarationType.Alias || !curDeclaration.symbolName) {
return {
declaration: curDeclaration,
isPrivate,
privatePyTypedImported,
privatePyTypedImporter,
};
}
// If we are not supposed to follow local alias names and this
// is a local name, don't continue to follow the alias.
if (!resolveLocalNames && curDeclaration.usesLocalName) {
return {
declaration: curDeclaration,
isPrivate,
privatePyTypedImported,
privatePyTypedImporter,
};
}
let lookupResult: ImportLookupResult | undefined;
if (curDeclaration.path && curDeclaration.loadSymbolsFromPath) {
lookupResult = importLookup(curDeclaration.path);
}
const symbol: Symbol | undefined = lookupResult
? lookupResult.symbolTable.get(curDeclaration.symbolName)
: undefined;
if (!symbol) {
if (curDeclaration.submoduleFallback) {
if (curDeclaration.symbolName) {
// See if we are resolving a specific imported symbol name and the submodule
// fallback cannot be resolved. For example, `from a import b`. If b is both
// a symbol in `a/__init__.py` and a submodule `a/b.py` and we are not using
// type information from this library (e.g. a non-py.typed library source file
// when useLibraryCodeForTypes is disabled), b should be evaluated as Unknown,
// not as a module.
if (
curDeclaration.submoduleFallback.type === DeclarationType.Alias &&
curDeclaration.submoduleFallback.path
) {
const lookupResult = importLookup(curDeclaration.submoduleFallback.path);
if (!lookupResult) {
return undefined;
}
}
}
return resolveAliasDeclaration(
importLookup,
curDeclaration.submoduleFallback,
resolveLocalNames,
allowExternallyHiddenAccess
);
}
// If the symbol comes from a native library, we won't
// be able to resolve its type directly.
if (curDeclaration.isNativeLib) {
return {
declaration: undefined,
isPrivate,
};
}
return undefined;
}
if (symbol.isPrivateMember()) {
isPrivate = true;
}
if (symbol.isExternallyHidden() && !allowExternallyHiddenAccess) {
return undefined;
}
// Prefer declarations with specified types. If we don't have any of those,
// fall back on declarations with inferred types.
let declarations = symbol.getTypedDeclarations();
// Try not to use declarations within an except suite even if it's a typed
// declaration. These are typically used for fallback exception handling.
declarations = declarations.filter((decl) => !decl.isInExceptSuite);
if (declarations.length === 0) {
declarations = symbol.getDeclarations();
declarations = declarations.filter((decl) => !decl.isInExceptSuite);
}
if (declarations.length === 0) {
// Use declarations within except clauses if there are no alternatives.
declarations = symbol.getDeclarations();
}
if (declarations.length === 0) {
return undefined;
}
// Prefer the last unvisited declaration in the list. This ensures that
// we use all of the overloads if it's an overloaded function.
const unvisitedDecls = declarations.filter((decl) => !alreadyVisited.includes(decl));
if (unvisitedDecls.length > 0) {
curDeclaration = unvisitedDecls[unvisitedDecls.length - 1];
} else {
curDeclaration = declarations[declarations.length - 1];
}
if (lookupResult?.isInPyTypedPackage) {
if (!sawPyTypedTransition) {
if (symbol.isPrivatePyTypedImport()) {
privatePyTypedImporter = curDeclaration?.moduleName;
}
// Note that we've seen a transition from a non-py.typed to a py.typed
// import. No further check is needed.
sawPyTypedTransition = true;
} else {
// If we've already seen a transition, look for the first non-private
// symbol that is resolved so we can tell the user to import from this
// location instead.
if (!symbol.isPrivatePyTypedImport()) {
privatePyTypedImported = privatePyTypedImported ?? curDeclaration?.moduleName;
}
}
}
// Make sure we don't follow a circular list indefinitely.
if (alreadyVisited.find((decl) => decl === curDeclaration)) {
// If the path path of the alias points back to the original path, use the submodule
// fallback instead. This happens in the case where a module's __init__.py file
// imports a submodule using itself as the import target. For example, if
// the module is foo, and the foo.__init__.py file contains the statement
// "from foo import bar", we want to import the foo/bar.py submodule.
if (
curDeclaration.path === declaration.path &&
curDeclaration.type === DeclarationType.Alias &&
curDeclaration.submoduleFallback
) {
return resolveAliasDeclaration(
importLookup,
curDeclaration.submoduleFallback,
resolveLocalNames,
allowExternallyHiddenAccess
);
}
return {
declaration,
isPrivate,
privatePyTypedImported,
privatePyTypedImporter,
};
}
alreadyVisited.push(curDeclaration);
}
}

View File

@ -4,7 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* A singleton that tracks the size of caches and empty them
* A singleton that tracks the size of caches and empties them
* if memory usage approaches the max heap space.
*/

View File

@ -11,6 +11,7 @@
* and checked. It also performs some additional checks that
* cannot (or should not be) performed lazily.
*/
import { CancellationToken } from 'vscode-languageserver';
import { Commands } from '../commands/commands';
@ -95,6 +96,7 @@ import { Declaration, DeclarationType, isAliasDeclaration } from './declaration'
import { createImportedModuleDescriptor, ImportedModuleDescriptor, ImportResolver } from './importResolver';
import { ImportResult, ImportType } from './importResult';
import { getRelativeModuleName, getTopLevelImports } from './importStatementUtils';
import { getParameterListDetails } from './parameterUtils';
import * as ParseTreeUtils from './parseTreeUtils';
import { ParseTreeWalker } from './parseTreeWalker';
import { validateClassPattern } from './patternMatching';
@ -158,7 +160,6 @@ import {
getDeclaredGeneratorReturnType,
getGeneratorTypeArgs,
getGeneratorYieldType,
getParameterListDetails,
getProtocolSymbols,
getTypeVarArgumentsRecursive,
getTypeVarScopeId,

View File

@ -15,17 +15,16 @@ import { DiagnosticRule } from '../common/diagnosticRules';
import { Localizer } from '../localization/localize';
import { ArgumentCategory, ExpressionNode, ParameterCategory } from '../parser/parseNodes';
import { getFileInfo } from './analyzerNodeInfo';
import { getParameterListDetails, ParameterSource } from './parameterUtils';
import { Symbol, SymbolFlags } from './symbol';
import { FunctionArgument, FunctionResult, TypeEvaluator } from './typeEvaluatorTypes';
import { ClassType, FunctionParameter, FunctionType, isClassInstance, isFunction, isTypeSame } from './types';
import {
applySolvedTypeVars,
convertToInstance,
getParameterListDetails,
getTypeVarScopeId,
lookUpObjectMember,
makeInferenceContext,
ParameterSource,
} from './typeUtils';
import { TypeVarContext } from './typeVarContext';

View File

@ -9,8 +9,17 @@
import { getEmptyRange } from '../common/textRange';
import { NameNode, ParseNodeType } from '../parser/parseNodes';
import { ImportLookup, ImportLookupResult } from './analyzerFileInfo';
import { AliasDeclaration, Declaration, DeclarationType, isAliasDeclaration, ModuleLoaderActions } from './declaration';
import { getFileInfoFromNode } from './parseTreeUtils';
import { Symbol } from './symbol';
export interface ResolvedAliasInfo {
declaration: Declaration | undefined;
isPrivate: boolean;
privatePyTypedImported?: string;
privatePyTypedImporter?: string;
}
export function hasTypeForDeclaration(declaration: Declaration): boolean {
switch (declaration.type) {
@ -200,3 +209,185 @@ export function createSynthesizedAliasDeclaration(path: string): AliasDeclaratio
isInExceptSuite: false,
};
}
// If the specified declaration is an alias declaration that points to a symbol,
// it resolves the alias and looks up the symbol, then returns the a declaration
// (typically the last) associated with that symbol. It does this recursively if
// necessary. If a symbol lookup fails, undefined is returned. If resolveLocalNames
// is true, the method resolves aliases through local renames ("as" clauses found
// in import statements).
export function resolveAliasDeclaration(
importLookup: ImportLookup,
declaration: Declaration,
resolveLocalNames: boolean,
allowExternallyHiddenAccess: boolean
): ResolvedAliasInfo | undefined {
let curDeclaration: Declaration | undefined = declaration;
const alreadyVisited: Declaration[] = [];
let isPrivate = false;
// These variables are used to find a transition from a non-py.typed to
// a py.typed resolution chain. In this case, if the imported symbol
// is a private symbol (i.e. not intended to be re-exported), we store
// the name of the importer and imported modules so the caller can
// report an error.
let sawPyTypedTransition = false;
let privatePyTypedImported: string | undefined;
let privatePyTypedImporter: string | undefined;
while (true) {
if (curDeclaration.type !== DeclarationType.Alias || !curDeclaration.symbolName) {
return {
declaration: curDeclaration,
isPrivate,
privatePyTypedImported,
privatePyTypedImporter,
};
}
// If we are not supposed to follow local alias names and this
// is a local name, don't continue to follow the alias.
if (!resolveLocalNames && curDeclaration.usesLocalName) {
return {
declaration: curDeclaration,
isPrivate,
privatePyTypedImported,
privatePyTypedImporter,
};
}
let lookupResult: ImportLookupResult | undefined;
if (curDeclaration.path && curDeclaration.loadSymbolsFromPath) {
lookupResult = importLookup(curDeclaration.path);
}
const symbol: Symbol | undefined = lookupResult
? lookupResult.symbolTable.get(curDeclaration.symbolName)
: undefined;
if (!symbol) {
if (curDeclaration.submoduleFallback) {
if (curDeclaration.symbolName) {
// See if we are resolving a specific imported symbol name and the submodule
// fallback cannot be resolved. For example, `from a import b`. If b is both
// a symbol in `a/__init__.py` and a submodule `a/b.py` and we are not using
// type information from this library (e.g. a non-py.typed library source file
// when useLibraryCodeForTypes is disabled), b should be evaluated as Unknown,
// not as a module.
if (
curDeclaration.submoduleFallback.type === DeclarationType.Alias &&
curDeclaration.submoduleFallback.path
) {
const lookupResult = importLookup(curDeclaration.submoduleFallback.path);
if (!lookupResult) {
return undefined;
}
}
}
return resolveAliasDeclaration(
importLookup,
curDeclaration.submoduleFallback,
resolveLocalNames,
allowExternallyHiddenAccess
);
}
// If the symbol comes from a native library, we won't
// be able to resolve its type directly.
if (curDeclaration.isNativeLib) {
return {
declaration: undefined,
isPrivate,
};
}
return undefined;
}
if (symbol.isPrivateMember()) {
isPrivate = true;
}
if (symbol.isExternallyHidden() && !allowExternallyHiddenAccess) {
return undefined;
}
// Prefer declarations with specified types. If we don't have any of those,
// fall back on declarations with inferred types.
let declarations = symbol.getTypedDeclarations();
// Try not to use declarations within an except suite even if it's a typed
// declaration. These are typically used for fallback exception handling.
declarations = declarations.filter((decl) => !decl.isInExceptSuite);
if (declarations.length === 0) {
declarations = symbol.getDeclarations();
declarations = declarations.filter((decl) => !decl.isInExceptSuite);
}
if (declarations.length === 0) {
// Use declarations within except clauses if there are no alternatives.
declarations = symbol.getDeclarations();
}
if (declarations.length === 0) {
return undefined;
}
// Prefer the last unvisited declaration in the list. This ensures that
// we use all of the overloads if it's an overloaded function.
const unvisitedDecls = declarations.filter((decl) => !alreadyVisited.includes(decl));
if (unvisitedDecls.length > 0) {
curDeclaration = unvisitedDecls[unvisitedDecls.length - 1];
} else {
curDeclaration = declarations[declarations.length - 1];
}
if (lookupResult?.isInPyTypedPackage) {
if (!sawPyTypedTransition) {
if (symbol.isPrivatePyTypedImport()) {
privatePyTypedImporter = curDeclaration?.moduleName;
}
// Note that we've seen a transition from a non-py.typed to a py.typed
// import. No further check is needed.
sawPyTypedTransition = true;
} else {
// If we've already seen a transition, look for the first non-private
// symbol that is resolved so we can tell the user to import from this
// location instead.
if (!symbol.isPrivatePyTypedImport()) {
privatePyTypedImported = privatePyTypedImported ?? curDeclaration?.moduleName;
}
}
}
// Make sure we don't follow a circular list indefinitely.
if (alreadyVisited.find((decl) => decl === curDeclaration)) {
// If the path path of the alias points back to the original path, use the submodule
// fallback instead. This happens in the case where a module's __init__.py file
// imports a submodule using itself as the import target. For example, if
// the module is foo, and the foo.__init__.py file contains the statement
// "from foo import bar", we want to import the foo/bar.py submodule.
if (
curDeclaration.path === declaration.path &&
curDeclaration.type === DeclarationType.Alias &&
curDeclaration.submoduleFallback
) {
return resolveAliasDeclaration(
importLookup,
curDeclaration.submoduleFallback,
resolveLocalNames,
allowExternallyHiddenAccess
);
}
return {
declaration,
isPrivate,
privatePyTypedImported,
privatePyTypedImporter,
};
}
alreadyVisited.push(curDeclaration);
}
}

View File

@ -5,7 +5,7 @@
* Author: Eric Traut
*
* Utility routines for summarizing and manipulating
* import statements in a python source file.
* import statements in a Python source file.
*/
import { CancellationToken } from 'vscode-languageserver';

View File

@ -7,7 +7,17 @@
*/
import { ParameterCategory } from '../parser/parseNodes';
import { ClassType, FunctionParameter, isClassInstance, isUnpackedClass } from './types';
import { isDunderName } from './symbolNameUtils';
import {
ClassType,
FunctionParameter,
FunctionType,
isClassInstance,
isUnpackedClass,
isVariadicTypeVar,
Type,
} from './types';
import { partiallySpecializeType } from './typeUtils';
export function isTypedKwargs(param: FunctionParameter): boolean {
return (
@ -18,3 +28,253 @@ export function isTypedKwargs(param: FunctionParameter): boolean {
!!param.type.details.typedDictEntries
);
}
export enum ParameterSource {
PositionOnly,
PositionOrKeyword,
KeywordOnly,
}
export interface VirtualParameterDetails {
param: FunctionParameter;
type: Type;
defaultArgType?: Type | undefined;
index: number;
source: ParameterSource;
}
export interface ParameterListDetails {
// Virtual parameter list that refers to original parameters
params: VirtualParameterDetails[];
// Counts of virtual parameters
positionOnlyParamCount: number;
positionParamCount: number;
// Indexes into virtual parameter list
kwargsIndex?: number;
argsIndex?: number;
firstKeywordOnlyIndex?: number;
firstPositionOrKeywordIndex: number;
// Other information
hasUnpackedVariadicTypeVar: boolean;
hasUnpackedTypedDict: boolean;
}
// Examines the input parameters within a function signature and creates a
// "virtual list" of parameters, stripping out any markers and expanding
// any *args with unpacked tuples.
export function getParameterListDetails(type: FunctionType): ParameterListDetails {
const result: ParameterListDetails = {
firstPositionOrKeywordIndex: 0,
positionParamCount: 0,
positionOnlyParamCount: 0,
params: [],
hasUnpackedVariadicTypeVar: false,
hasUnpackedTypedDict: false,
};
let positionOnlyIndex = type.details.parameters.findIndex(
(p) => p.category === ParameterCategory.Simple && !p.name
);
// Handle the old (pre Python 3.8) way of specifying positional-only
// parameters by naming them with "__".
if (positionOnlyIndex < 0) {
for (let i = 0; i < type.details.parameters.length; i++) {
const p = type.details.parameters[i];
if (p.category !== ParameterCategory.Simple) {
break;
}
if (!p.name) {
break;
}
if (isDunderName(p.name) || !p.name.startsWith('__')) {
break;
}
positionOnlyIndex = i + 1;
}
}
if (positionOnlyIndex >= 0) {
result.firstPositionOrKeywordIndex = positionOnlyIndex;
}
for (let i = 0; i < positionOnlyIndex; i++) {
if (type.details.parameters[i].hasDefault) {
break;
}
result.positionOnlyParamCount++;
}
let sawKeywordOnlySeparator = false;
const addVirtualParameter = (
param: FunctionParameter,
index: number,
typeOverride?: Type,
defaultArgTypeOverride?: Type,
sourceOverride?: ParameterSource
) => {
if (param.name) {
let source: ParameterSource;
if (sourceOverride !== undefined) {
source = sourceOverride;
} else if (param.category === ParameterCategory.VarArgList) {
source = ParameterSource.PositionOnly;
} else if (sawKeywordOnlySeparator) {
source = ParameterSource.KeywordOnly;
} else if (positionOnlyIndex >= 0 && index < positionOnlyIndex) {
source = ParameterSource.PositionOnly;
} else {
source = ParameterSource.PositionOrKeyword;
}
result.params.push({
param,
index,
type: typeOverride ?? FunctionType.getEffectiveParameterType(type, index),
defaultArgType: defaultArgTypeOverride,
source,
});
}
};
type.details.parameters.forEach((param, index) => {
if (param.category === ParameterCategory.VarArgList) {
// If this is an unpacked tuple, expand the entries.
const paramType = FunctionType.getEffectiveParameterType(type, index);
if (param.name && isUnpackedClass(paramType) && paramType.tupleTypeArguments) {
const addToPositionalOnly = index < result.positionOnlyParamCount;
paramType.tupleTypeArguments.forEach((tupleArg, tupleIndex) => {
const category =
isVariadicTypeVar(tupleArg.type) || tupleArg.isUnbounded
? ParameterCategory.VarArgList
: ParameterCategory.Simple;
if (category === ParameterCategory.VarArgList) {
result.argsIndex = result.params.length;
}
if (isVariadicTypeVar(param.type)) {
result.hasUnpackedVariadicTypeVar = true;
}
addVirtualParameter(
{
category,
name: `${param.name}[${tupleIndex.toString()}]`,
isNameSynthesized: true,
type: tupleArg.type,
hasDeclaredType: true,
},
index,
tupleArg.type,
/* defaultArgTypeOverride */ undefined,
ParameterSource.PositionOnly
);
if (category === ParameterCategory.Simple) {
result.positionParamCount++;
}
if (tupleIndex > 0 && addToPositionalOnly) {
result.positionOnlyParamCount++;
}
});
// Normally, a VarArgList parameter (either named or as an unnamed separator)
// would signify the start of keyword-only parameters. However, we can construct
// callable signatures that defy this rule by using Callable and TypeVarTuples
// or unpacked tuples.
if (!sawKeywordOnlySeparator && (positionOnlyIndex < 0 || index >= positionOnlyIndex)) {
result.firstKeywordOnlyIndex = result.params.length;
sawKeywordOnlySeparator = true;
}
} else {
if (param.name && result.argsIndex === undefined) {
result.argsIndex = result.params.length;
if (isVariadicTypeVar(param.type)) {
result.hasUnpackedVariadicTypeVar = true;
}
}
// Normally, a VarArgList parameter (either named or as an unnamed separator)
// would signify the start of keyword-only parameters. However, we can construct
// callable signatures that defy this rule by using Callable and TypeVarTuples
// or unpacked tuples.
if (!sawKeywordOnlySeparator && (positionOnlyIndex < 0 || index >= positionOnlyIndex)) {
result.firstKeywordOnlyIndex = result.params.length;
if (param.name) {
result.firstKeywordOnlyIndex++;
}
sawKeywordOnlySeparator = true;
}
addVirtualParameter(param, index);
}
} else if (param.category === ParameterCategory.VarArgDictionary) {
sawKeywordOnlySeparator = true;
const paramType = FunctionType.getEffectiveParameterType(type, index);
// Is this an unpacked TypedDict? If so, expand the entries.
if (isClassInstance(paramType) && isUnpackedClass(paramType) && paramType.details.typedDictEntries) {
if (result.firstKeywordOnlyIndex === undefined) {
result.firstKeywordOnlyIndex = result.params.length;
}
const typedDictType = paramType;
paramType.details.typedDictEntries.forEach((entry, name) => {
const specializedParamType = partiallySpecializeType(entry.valueType, typedDictType);
addVirtualParameter(
{
category: ParameterCategory.Simple,
name,
type: specializedParamType,
hasDeclaredType: true,
hasDefault: !entry.isRequired,
},
index,
specializedParamType
);
});
result.hasUnpackedTypedDict = true;
} else if (param.name) {
if (result.kwargsIndex === undefined) {
result.kwargsIndex = result.params.length;
}
if (result.firstKeywordOnlyIndex === undefined) {
result.firstKeywordOnlyIndex = result.params.length;
}
addVirtualParameter(param, index);
}
} else if (param.category === ParameterCategory.Simple) {
if (param.name && !sawKeywordOnlySeparator) {
result.positionParamCount++;
}
addVirtualParameter(
param,
index,
/* typeOverride */ undefined,
type.specializedTypes?.parameterDefaultArgs
? type.specializedTypes?.parameterDefaultArgs[index]
: undefined
);
}
});
return result;
}

View File

@ -4,7 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Utility routines used to resolve various paths in python.
* Utility routines used to resolve various paths in Python.
*/
import { ConfigOptions } from '../common/configOptions';

View File

@ -41,8 +41,8 @@ export function getRegionComments(parseResults: ParseResults): RegionComment[] {
// A comment starting with "region" or "endregion" is only treated as a region/endregion
// if it is not followed by an identifier character.
const StartRegionRegx = /^\s*region\b/;
const EndRegionRegex = /^\s*endregion\b/;
const StartRegionRegEx = /^\s*region\b/;
const EndRegionRegEx = /^\s*endregion\b/;
function getRegionCommentType(comment: Comment, parseResults: ParseResults): RegionCommentType | undefined {
const hashOffset = comment.start - 1;
@ -58,8 +58,8 @@ function getRegionCommentType(comment: Comment, parseResults: ParseResults): Reg
}
}
const startRegionMatch = StartRegionRegx.exec(comment.value);
const endRegionMatch = EndRegionRegex.exec(comment.value);
const startRegionMatch = StartRegionRegEx.exec(comment.value);
const endRegionMatch = EndRegionRegEx.exec(comment.value);
if (startRegionMatch) {
return RegionCommentType.Region;

View File

@ -4,7 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* A persistent service that is able to analyze a collection of
* A service that is able to analyze a collection of
* Python files.
*/

View File

@ -4,7 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Class that represents a single python source file.
* Class that represents a single Python source or stub file.
*/
import {

View File

@ -3,7 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Collection of functions that operate on SourceFileInfo objects.
* Functions that operate on SourceFileInfo objects.
*/
import { SourceFileInfo } from './program';

View File

@ -3,7 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Logic that maps a (.pyi) stub to its (.py) implementation source file.
* Logic that maps a ".pyi" stub to its ".py" source file.
*/
import { CancellationToken } from 'vscode-jsonrpc';

View File

@ -12,6 +12,22 @@ class NumberReference {
value = 0;
}
// Builds an array of imports from the 'from' to the 'to' entry where 'from'
// is on the front of the array and the item just before 'to' is on the
// back of the array.
export function buildImportTree(
to: string,
from: string,
next: (from: string) => string[],
token: CancellationToken
): string[] {
const totalCountRef = new NumberReference();
const results = _buildImportTreeImpl(to, from, next, [], totalCountRef, token);
// Result should always have the 'from' node in it.
return results.length > 0 ? results : [from];
}
function _buildImportTreeImpl(
to: string,
from: string,
@ -29,47 +45,23 @@ function _buildImportTreeImpl(
if (from === to) {
// At the top, previous should have our way into this recursion.
return previous.length ? previous : [from];
} else if (previous.length > 1 && previous.find((s) => s === from)) {
}
if (previous.length > 1 && previous.find((s) => s === from)) {
// Fail the search, we're stuck in a loop.
return [];
} else {
const nextEntries = next(from);
for (let i = 0; i < nextEntries.length && !token.isCancellationRequested; i++) {
// Do a search through the next level to get to the 'to' entry.
const subentries = _buildImportTreeImpl(
to,
nextEntries[i],
next,
[...previous, from],
totalSearched,
token
);
if (subentries.length > 0) {
return subentries;
}
}
const nextEntries = next(from);
for (let i = 0; i < nextEntries.length && !token.isCancellationRequested; i++) {
// Do a search through the next level to get to the 'to' entry.
const subentries = _buildImportTreeImpl(to, nextEntries[i], next, [...previous, from], totalSearched, token);
if (subentries.length > 0) {
return subentries;
}
}
// Search failed on this tree, fail so we can exit recursion.
// Search failed on this tree. Fail so we can exit recursion.
return [];
}
/**
* Builds an array of imports from the 'from' to the 'to' entry where 'from' is on the front of the array and
* the item just before 'to' is on the back of the array
* @param to
* @param from
* @param next
* @returns
*/
export function buildImportTree(
to: string,
from: string,
next: (from: string) => string[],
token: CancellationToken
): string[] {
const totalCountRef = new NumberReference();
const results = _buildImportTreeImpl(to, from, next, [], totalCountRef, token);
// Result should always have the 'from' node in it.
return results.length > 0 ? results : [from];
}

View File

@ -4,8 +4,8 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Collection of static methods that operate on expressions
* (parse node trees).
* Functions that operate on expressions (parse node trees)
* whose values can be evaluated statically.
*/
import { ExecutionEnvironment, PythonPlatform } from '../common/configOptions';

View File

@ -4,7 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Collection of functions that operate on Symbol objects.
* Functions that operate on Symbol objects.
*/
import { Declaration, DeclarationType } from './declaration';

View File

@ -1,5 +1,8 @@
/*
* testWalker.ts
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
* Author: Eric Traut
*
* Walks a parse tree to validate internal consistency and completeness.
*/

View File

@ -3,7 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Converts various types into a string representation.
* Converts various types into string representations.
*/
import { isNumber, isString } from '../common/core';

View File

@ -82,7 +82,6 @@ import {
} from '../parser/parseNodes';
import { ParseOptions, Parser } from '../parser/parser';
import { KeywordType, OperatorType, StringTokenFlags } from '../parser/tokenizerTypes';
import * as DeclarationUtils from './aliasDeclarationUtils';
import { AnalyzerFileInfo, ImportLookup, isAnnotationEvaluationPostponed } from './analyzerFileInfo';
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
import { CodeFlowAnalyzer, FlowNodeTypeOptions, FlowNodeTypeResult, getCodeFlowEngine } from './codeFlowEngine';
@ -114,6 +113,8 @@ import {
createSynthesizedAliasDeclaration,
getDeclarationsWithUsesLocalNameRemoved,
getNameNodeForDeclaration,
resolveAliasDeclaration as resolveAliasDeclarationUtil,
ResolvedAliasInfo,
} from './declarationUtils';
import {
createEnumType,
@ -125,6 +126,12 @@ import {
} from './enums';
import { applyFunctionTransform } from './functionTransform';
import { createNamedTupleType } from './namedTuples';
import {
getParameterListDetails,
ParameterListDetails,
ParameterSource,
VirtualParameterDetails,
} from './parameterUtils';
import * as ParseTreeUtils from './parseTreeUtils';
import { assignTypeToPatternTargets, checkForUnusedPattern, narrowTypeBasedOnPattern } from './patternMatching';
import {
@ -257,7 +264,6 @@ import {
getGeneratorTypeArgs,
getGeneratorYieldType,
getLiteralTypeClassName,
getParameterListDetails,
getSpecializedTupleType,
getTypeCondition,
getTypeVarArgumentsRecursive,
@ -285,8 +291,6 @@ import {
lookUpObjectMember,
makeInferenceContext,
mapSubtypes,
ParameterListDetails,
ParameterSource,
partiallySpecializeType,
preserveUnknown,
removeParamSpecVariadicsFromFunction,
@ -301,7 +305,6 @@ import {
synthesizeTypeVarForSelfCls,
transformPossibleRecursiveTypeAlias,
validateTypeVarDefault,
VirtualParameterDetails,
} from './typeUtils';
import { TypeVarContext, TypeVarSignatureContext } from './typeVarContext';
@ -21166,25 +21169,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
resolveLocalNames: boolean,
allowExternallyHiddenAccess = false
): Declaration | undefined {
return DeclarationUtils.resolveAliasDeclaration(
importLookup,
declaration,
resolveLocalNames,
allowExternallyHiddenAccess
)?.declaration;
return resolveAliasDeclarationUtil(importLookup, declaration, resolveLocalNames, allowExternallyHiddenAccess)
?.declaration;
}
function resolveAliasDeclarationWithInfo(
declaration: Declaration,
resolveLocalNames: boolean,
allowExternallyHiddenAccess = false
): DeclarationUtils.ResolvedAliasInfo | undefined {
return DeclarationUtils.resolveAliasDeclaration(
importLookup,
declaration,
resolveLocalNames,
allowExternallyHiddenAccess
);
): ResolvedAliasInfo | undefined {
return resolveAliasDeclarationUtil(importLookup, declaration, resolveLocalNames, allowExternallyHiddenAccess);
}
// Returns the type of the symbol. If the type is explicitly declared, that type

View File

@ -5,7 +5,6 @@
* Author: Eric Traut
*
* Abstract interface and other helper types for type evaluator module.
*
*/
import { CancellationToken } from 'vscode-languageserver-protocol';
@ -30,10 +29,10 @@ import {
RaiseNode,
StringNode,
} from '../parser/parseNodes';
import * as DeclarationUtils from './aliasDeclarationUtils';
import { AnalyzerFileInfo } from './analyzerFileInfo';
import { CodeFlowReferenceExpressionNode, FlowNode } from './codeFlowTypes';
import { Declaration } from './declaration';
import * as DeclarationUtils from './declarationUtils';
import { SymbolWithScope } from './scope';
import { Symbol } from './symbol';
import { PrintTypeFlags } from './typePrinter';

View File

@ -5,9 +5,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* This wraps real type evaluator to track performance information such
* as which type inferring takes most of time, what files are read most of times
* and etc.
* Wraps type evaluator to track performance of internal calls.
*/
import { LogLevel } from '../common/console';
@ -26,8 +24,8 @@ export function createTypeEvaluatorWithTracker(
printer?: TracePrinter
) {
function wrapWithLogger<T extends (...args: any[]) => any>(func: T): (...args: Parameters<T>) => ReturnType<T> {
// Only wrap the function if told to do so and the log level is high enough for it
// to actually log something.
// Wrap the function only if told to do so and the log level is high
// enough for it to log something.
if (evaluatorOptions.logCalls && logger.logLevel === LogLevel.Log) {
return (...args: Parameters<T>): ReturnType<T> => {
return logger.log(

View File

@ -4,7 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Collection of functions that operate on Type objects.
* Functions that operate on Type objects.
*/
import { appendArray } from '../common/collectionUtils';
@ -12,7 +12,6 @@ import { assert } from '../common/debug';
import { ParameterCategory } from '../parser/parseNodes';
import { DeclarationType } from './declaration';
import { Symbol, SymbolFlags, SymbolTable } from './symbol';
import { isDunderName } from './symbolNameUtils';
import { isTypedDictMemberAccessedThroughIndex } from './symbolUtils';
import {
AnyType,
@ -37,7 +36,6 @@ import {
isUnbound,
isUnion,
isUnknown,
isUnpackedClass,
isUnpackedVariadicTypeVar,
isVariadicTypeVar,
maxTypeRecursionCount,
@ -191,39 +189,6 @@ export const enum AssignTypeFlags {
PopulatingExpectedType = 1 << 10,
}
export enum ParameterSource {
PositionOnly,
PositionOrKeyword,
KeywordOnly,
}
export interface VirtualParameterDetails {
param: FunctionParameter;
type: Type;
defaultArgType?: Type | undefined;
index: number;
source: ParameterSource;
}
export interface ParameterListDetails {
// Virtual parameter list that refers to original parameters
params: VirtualParameterDetails[];
// Counts of virtual parameters
positionOnlyParamCount: number;
positionParamCount: number;
// Indexes into virtual parameter list
kwargsIndex?: number;
argsIndex?: number;
firstKeywordOnlyIndex?: number;
firstPositionOrKeywordIndex: number;
// Other information
hasUnpackedVariadicTypeVar: boolean;
hasUnpackedTypedDict: boolean;
}
export interface ApplyTypeVarOptions {
unknownIfNotFound?: boolean;
useUnknownOverDefault?: boolean;
@ -238,223 +203,6 @@ export interface InferenceContext {
typeVarContext?: TypeVarContext;
}
// Examines the input parameters within a function signature and creates a
// "virtual list" of parameters, stripping out any markers and expanding
// any *args with unpacked tuples.
export function getParameterListDetails(type: FunctionType): ParameterListDetails {
const result: ParameterListDetails = {
firstPositionOrKeywordIndex: 0,
positionParamCount: 0,
positionOnlyParamCount: 0,
params: [],
hasUnpackedVariadicTypeVar: false,
hasUnpackedTypedDict: false,
};
let positionOnlyIndex = type.details.parameters.findIndex(
(p) => p.category === ParameterCategory.Simple && !p.name
);
// Handle the old (pre Python 3.8) way of specifying positional-only
// parameters by naming them with "__".
if (positionOnlyIndex < 0) {
for (let i = 0; i < type.details.parameters.length; i++) {
const p = type.details.parameters[i];
if (p.category !== ParameterCategory.Simple) {
break;
}
if (!p.name) {
break;
}
if (isDunderName(p.name) || !p.name.startsWith('__')) {
break;
}
positionOnlyIndex = i + 1;
}
}
if (positionOnlyIndex >= 0) {
result.firstPositionOrKeywordIndex = positionOnlyIndex;
}
for (let i = 0; i < positionOnlyIndex; i++) {
if (type.details.parameters[i].hasDefault) {
break;
}
result.positionOnlyParamCount++;
}
let sawKeywordOnlySeparator = false;
const addVirtualParameter = (
param: FunctionParameter,
index: number,
typeOverride?: Type,
defaultArgTypeOverride?: Type,
sourceOverride?: ParameterSource
) => {
if (param.name) {
let source: ParameterSource;
if (sourceOverride !== undefined) {
source = sourceOverride;
} else if (param.category === ParameterCategory.VarArgList) {
source = ParameterSource.PositionOnly;
} else if (sawKeywordOnlySeparator) {
source = ParameterSource.KeywordOnly;
} else if (positionOnlyIndex >= 0 && index < positionOnlyIndex) {
source = ParameterSource.PositionOnly;
} else {
source = ParameterSource.PositionOrKeyword;
}
result.params.push({
param,
index,
type: typeOverride ?? FunctionType.getEffectiveParameterType(type, index),
defaultArgType: defaultArgTypeOverride,
source,
});
}
};
type.details.parameters.forEach((param, index) => {
if (param.category === ParameterCategory.VarArgList) {
// If this is an unpacked tuple, expand the entries.
const paramType = FunctionType.getEffectiveParameterType(type, index);
if (param.name && isUnpackedClass(paramType) && paramType.tupleTypeArguments) {
const addToPositionalOnly = index < result.positionOnlyParamCount;
paramType.tupleTypeArguments.forEach((tupleArg, tupleIndex) => {
const category =
isVariadicTypeVar(tupleArg.type) || tupleArg.isUnbounded
? ParameterCategory.VarArgList
: ParameterCategory.Simple;
if (category === ParameterCategory.VarArgList) {
result.argsIndex = result.params.length;
}
if (isVariadicTypeVar(param.type)) {
result.hasUnpackedVariadicTypeVar = true;
}
addVirtualParameter(
{
category,
name: `${param.name}[${tupleIndex.toString()}]`,
isNameSynthesized: true,
type: tupleArg.type,
hasDeclaredType: true,
},
index,
tupleArg.type,
/* defaultArgTypeOverride */ undefined,
ParameterSource.PositionOnly
);
if (category === ParameterCategory.Simple) {
result.positionParamCount++;
}
if (tupleIndex > 0 && addToPositionalOnly) {
result.positionOnlyParamCount++;
}
});
// Normally, a VarArgList parameter (either named or as an unnamed separator)
// would signify the start of keyword-only parameters. However, we can construct
// callable signatures that defy this rule by using Callable and TypeVarTuples
// or unpacked tuples.
if (!sawKeywordOnlySeparator && (positionOnlyIndex < 0 || index >= positionOnlyIndex)) {
result.firstKeywordOnlyIndex = result.params.length;
sawKeywordOnlySeparator = true;
}
} else {
if (param.name && result.argsIndex === undefined) {
result.argsIndex = result.params.length;
if (isVariadicTypeVar(param.type)) {
result.hasUnpackedVariadicTypeVar = true;
}
}
// Normally, a VarArgList parameter (either named or as an unnamed separator)
// would signify the start of keyword-only parameters. However, we can construct
// callable signatures that defy this rule by using Callable and TypeVarTuples
// or unpacked tuples.
if (!sawKeywordOnlySeparator && (positionOnlyIndex < 0 || index >= positionOnlyIndex)) {
result.firstKeywordOnlyIndex = result.params.length;
if (param.name) {
result.firstKeywordOnlyIndex++;
}
sawKeywordOnlySeparator = true;
}
addVirtualParameter(param, index);
}
} else if (param.category === ParameterCategory.VarArgDictionary) {
sawKeywordOnlySeparator = true;
const paramType = FunctionType.getEffectiveParameterType(type, index);
// Is this an unpacked TypedDict? If so, expand the entries.
if (isClassInstance(paramType) && isUnpackedClass(paramType) && paramType.details.typedDictEntries) {
if (result.firstKeywordOnlyIndex === undefined) {
result.firstKeywordOnlyIndex = result.params.length;
}
const typedDictType = paramType;
paramType.details.typedDictEntries.forEach((entry, name) => {
const specializedParamType = partiallySpecializeType(entry.valueType, typedDictType);
addVirtualParameter(
{
category: ParameterCategory.Simple,
name,
type: specializedParamType,
hasDeclaredType: true,
hasDefault: !entry.isRequired,
},
index,
specializedParamType
);
});
result.hasUnpackedTypedDict = true;
} else if (param.name) {
if (result.kwargsIndex === undefined) {
result.kwargsIndex = result.params.length;
}
if (result.firstKeywordOnlyIndex === undefined) {
result.firstKeywordOnlyIndex = result.params.length;
}
addVirtualParameter(param, index);
}
} else if (param.category === ParameterCategory.Simple) {
if (param.name && !sawKeywordOnlySeparator) {
result.positionParamCount++;
}
addVirtualParameter(
param,
index,
/* typeOverride */ undefined,
type.specializedTypes?.parameterDefaultArgs
? type.specializedTypes?.parameterDefaultArgs[index]
: undefined
);
}
});
return result;
}
export function isOptionalType(type: Type): boolean {
if (isUnion(type)) {
return findSubtype(type, (subtype) => isNoneInstance(subtype)) !== undefined;

View File

@ -4,7 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Class that represents errors and warnings.
* Class that collects and deduplicates diagnostics.
*/
import { appendArray } from './collectionUtils';

View File

@ -1,3 +1,11 @@
/*
* envVarUtils.ts
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Utils functions that handles environment variables.
*/
import * as os from 'os';
import {
@ -7,14 +15,6 @@ import {
hasTrailingDirectorySeparator,
} from './pathUtils';
/*
* envVarUtils.ts
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Utils functions that handles environment variables.
*/
// Expands certain predefined variables supported within VS Code settings.
// Ideally, VS Code would provide an API for doing this expansion, but
// it doesn't. We'll handle the most common variables here as a convenience.

View File

@ -3,7 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Helper methods relating to file based cancellation.
* Helper methods relating to file-based cancellation.
*/
import * as fs from 'fs';

View File

@ -3,7 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Provides accesses to the host the language service runs on
* Provides access to the host environment the language service is running on.
*/
import { PythonPathResult } from '../analyzer/pythonPathUtils';

View File

@ -2,7 +2,7 @@
* lspUtils.ts
* Copyright (c) Microsoft Corporation.
*
* Helper functions around Language Server Protocol (LSP).
* Helper functions related to the Language Server Protocol (LSP).
*/
import { LSPAny } from 'vscode-languageserver';

View File

@ -1,7 +1,10 @@
/*
* progressReporter.ts
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
* Author: Eric Traut
*
* Implements progress reporter.
* Implements a mechanism for reporting progress in a language server client.
*/
export interface ProgressReporter {

View File

@ -4,8 +4,7 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Types and functions that relate to the Python language version
* and features within them.
* Types and functions that relate to the Python language version.
*/
export enum PythonVersion {

View File

@ -1,7 +1,7 @@
/*
* realFileSystem.ts
*
* Collection of helper functions that require real fs access.
* Helper functions that require real filesystem access.
*/
import { FakeFS, NativePath, PortablePath, PosixFS, ppath, VirtualFS, ZipFS, ZipOpenFS } from '@yarnpkg/fslib';

View File

@ -3,7 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Track text edits per files.
* Tracks text edits on a per-file basis.
*/
import { CancellationToken } from 'vscode-languageserver';

View File

@ -3,7 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Convert Pyright's FileEditActions to LanguageServer's WorkspaceEdits.
* Convert pyright's FileEditActions to LanguageServer's WorkspaceEdits.
*/
import {

View File

@ -7,6 +7,7 @@
* Runs the analyzer service of a given workspace service instance
* with a specified set of options.
*/
import { isPythonBinary } from '../analyzer/pythonPathUtils';
import { AnalyzerService, getNextServiceId } from '../analyzer/service';
import { CommandLineOptions } from '../common/commandLineOptions';

View File

@ -3,6 +3,7 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Logic for performing auto-import completions.
*/
import { CancellationToken, CompletionItemKind, SymbolKind } from 'vscode-languageserver';

View File

@ -5,6 +5,7 @@
*
* Helper functions for providing completions
*/
import { InsertTextFormat, MarkupContent, MarkupKind, TextEdit } from 'vscode-languageserver-types';
import { Declaration, DeclarationType } from '../analyzer/declaration';

View File

@ -18,11 +18,10 @@ import {
} from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { resolveAliasDeclaration } from '../analyzer/aliasDeclarationUtils';
import { AnalyzerFileInfo, ImportLookup } from '../analyzer/analyzerFileInfo';
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
import { AliasDeclaration, Declaration, DeclarationType } from '../analyzer/declaration';
import { getNameFromDeclaration } from '../analyzer/declarationUtils';
import { getNameFromDeclaration, resolveAliasDeclaration } from '../analyzer/declarationUtils';
import { getLastTypedDeclaredForSymbol, isVisibleExternally } from '../analyzer/symbolUtils';
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
import { isMaybeDescriptorInstance } from '../analyzer/typeUtils';

View File

@ -5,7 +5,7 @@
* Author: Eric Traut
*
* Provides code that sorts and formats import statements within a
* python source file.
* Python source file.
*/
import { CancellationToken } from 'vscode-languageserver';

View File

@ -3,7 +3,8 @@
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*
* Provides code to get indentation and re-indent code to the given indentation.
* Provides code to get indentation and re-indent code for the
* given indentation.
*/
import Char from 'typescript-char';

View File

@ -4,8 +4,8 @@
* Licensed under the MIT license.
* Author: Eric Traut
*
* Tooltip helper methods that can be shared between multiple language server features such as
* hover and completion tooltip.
* Helper functions for formatting text that can appear in hover text,
* completion suggestions, etc.
*/
import { Declaration, DeclarationType, VariableDeclaration } from '../analyzer/declaration';