diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java b/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java index d9f999d3af74..f7cf6414d8e2 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/stubs/PyQualifiedNameCompletionMatcher.java @@ -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 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))) { diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/python_stubs/mypackage/__init__.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/python_stubs/mypackage/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/python_stubs/mypackage/_impl.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/python_stubs/mypackage/_impl.py new file mode 100644 index 000000000000..2609413403d8 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/python_stubs/mypackage/_impl.py @@ -0,0 +1,6 @@ +def func_exported(): + pass + + +def func(): + pass diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/site-packages/mypackage/__init__.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/site-packages/mypackage/__init__.py new file mode 100644 index 000000000000..3b6f5772854d --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/site-packages/mypackage/__init__.py @@ -0,0 +1 @@ +from ._impl import func_exported diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/src/main.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/src/main.py new file mode 100644 index 000000000000..295a59659c49 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/src/main.py @@ -0,0 +1 @@ +mypa.fu \ No newline at end of file diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/src/mypackage_util/_impl.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/src/mypackage_util/_impl.py new file mode 100644 index 000000000000..b9bfa6f12339 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalSkeletonsExcludedUnlessExported/src/mypackage_util/_impl.py @@ -0,0 +1,2 @@ +def func(): + pass diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/__init__.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/__init__.py new file mode 100644 index 000000000000..3b6f5772854d --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/__init__.py @@ -0,0 +1 @@ +from ._impl import func_exported diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_impl.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_impl.py new file mode 100644 index 000000000000..2609413403d8 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_impl.py @@ -0,0 +1,6 @@ +def func_exported(): + pass + + +def func(): + pass diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_vendor/__init__.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_vendor/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_vendor/lib.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_vendor/lib.py new file mode 100644 index 000000000000..b9bfa6f12339 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/site-packages/mypackage/_vendor/lib.py @@ -0,0 +1,2 @@ +def func(): + pass diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/src/main.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/src/main.py new file mode 100644 index 000000000000..d140b0a9b7e3 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/src/main.py @@ -0,0 +1 @@ +mypack.fun diff --git a/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/src/mypackage_util/_impl.py b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/src/mypackage_util/_impl.py new file mode 100644 index 000000000000..b9bfa6f12339 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/VariantsFromInternalThirdPartyModulesExcludedUnlessExported/src/mypackage_util/_impl.py @@ -0,0 +1,2 @@ +def func(): + pass diff --git a/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java b/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java index 68d10ce71342..eba87c58750b 100644 --- a/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java @@ -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 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 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"));