diff --git a/python/src/META-INF/python-plugin-core.xml b/python/src/META-INF/python-plugin-core.xml index 8858ffbac17e..5919af8f1e17 100644 --- a/python/src/META-INF/python-plugin-core.xml +++ b/python/src/META-INF/python-plugin-core.xml @@ -558,6 +558,7 @@ + diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java new file mode 100644 index 000000000000..0e5fc718f412 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java @@ -0,0 +1,82 @@ +package com.jetbrains.python.codeInsight.userSkeletons; + +import com.intellij.psi.PsiElement; +import com.jetbrains.python.codeInsight.PyDynamicMember; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.PyTargetExpression; +import com.jetbrains.python.psi.types.PyClassMembersProviderBase; +import com.jetbrains.python.psi.types.PyClassType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author vlan + */ +public class PyUserSkeletonsClassMembersProvider extends PyClassMembersProviderBase { + @NotNull + @Override + public Collection getMembers(@NotNull PyClassType classType, PsiElement location) { + final PyClass cls = classType.getPyClass(); + final PyClass skeleton = PyUserSkeletonsUtil.getUserSkeleton(cls); + if (skeleton != null) { + return getClassMembers(skeleton); + } + return Collections.emptyList(); + } + + @Nullable + @Override + public PsiElement resolveMember(@NotNull PyClassType classType, @NotNull String name, PsiElement location) { + final PyClass cls = classType.getPyClass(); + final PyClass skeleton = PyUserSkeletonsUtil.getUserSkeleton(cls); + if (skeleton != null) { + return findClassMember(skeleton, name); + } + return null; + } + + private static PsiElement findClassMember(@NotNull PyClass cls, @NotNull String name) { + final PyFunction function = cls.findMethodByName(name, false); + if (function != null) { + return function; + } + final PyTargetExpression instanceAttribute = cls.findInstanceAttribute(name, false); + if (instanceAttribute != null) { + return instanceAttribute; + } + final PyTargetExpression classAttribute = cls.findClassAttribute(name, false); + if (classAttribute != null) { + return classAttribute; + } + return null; + } + + private static Collection getClassMembers(@NotNull PyClass cls) { + final List result = new ArrayList(); + for (PyFunction function : cls.getMethods()) { + final String name = function.getName(); + if (name != null) { + result.add(new PyDynamicMember(name, function)); + } + } + for (PyTargetExpression attribute : cls.getInstanceAttributes()) { + final String name = attribute.getName(); + if (name != null) { + result.add(new PyDynamicMember(name, attribute)); + } + } + for (PyTargetExpression attribute : cls.getClassAttributes()) { + final String name = attribute.getName(); + if (name != null) { + result.add(new PyDynamicMember(name, attribute)); + } + } + return result; + } +} diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/DatetimeDateAttributesOutsideClass/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/DatetimeDateAttributesOutsideClass/a.py new file mode 100644 index 000000000000..0a96e561c370 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/DatetimeDateAttributesOutsideClass/a.py @@ -0,0 +1,5 @@ +from datetime import date + +print(date.today) +print(date.max) +print(date.foo) diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/DatetimeDateAttributesOutsideClass/datetime.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/DatetimeDateAttributesOutsideClass/datetime.py new file mode 100644 index 000000000000..162a9f6043c7 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/DatetimeDateAttributesOutsideClass/datetime.py @@ -0,0 +1,4 @@ +class date(object): + @classmethod + def today(cls): + return date(0, 0, 0) diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java index 7d8b659c4b82..5c51dafc860c 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -58,6 +58,11 @@ public class Py3UnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-9011 + public void testDatetimeDateAttributesOutsideClass() { + doMultiFileTest("a.py"); + } + public void testObjectNewAttributes() { doTest(); }