Skip to content

Commit

Permalink
Fixed bug in completion provider that resulted in an attempt to token…
Browse files Browse the repository at this point in the history
…ize and parse a binary (native) library file. This led to a crash in some cases. This addresses microsoft/pylance-release#5090.
  • Loading branch information
erictraut committed Nov 7, 2023
1 parent f8746ed commit e519173
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 15 deletions.
17 changes: 13 additions & 4 deletions packages/pyright-internal/src/analyzer/importResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ interface SupportedVersionRange {
}

const supportedNativeLibExtensions = ['.pyd', '.so', '.dylib'];
export const supportedSourceFileExtensions = ['.py', '.pyi'];
export const supportedFileExtensions = [...supportedSourceFileExtensions, ...supportedNativeLibExtensions];
const supportedSourceFileExtensions = ['.py', '.pyi'];
const supportedFileExtensions = [...supportedSourceFileExtensions, ...supportedNativeLibExtensions];

// Should we allow partial resolution for third-party packages? Some use tricks
// to populate their package namespaces, so we might be able to partially resolve
Expand Down Expand Up @@ -139,6 +139,16 @@ export class ImportResolver {
return this.serviceProvider.tryGet(ServiceKeys.partialStubs);
}

static isSupportedImportSourceFile(path: string) {
const fileExtension = getFileExtension(path).toLowerCase();
return supportedSourceFileExtensions.some((ext) => fileExtension === ext);
}

static isSupportedImportFile(path: string) {
const fileExtension = getFileExtension(path).toLowerCase();
return supportedFileExtensions.some((ext) => fileExtension === ext);
}

invalidateCache() {
this._cachedImportResults = new Map<string | undefined, CachedImportResults>();
this._cachedModuleNameResults = new Map<string, Map<string, ModuleImportInfo>>();
Expand Down Expand Up @@ -2396,10 +2406,9 @@ export class ImportResolver {
entries.files.forEach((file) => {
// Strip multi-dot extensions to handle file names like "foo.cpython-32m.so". We want
// to detect the ".so" but strip off the entire ".cpython-32m.so" extension.
const fileExtension = getFileExtension(file, /* multiDotExtension */ false).toLowerCase();
const fileWithoutExtension = stripFileExtension(file, /* multiDotExtension */ true);

if (supportedFileExtensions.some((ext) => ext === fileExtension)) {
if (ImportResolver.isSupportedImportFile(file)) {
if (fileWithoutExtension === '__init__') {
return;
}
Expand Down
11 changes: 2 additions & 9 deletions packages/pyright-internal/src/analyzer/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {
containsPath,
forEachAncestorDirectory,
getDirectoryPath,
getFileExtension,
getFileName,
getFileSpec,
getFileSystemEntries,
Expand All @@ -57,12 +56,7 @@ import {
BackgroundAnalysisProgramFactory,
InvalidatedReason,
} from './backgroundAnalysisProgram';
import {
ImportResolver,
ImportResolverFactory,
createImportedModuleDescriptor,
supportedSourceFileExtensions,
} from './importResolver';
import { ImportResolver, ImportResolverFactory, createImportedModuleDescriptor } from './importResolver';
import { MaxAnalysisTime, Program } from './program';
import { findPythonSearchPaths } from './pythonPathUtils';
import { IPythonMode } from './sourceFile';
Expand Down Expand Up @@ -1093,8 +1087,7 @@ export class AnalyzerService {

// Add the implicit import paths.
importResult.filteredImplicitImports.forEach((implicitImport) => {
const fileExtension = getFileExtension(implicitImport.path).toLowerCase();
if (supportedSourceFileExtensions.some((ext) => fileExtension === ext)) {
if (ImportResolver.isSupportedImportSourceFile(implicitImport.path)) {
filesToImport.push(implicitImport.path);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
} from '../analyzer/declaration';
import { isDefinedInFile } from '../analyzer/declarationUtils';
import { convertDocStringToMarkdown, convertDocStringToPlainText } from '../analyzer/docStringConversion';
import { ImportedModuleDescriptor } from '../analyzer/importResolver';
import { ImportedModuleDescriptor, ImportResolver } from '../analyzer/importResolver';
import { ImportResult } from '../analyzer/importResult';
import { isTypedKwargs } from '../analyzer/parameterUtils';
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
Expand Down Expand Up @@ -346,7 +346,10 @@ export class CompletionProvider {
return;
}

if (completionItemData.modulePath) {
if (
completionItemData.modulePath &&
ImportResolver.isSupportedImportSourceFile(completionItemData.modulePath)
) {
const documentation = getModuleDocStringFromPaths([completionItemData.modulePath], this.sourceMapper);
if (!documentation) {
return;
Expand Down

0 comments on commit e519173

Please sign in to comment.