diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml
index 65a362ba0c68..457088cb3e30 100644
--- a/python/src/META-INF/python-plugin-common.xml
+++ b/python/src/META-INF/python-plugin-common.xml
@@ -324,6 +324,7 @@
+
@@ -354,6 +355,7 @@
+
diff --git a/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java b/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java
index 2a895618e65b..8c63c06b7d0f 100644
--- a/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java
+++ b/python/src/com/jetbrains/python/codeInsight/PyDynamicMember.java
@@ -74,7 +74,10 @@ public class PyDynamicMember {
return myName;
}
- public static Icon getIcon() {
+ public Icon getIcon() {
+ if (myTarget != null) {
+ return myTarget.getIcon(0);
+ }
return IconLoader.getIcon("/nodes/method.png");
}
diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java b/python/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java
new file mode 100644
index 000000000000..12b7f55b5415
--- /dev/null
+++ b/python/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java
@@ -0,0 +1,28 @@
+package com.jetbrains.python.psi.types;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.jetbrains.python.codeInsight.PyDynamicMember;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author yole
+ */
+public abstract class PyModuleMembersProvider {
+ public static final ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.pyModuleMembersProvider");
+
+ public abstract Collection getMembers(PsiFile module);
+
+ @Nullable
+ public PsiElement resolveMember(PsiFile module, String name) {
+ for (PyDynamicMember o : getMembers(module)) {
+ if (o.getName().equals(name)) {
+ return o.resolve(module);
+ }
+ }
+ return null;
+ }
+}
diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java
index e66bcd01d760..bc9bf3568395 100644
--- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java
+++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java
@@ -3,12 +3,14 @@ package com.jetbrains.python.psi.types;
import com.google.common.collect.ImmutableSet;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.intellij.util.SmartList;
import com.jetbrains.python.PyNames;
+import com.jetbrains.python.codeInsight.PyDynamicMember;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyFile;
@@ -46,6 +48,13 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
@Nullable
public List extends PsiElement> resolveMember(final String name, AccessDirection direction, PyResolveContext resolveContext) {
+ for(PyModuleMembersProvider provider: Extensions.getExtensions(PyModuleMembersProvider.EP_NAME)) {
+ final PsiElement element = provider.resolveMember(myModule, name);
+ if (element != null) {
+ return new SmartList(element);
+ }
+ }
+
//return PyResolveUtil.treeWalkUp(new PyResolveUtil.ResolveProcessor(name), myModule, null, null);
final PsiElement result = ResolveImportUtil.resolveChild(myModule, name, myModule, null, false);
if (result != null) return new SmartList(result);
@@ -94,6 +103,14 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
public Object[] getCompletionVariants(String completionPrefix, PyExpression location, ProcessingContext context) {
Set names_already = context.get(CTX_NAMES);
List