diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt index 8b48f640fe35..c6291d97a843 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/completion/PyUnresolvedModuleAttributeCompletionContributor.kt @@ -9,10 +9,7 @@ import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.util.Key import com.intellij.patterns.PatternCondition import com.intellij.patterns.PlatformPatterns.psiElement -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFileSystemItem -import com.intellij.psi.PsiNamedElement +import com.intellij.psi.* import com.intellij.psi.util.QualifiedName import com.intellij.util.ProcessingContext import com.intellij.util.Processor @@ -28,6 +25,7 @@ import com.jetbrains.python.psi.stubs.PyModuleNameIndex import com.jetbrains.python.psi.stubs.PyQualifiedNameCompletionMatcher import com.jetbrains.python.psi.stubs.PyQualifiedNameCompletionMatcher.QualifiedNameMatcher import com.jetbrains.python.psi.types.PyModuleType +import com.jetbrains.python.psi.types.TypeEvalContext class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() { @@ -106,6 +104,7 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() if (isOtherReferenceQualifier) return val project = parameters.position.project + val originalFile = parameters.originalFile val attribute = result.prefixMatcher.prefix val qualifier = context.get(REFERENCE_QUALIFIER) val suggestedQualifiedNames = HashSet() @@ -116,6 +115,7 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() val packageName = if (packageNameForAlias != null) QualifiedName.fromDottedString(packageNameForAlias) else qualifier val resultMatchingCompleteReference = result.withPrefixMatcher(QualifiedNameMatcher(qualifiedName)) val scope = PySearchUtilBase.defaultSuggestionScope(parameters.originalFile) + val typeContext = TypeEvalContext.userInitiated(project, originalFile) val availableModules = PyModuleNameIndex.findByQualifiedName(packageName, project, scope) .asSequence() @@ -137,8 +137,12 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() .forEach { resultMatchingCompleteReference.addElement(it) } } - availableModules.flatMap { it.iterateNames() } - .filter { it.containingFile != null } + availableModules.asSequence() + .filterIsInstance() + .map { PyModuleType(it) } + .flatMap { it.getCompletionVariantsAsLookupElements(parameters.position, context, false, false, typeContext) } + .mapNotNull {it.psiElement } + .filterIsInstance() .filterNot { it is PsiFileSystemItem } .filterNot { it.name == null || it.name!!.startsWith('_') } .filter { attribute.isEmpty() || resultMatchingCompleteReference.prefixMatcher.prefixMatches("$qualifier.${it.name}") } @@ -158,13 +162,12 @@ class PyUnresolvedModuleAttributeCompletionContributor : CompletionContributor() return } PyQualifiedNameCompletionMatcher.processMatchingExportedNames( - qualifiedName, parameters.originalFile, scope, + qualifiedName, originalFile, scope, Processor { ProgressManager.checkCanceled() if (suggestedQualifiedNames.add(it.qualifiedName.toString())) { resultMatchingCompleteReference.addElement(LookupElementBuilder - .createWithSmartPointer(it.qualifiedName.toString(), - it.element) + .createWithSmartPointer(it.qualifiedName.toString(), it.element) .withIcon(it.element.getIcon(0)) .withInsertHandler(getInsertHandler(it.element, parameters.position))) } diff --git a/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/main.after.py b/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/main.after.py new file mode 100644 index 000000000000..ce5fe9063d48 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/main.after.py @@ -0,0 +1,3 @@ +import numpy as np + +np.arange() diff --git a/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/main.py b/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/main.py new file mode 100644 index 000000000000..b5ea5de7786c --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/main.py @@ -0,0 +1 @@ +np.a diff --git a/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/numpy/__init__.py b/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/numpy/__init__.py new file mode 100644 index 000000000000..f8eac879a851 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AliasAttributesNotLimitedByDunderAll/numpy/__init__.py @@ -0,0 +1,9 @@ +def max(): + pass + + +def arange(): + pass + + +__all__ = ['max'] diff --git a/python/testData/completion/notImportedQualifiedName/AttributesNotLimitedByDunderAll/main.py b/python/testData/completion/notImportedQualifiedName/AttributesNotLimitedByDunderAll/main.py new file mode 100644 index 000000000000..0bbecdd03ff6 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AttributesNotLimitedByDunderAll/main.py @@ -0,0 +1 @@ +mod. diff --git a/python/testData/completion/notImportedQualifiedName/AttributesNotLimitedByDunderAll/mod.py b/python/testData/completion/notImportedQualifiedName/AttributesNotLimitedByDunderAll/mod.py new file mode 100644 index 000000000000..5d546b38bfc6 --- /dev/null +++ b/python/testData/completion/notImportedQualifiedName/AttributesNotLimitedByDunderAll/mod.py @@ -0,0 +1,3 @@ +foo = 1 +bar = 2 +__all__ = ['bar'] diff --git a/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java b/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java index e6a43bd476e2..4d7250ea11ee 100644 --- a/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PyNotImportedQualifiedNameCompletionTest.java @@ -4,6 +4,7 @@ package com.jetbrains.python; import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.stubs.PyQualifiedNameCompletionMatcher.QualifiedNameMatcher; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -135,12 +136,27 @@ public class PyNotImportedQualifiedNameCompletionTest extends PyTestCase { doTestBasicCompletion(); } - private void doTestBasicCompletion() { - final String testName = getTestName(false); - myFixture.copyDirectoryToProject(testName, ""); + // PY-48219 + public void testAttributesNotLimitedByDunderAll() { + assertContainsElements(doBasicCompletion(), "mod.foo"); + } + + // PY-48219 + public void testAliasAttributesNotLimitedByDunderAll() { + doTestBasicCompletion(); + } + + @Nullable + private List doBasicCompletion() { + myFixture.copyDirectoryToProject(getTestName(false), ""); myFixture.configureByFile("main.py"); myFixture.completeBasic(); - myFixture.checkResultByFile(testName + "/main.after.py"); + return myFixture.getLookupElementStrings(); + } + + private void doTestBasicCompletion() { + doBasicCompletion(); + myFixture.checkResultByFile(getTestName(false) + "/main.after.py"); } @Override