diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index d1166d42a8f4..e73e74de4a1f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -2,6 +2,7 @@ package com.jetbrains.python.psi.types; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.extensions.Extensions; @@ -165,7 +166,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje @NotNull String name, @NotNull List importElements) { final VirtualFile moduleFile = moduleOrPackage.getVirtualFile(); - final PsiElement anchor = location != null ? location : moduleOrPackage; + final PsiElement anchor = location != null ? location.getContainingFile() : moduleOrPackage; if (moduleFile != null) { for (QualifiedName packageQName : QualifiedNameFinder.findImportableQNames(anchor, moduleOrPackage.getVirtualFile())) { final QualifiedName resolvingQName = packageQName.append(name); @@ -174,7 +175,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje if (qName.matchesPrefix(resolvingQName)) { final List submodules = ResolveImportUtil - .resolveChildren(moduleOrPackage, name, PyUtil.as(moduleOrPackage, PyFile.class), false, true, false, false); + .resolveChildren(moduleOrPackage, name, PyUtil.as(anchor, PyFile.class), false, true, false, false); if (!submodules.isEmpty()) { return ResolveResultList.asImportedResults(submodules, importElement); } @@ -408,39 +409,32 @@ public class PyModuleType implements PyType { // Modules don't descend from obje } @Nullable - public static List collectImportedSubmodules(@NotNull PsiFileSystemItem pyPackage, @NotNull PsiElement location) { - final PsiElement parentAnchor; - if (pyPackage instanceof PyFile && PyUtil.isPackage(((PyFile)pyPackage))) { - parentAnchor = ((PyFile)pyPackage).getContainingDirectory(); - } - else if (pyPackage instanceof PsiDirectory && PyUtil.isPackage(((PsiDirectory)pyPackage), location)) { - parentAnchor = pyPackage; - } - else { + private static List collectImportedSubmodules(@NotNull PsiFileSystemItem pyPackage, @NotNull PsiElement location) { + + if (!PyUtil.isPackage(pyPackage, location)) { return null; } - final ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(location); if (scopeOwner == null) { return Collections.emptyList(); } + + final List myQnames = QualifiedNameFinder.findImportableQNames(pyPackage, pyPackage.getVirtualFile()); final List result = new ArrayList<>(); - nextImportElement: + final Set seen = Sets.newHashSet(); for (PyImportElement importElement : getVisibleImports(scopeOwner)) { - PsiElement resolvedChild = PyUtil.turnInitIntoDir(importElement.resolve()); - if (resolvedChild == null || !PsiTreeUtil.isAncestor(parentAnchor, resolvedChild, true)) { - continue; - } - QualifiedName importedQName = importElement.getImportedQName(); - // Looking for strict child of parentAncestor - while (resolvedChild != null && resolvedChild.getParent() != parentAnchor) { - if (importedQName == null || importedQName.getComponentCount() <= 1) { - continue nextImportElement; + for (QualifiedName packageQName : myQnames) { + for (QualifiedName importedQname : getImportedQNames(importElement)) { + if (importedQname.matchesPrefix(packageQName) && importedQname.getComponentCount() > packageQName.getComponentCount()) { + final String directChild = importedQname.removeHead(packageQName.getComponentCount()).getFirstComponent(); + if (directChild != null && seen.add(directChild)) { + final List results = + ResolveImportUtil.resolveChildren(pyPackage, directChild, location.getContainingFile(), true, true, false, false); + result.addAll(ResolveResultList.getElements(results)); + } + } } - importedQName = importedQName.removeTail(1); - resolvedChild = PyUtil.turnInitIntoDir(ResolveImportUtil.resolveImportElement(importElement, importedQName)); } - ContainerUtil.addIfNotNull(result, resolvedChild); } return result; } diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage2/a.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage2/a.py new file mode 100644 index 000000000000..cc8d7d6cb331 --- /dev/null +++ b/python/testData/completion/submoduleOfIndirectlyImportedPackage2/a.py @@ -0,0 +1,4 @@ +from pkg1.subpkg1 import m1 +import pkg1 + +pkg1. \ No newline at end of file diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage2/pkg1/__init__.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage2/pkg1/__init__.py new file mode 100644 index 000000000000..fe5433bef744 --- /dev/null +++ b/python/testData/completion/submoduleOfIndirectlyImportedPackage2/pkg1/__init__.py @@ -0,0 +1 @@ +VAR = 42 \ No newline at end of file diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage2/pkg1/subpkg1/__init__.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage2/pkg1/subpkg1/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage2/pkg1/subpkg1/m1.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage2/pkg1/subpkg1/m1.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 46676333ac04..7276f106b90e 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -793,6 +793,17 @@ public class PythonCompletionTest extends PyTestCase { assertSameElements(suggested, "VAR", "subpkg1"); } + //PY-28332 + public void testSubmoduleOfIndirectlyImportedPackage2() { + myFixture.copyDirectoryToProject(getTestName(true), ""); + myFixture.configureByFile("a.py"); + myFixture.completeBasic(); + final List suggested = myFixture.getLookupElementStrings(); + assertNotNull(suggested); + assertSameElements(suggested, "VAR", "subpkg1"); + } + + // PY-14519 public void testOsPath() { myFixture.copyDirectoryToProject(getTestName(true), "");