diff --git a/.idea/modules.xml b/.idea/modules.xml
index dc3b64547d50..66832a09f40a 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -79,7 +79,6 @@
-
diff --git a/platform/platform-api/src/com/intellij/notification/Notification.java b/platform/platform-api/src/com/intellij/notification/Notification.java
index 7b3ecd67ca47..fc818e4e5239 100644
--- a/platform/platform-api/src/com/intellij/notification/Notification.java
+++ b/platform/platform-api/src/com/intellij/notification/Notification.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,19 +44,23 @@ public class Notification {
private Boolean myImportant;
private WeakReference myBalloonRef;
- public Notification(@NotNull final String groupDisplayId, @NotNull final String title, @NotNull final String content, @NotNull final NotificationType type) {
+ public Notification(@NotNull String groupDisplayId, @NotNull String title, @NotNull String content, @NotNull NotificationType type) {
this(groupDisplayId, title, content, type, null);
}
/**
* @param groupDisplayId this should be a human-readable, capitalized string like "Facet Detector".
* It will appear in "Notifications" configurable.
- * @param title notification title
- * @param content notification content
- * @param type notification type
- * @param listener notification lifecycle listener
+ * @param title notification title
+ * @param content notification content
+ * @param type notification type
+ * @param listener notification lifecycle listener
*/
- public Notification(@NotNull final String groupDisplayId, @NotNull final String title, @NotNull final String content, @NotNull final NotificationType type, @Nullable NotificationListener listener) {
+ public Notification(@NotNull String groupDisplayId,
+ @NotNull String title,
+ @NotNull String content,
+ @NotNull NotificationType type,
+ @Nullable NotificationListener listener) {
myGroupId = groupDisplayId;
myTitle = title;
myContent = content;
diff --git a/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java b/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java
index b4b88ee2f5a9..9034294e8740 100644
--- a/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java
+++ b/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2011 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java b/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java
index bdddb3cf129a..d89f1cba7024 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java
@@ -22,12 +22,14 @@ public enum PointInImport {
NONE,
/**
- * The reference is inside import and refers to a module
+ * The reference is inside import and it's either source qualifier in {@code from REFERENCE import ...}
+ * or imported name itself in {@code import REFERENCE}.
*/
AS_MODULE,
/**
- * The reference is inside import and refers to a name imported from a module
+ * The reference is inside import and refers to a name imported from a module, i.e. it's concrete imported name in
+ * import statement of form {@code from somewhere import REFERENCE}, not the optional alias part.
*/
AS_NAME
}
diff --git a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java
index b7323c87e6e5..9a584e0ffff4 100644
--- a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java
@@ -31,7 +31,6 @@ import com.intellij.psi.util.QualifiedName;
import com.intellij.ui.components.JBList;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
-import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.CheckBox;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
@@ -39,6 +38,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
+import com.jetbrains.python.psi.types.PyClassLikeType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -117,7 +117,7 @@ public class PyPep8NamingInspection extends PyInspection {
if (!LOWERCASE_REGEX.matcher(name).matches()) {
final ASTNode nameNode = function.getNameNode();
if (nameNode != null) {
- final List quickFixes = new SmartList(new PyRenameElementQuickFix());
+ final List quickFixes = Lists.newArrayList(new PyRenameElementQuickFix());
if (containingClass != null) {
quickFixes.add(new IgnoreBaseClassQuickFix(containingClass, myTypeEvalContext));
}
@@ -135,8 +135,8 @@ public class PyPep8NamingInspection extends PyInspection {
if (blackList.contains(pyClass.getQualifiedName())) {
return true;
}
- for (PyClass ancestor : pyClass.getAncestorClasses(myTypeEvalContext)) {
- if (blackList.contains(ancestor.getQualifiedName())) {
+ for (PyClassLikeType ancestor : pyClass.getAncestorTypes(myTypeEvalContext)) {
+ if (blackList.contains(ancestor.getClassQName())) {
return true;
}
}
diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java
index 47e66d75db5a..b7aa91031d5e 100644
--- a/python/src/com/jetbrains/python/psi/PyUtil.java
+++ b/python/src/com/jetbrains/python/psi/PyUtil.java
@@ -38,7 +38,6 @@ import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
@@ -77,6 +76,7 @@ import com.jetbrains.python.refactoring.classes.PyDependenciesComparator;
import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuperclassHelper;
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
import com.jetbrains.python.sdk.PythonSdkType;
+import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -94,8 +94,6 @@ import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
public class PyUtil {
- private static final Object[] EMPTY_OBJECTS = new Object[0];
-
private PyUtil() {
}
@@ -992,8 +990,9 @@ public class PyUtil {
} // don't touch non-dirs
}
+ @Contract("null -> null; !null -> !null")
@Nullable
- public static PsiElement turnInitIntoDir(PsiElement target) {
+ public static PsiElement turnInitIntoDir(@Nullable PsiElement target) {
if (target instanceof PyFile && isPackage((PsiFile)target)) {
return ((PsiFile)target).getContainingDirectory();
}
@@ -1230,23 +1229,6 @@ public class PyUtil {
return false;
}
- public static class UnderscoreFilter implements Condition {
- private int myAllowed; // how many starting underscores is allowed: 0 is none, 1 is only one, 2 is two and more.
-
- public UnderscoreFilter(int allowed) {
- myAllowed = allowed;
- }
-
- public boolean value(String name) {
- if (name == null) return false;
- if (name.length() < 1) return false; // empty strings make no sense
- int have_underscores = 0;
- if (name.charAt(0) == '_') have_underscores = 1;
- if (have_underscores != 0 && name.length() > 1 && name.charAt(1) == '_') have_underscores = 2;
- return myAllowed >= have_underscores;
- }
- }
-
@Nullable
public static String getKeywordArgumentString(PyCallExpression expr, String keyword) {
return PyPsiUtils.strValue(expr.getKeywordArgument(keyword));
diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java
index e2ea4a797317..4c6535cacd5a 100644
--- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java
+++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java
@@ -392,17 +392,16 @@ public class ResolveImportUtil {
/**
* @param element what we test (identifier, reference, import element, etc)
* @return the how the element relates to an enclosing import statement, if any
+ * @see com.jetbrains.python.psi.resolve.PointInImport
*/
+ @NotNull
public static PointInImport getPointInImport(@NotNull PsiElement element) {
- PsiElement parent = PsiTreeUtil.getNonStrictParentOfType(
- element,
- PyImportElement.class, PyFromImportStatement.class
- );
+ final PsiElement parent = PsiTreeUtil.getNonStrictParentOfType(element, PyImportElement.class, PyFromImportStatement.class);
if (parent instanceof PyFromImportStatement) {
return PointInImport.AS_MODULE; // from foo ...
}
if (parent instanceof PyImportElement) {
- PsiElement statement = parent.getParent();
+ final PsiElement statement = parent.getParent();
if (statement instanceof PyImportStatement) {
return PointInImport.AS_MODULE; // import foo,...
}
diff --git a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java
index bc26a1a87ba2..01bcf032bfa2 100644
--- a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java
+++ b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java
@@ -15,17 +15,20 @@
*/
package com.jetbrains.python.psi.types;
+import com.google.common.collect.Sets;
import com.intellij.codeInsight.lookup.LookupElement;
-import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
-import com.intellij.psi.util.QualifiedName;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
-import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
-import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
-import com.jetbrains.python.psi.*;
+import com.intellij.util.containers.ContainerUtil;
+import com.jetbrains.python.psi.AccessDirection;
+import com.jetbrains.python.psi.PyExpression;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyUtil;
import com.jetbrains.python.psi.impl.PyImportedModule;
+import com.jetbrains.python.psi.resolve.PointInImport;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
@@ -35,6 +38,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* @author yole
@@ -58,33 +62,37 @@ public class PyImportedModuleType implements PyType {
return new PyModuleType(file, myImportedModule).resolveMember(name, location, direction, resolveContext);
}
else if (resolved instanceof PsiDirectory) {
- final List elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, null, true, true));
+ List elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, null, true, true));
+ if (location != null && ResolveImportUtil.getPointInImport(location) == PointInImport.NONE) {
+ final Set imported = Sets.newHashSet(PyModuleType.collectImportedSubmodules((PsiDirectory)resolved, location));
+ elements = ContainerUtil.filter(elements, new Condition() {
+ @Override
+ public boolean value(PsiElement element) {
+ return imported.contains(element);
+ }
+ });
+ }
return ResolveImportUtil.rateResults(elements);
}
return null;
}
public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) {
- List result = new ArrayList();
- ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(location);
- assert scopeOwner != null;
- final List importTargets = PyModuleType.getVisibleImports(scopeOwner);
- final int imported = myImportedModule.getImportedPrefix().getComponentCount();
- for (PyImportElement importTarget : importTargets) {
- final QualifiedName qName = importTarget.getImportedQName();
- if (qName != null && qName.matchesPrefix(myImportedModule.getImportedPrefix())) {
- final List components = qName.getComponents();
- if (components.size() > imported) {
- String module = components.get(imported);
- result.add(LookupElementBuilder.create(module));
- }
- }
- }
+ final List result = new ArrayList();
final PsiElement resolved = myImportedModule.resolve();
- if (resolved instanceof PsiDirectory) {
+ if (resolved instanceof PyFile) {
+ final PyModuleType moduleType = new PyModuleType((PyFile)resolved, myImportedModule);
+ result.addAll(moduleType.getCompletionVariantsAsLookupElements(location, context, false, false));
+ }
+ else if (resolved instanceof PsiDirectory) {
final PsiDirectory dir = (PsiDirectory)resolved;
if (PyUtil.isPackage(dir, location)) {
- result.addAll(PyModuleType.getSubModuleVariants(dir, location, null));
+ if (ResolveImportUtil.getPointInImport(location) != PointInImport.NONE) {
+ result.addAll(PyModuleType.getSubModuleVariants(dir, location, null));
+ }
+ else {
+ result.addAll(PyModuleType.collectImportedSubmodulesAsLookupElements(dir, location, context.get(CTX_NAMES)));
+ }
}
}
return ArrayUtil.toObjectArray(result);
diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java
index de3be514462f..3fedadfd29c4 100644
--- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java
+++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java
@@ -29,7 +29,9 @@ import com.intellij.psi.*;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
+import com.intellij.util.Function;
import com.intellij.util.ProcessingContext;
+import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.PyCustomMember;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
@@ -331,7 +333,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
result.addAll(getSubModuleVariants(myModule.getContainingDirectory(), location, namesAlready));
}
else {
- addImportedSubmodules(location, namesAlready, result);
+ result.addAll(collectImportedSubmodulesAsLookupElements(myModule, location, namesAlready));
}
}
return result;
@@ -355,27 +357,62 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
return processor;
}
- private void addImportedSubmodules(PsiElement location, Set existingNames, List result) {
- PsiFile file = location.getContainingFile();
- if (file instanceof PyFile) {
- PyFile pyFile = (PyFile)file;
- PsiElement moduleBase = PyUtil.isPackage(myModule) ? myModule.getContainingDirectory() : myModule;
- for (PyImportElement importElement : pyFile.getImportTargets()) {
- PsiElement target = PyUtil.turnInitIntoDir(importElement.resolve());
- if (target != null && PsiTreeUtil.isAncestor(moduleBase, target, true)) {
- LookupElement element = null;
- if (target instanceof PsiFileSystemItem) {
- element = buildFileLookupElement((PsiFileSystemItem) target, existingNames);
- }
- else if (target instanceof PsiNamedElement) {
- element = LookupElementBuilder.createWithIcon((PsiNamedElement)target);
- }
- if (element != null) {
- result.add(element);
- }
- }
- }
+ @NotNull
+ public static List collectImportedSubmodulesAsLookupElements(@NotNull PsiFileSystemItem pyPackage,
+ @NotNull PsiElement location,
+ @Nullable final Set existingNames) {
+
+ return ContainerUtil.mapNotNull(collectImportedSubmodules(pyPackage, location),
+ new Function() {
+ @Override
+ public LookupElement fun(PsiElement element) {
+ if (element instanceof PsiFileSystemItem) {
+ return buildFileLookupElement((PsiFileSystemItem)element, existingNames);
+ }
+ else if (element instanceof PsiNamedElement) {
+ return LookupElementBuilder.createWithIcon((PsiNamedElement)element);
+ }
+ return null;
+ }
+ });
+ }
+
+ @NotNull
+ 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 {
+ throw new IllegalArgumentException("Package argument should be either standard Python package or namespace package");
+ }
+
+ final ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(location);
+ if (scopeOwner == null) {
+ return Collections.emptyList();
+ }
+ final List result = new ArrayList();
+ nextImportElement:
+ 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;
+ }
+ importedQName = importedQName.removeTail(1);
+ resolvedChild = PyUtil.turnInitIntoDir(ResolveImportUtil.resolveImportElement(importElement, importedQName));
+ }
+ ContainerUtil.addIfNotNull(result, resolvedChild);
+ }
+ return result;
}
public static List getSubModuleVariants(final PsiDirectory directory,
diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/a.after.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.after.py
new file mode 100644
index 000000000000..f217a2117a34
--- /dev/null
+++ b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.after.py
@@ -0,0 +1,3 @@
+import pkg1.m1
+
+pkg1.foo
\ No newline at end of file
diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/a.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.py
new file mode 100644
index 000000000000..03e922263bd6
--- /dev/null
+++ b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.py
@@ -0,0 +1,3 @@
+import pkg1.m1
+
+pkg1.f
\ No newline at end of file
diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/__init__.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/__init__.py
new file mode 100644
index 000000000000..79bf605b3de4
--- /dev/null
+++ b/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/__init__.py
@@ -0,0 +1 @@
+foo = 42
\ No newline at end of file
diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/m1.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/m1.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.after.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.after.py
new file mode 100644
index 000000000000..8d04a72755a1
--- /dev/null
+++ b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.after.py
@@ -0,0 +1,3 @@
+import nspkg1.m2
+
+nspkg1.m2
diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.py
new file mode 100644
index 000000000000..1685a9106112
--- /dev/null
+++ b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.py
@@ -0,0 +1,3 @@
+import nspkg1.m2
+
+nspkg1.m
diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m2.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m2.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m3.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m3.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/a.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/a.py
new file mode 100644
index 000000000000..496be75feba7
--- /dev/null
+++ b/python/testData/completion/submoduleOfIndirectlyImportedPackage/a.py
@@ -0,0 +1,4 @@
+import pkg1.subpkg1.m1
+import pkg1
+
+pkg1.
\ No newline at end of file
diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/__init__.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/__init__.py
new file mode 100644
index 000000000000..fe5433bef744
--- /dev/null
+++ b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/__init__.py
@@ -0,0 +1 @@
+VAR = 42
\ No newline at end of file
diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/__init__.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/m1.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/m1.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py
new file mode 100644
index 000000000000..54e2273f4550
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py
@@ -0,0 +1,5 @@
+import nspkg1.m2
+
+print(nspkg1.m2)
+print(nspkg1.m3)
+print(nspkg1.nssubpkg1)
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java
index 7e085a397a2e..51b463948b03 100644
--- a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java
+++ b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java
@@ -136,4 +136,9 @@ public class Py3CompletionTest extends PyTestCase {
myFixture.completeBasic();
myFixture.checkResultByFile("completion/" + getTestName(true) + "/nspkg1/a.after.py");
}
+
+ // PY-14385
+ public void testNotImportedSubmodulesOfNamespacePackage() {
+ doMultiFileTest();
+ }
}
diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
index a1366ac2e17a..c6a66f4f9fb0 100644
--- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java
@@ -707,4 +707,19 @@ public class PythonCompletionTest extends PyTestCase {
assertContainsElements(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES);
assertDoesntContain(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES);
}
+
+ // PY-14388
+ public void testAttributeOfIndirectlyImportedPackage() {
+ doMultiFileTest();
+ }
+
+ // PY-14387
+ public void testSubmoduleOfIndirectlyImportedPackage() {
+ myFixture.copyDirectoryToProject("completion/" + getTestName(true), "");
+ myFixture.configureByFile("a.py");
+ myFixture.completeBasic();
+ final List suggested = myFixture.getLookupElementStrings();
+ assertNotNull(suggested);
+ assertSameElements(suggested, "VAR", "subpkg1");
+ }
}
diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java
index 159a7218ff6f..32505db617a1 100644
--- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java
@@ -111,4 +111,9 @@ public class Py3UnresolvedReferencesInspectionTest extends PyTestCase {
public void testAttributesOfUnresolvedTypeFile() {
doTest();
}
+
+ // PY-14385
+ public void testNotImportedSubmodulesOfNamespacePackage() {
+ doMultiFileTest("main.py");
+ }
}