PY-47281 Exclude names from internal modules of libraries from auto-importing

Namely, names such as "numpy.random._examples.numba.extending.numbacall" or
"numpy.testing._private.noseclasses.NumpyDoctest" should no longer be suggested,
unless they are also exported in a public package higher in the hierarchy.

It doesn't not affect definitions from internal modules that belong to project
sources, these are still offered in the lookup.

GitOrigin-RevId: 2be393f30bd7d9905a31bdbe8db101807c136617
This commit is contained in:
Mikhail Golubev
2021-02-19 16:19:51 +00:00
committed by intellij-monorepo-bot
parent df979257a6
commit 6f9013eb6b
13 changed files with 56 additions and 0 deletions
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.util.QualifiedName;
@@ -41,6 +42,7 @@ public class PyQualifiedNameCompletionMatcher {
QualifiedNameMatcher matcher = new QualifiedNameMatcher(qualifiedNamePattern);
StubIndex stubIndex = StubIndex.getInstance();
Project project = Objects.requireNonNull(scope.getProject());
PsiManager psiManager = PsiManager.getInstance(project);
GlobalSearchScope moduleMatchingScope = scope.intersectWith(new ModuleQualifiedNameMatchingScope(matcher, project));
Set<QualifiedName> alreadySuggestedAttributes = new HashSet<>();
@@ -66,6 +68,9 @@ public class PyQualifiedNameCompletionMatcher {
else {
importPath = moduleQualifiedName;
}
if (ContainerUtil.exists(importPath.getComponents(), c -> c.startsWith("_")) && !psiManager.isInProject(element)) {
return true;
}
QualifiedName attributeQualifiedName = importPath.append(attributeName);
if (alreadySuggestedAttributes.add(attributeQualifiedName)) {
if (!processor.process(new ExportedName(attributeQualifiedName, originallyTypedAlias, element))) {
@@ -28,6 +28,35 @@ public class PyNotImportedQualifiedNameCompletionTest extends PyTestCase {
assertContainsElements(variants, "bar.func", "bar.func1");
}
// PY-47281
public void testVariantsFromInternalThirdPartyModulesExcludedUnlessExported() {
runWithAdditionalClassEntryInSdkRoots(getTestName(false) + "/site-packages", () -> {
myFixture.copyDirectoryToProject(getTestName(false) + "/src", "");
myFixture.configureByFile("main.py");
myFixture.completeBasic();
List<String> variants = myFixture.getLookupElementStrings();
assertNotNull(variants);
assertDoesntContain(variants, "mypackage._impl.func", "mypackage._vendor.lib.func");
assertContainsElements(variants, "mypackage.func_exported", "mypackage_util._impl.func");
});
}
// PY-47281
public void testVariantsFromInternalSkeletonsExcludedUnlessExported() {
String testName = getTestName(false);
runWithAdditionalClassEntryInSdkRoots(testName + "/site-packages", () -> {
runWithAdditionalClassEntryInSdkRoots(testName + "/python_stubs", () -> {
myFixture.copyDirectoryToProject(testName + "/src", "");
myFixture.configureByFile("main.py");
myFixture.completeBasic();
List<String> variants = myFixture.getLookupElementStrings();
assertNotNull(variants);
assertDoesntContain(variants, "mypackage._impl.func");
assertContainsElements(variants, "mypackage.func_exported", "mypackage_util._impl.func");
});
});
}
public void testQualifiedNameMatcherTest() {
QualifiedNameMatcher matcher = new QualifiedNameMatcher(QualifiedName.fromDottedString("foo.bar.baz"));
assertTrue(matcher.prefixMatches("foo.bar.baz"));