diff --git a/python/helpers/tools/stdlib_packages.txt b/python/helpers/tools/stdlib_packages.txt
index fb799ffd36a4..bd7722c14002 100644
--- a/python/helpers/tools/stdlib_packages.txt
+++ b/python/helpers/tools/stdlib_packages.txt
@@ -55,6 +55,7 @@ csv
ctypes
curses
datetime
+dataclasses
dbhash
dbm
decimal
diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml
index 100b069a6695..026a5417b9aa 100644
--- a/python/src/META-INF/python-core-common.xml
+++ b/python/src/META-INF/python-core-common.xml
@@ -703,6 +703,7 @@
+
diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyDataclassesTypeProvider.kt b/python/src/com/jetbrains/python/codeInsight/stdlib/PyDataclassesTypeProvider.kt
new file mode 100644
index 000000000000..56aecf472540
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyDataclassesTypeProvider.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+ */
+package com.jetbrains.python.codeInsight.stdlib
+
+import com.jetbrains.python.psi.*
+import com.jetbrains.python.psi.impl.PyCallExpressionNavigator
+import com.jetbrains.python.psi.resolve.PyResolveContext
+import com.jetbrains.python.psi.types.*
+
+class PyDataclassesTypeProvider : PyTypeProviderBase() {
+
+ override fun getReferenceExpressionType(referenceExpression: PyReferenceExpression, context: TypeEvalContext): PyType? {
+ return getDataclassTypeForCallee(referenceExpression, context)
+ }
+
+ private fun getDataclassTypeForCallee(referenceExpression: PyReferenceExpression, context: TypeEvalContext): PyCallableType? {
+ if (PyCallExpressionNavigator.getPyCallExpressionByCallee(referenceExpression) == null) return null
+
+ val resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context)
+ val resolveResults = referenceExpression.getReference(resolveContext).multiResolve(false)
+
+ return PyUtil.filterTopPriorityResults(resolveResults)
+ .asSequence()
+ .filterIsInstance()
+ .map { getDataclassTypeForClass(it, context) }
+ .firstOrNull { it != null }
+ }
+
+ private fun getDataclassTypeForClass(cls: PyClass, context: TypeEvalContext): PyCallableType? {
+ if (!PyKnownDecoratorUtil.getKnownDecorators(cls, context).contains(PyKnownDecoratorUtil.KnownDecorator.DATACLASSES_DATACLASS)) {
+ return null
+ }
+
+ val parameters = ArrayList()
+
+ cls.processClassLevelDeclarations { element, _ ->
+ if (element is PyTargetExpression && element.annotationValue != null) {
+ parameters.add(PyCallableParameterImpl.nonPsi(element.name, context.getType(element), element.findAssignedValue()))
+ }
+ true
+ }
+
+ return PyCallableTypeImpl(parameters, context.getType(cls))
+ }
+}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java
index 34e4b0f92d33..9ab09cdaf5ae 100644
--- a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java
+++ b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java
@@ -67,7 +67,9 @@ public class PyKnownDecoratorUtil {
PYRAMID_DECORATOR_REIFY("pyramid.decorator.reify"),
DJANGO_UTILS_FUNCTIONAL_CACHED_PROPERTY("django.utils.functional.cached_property"),
- KOMBU_UTILS_CACHED_PROPERTY("kombu.utils.cached_property");
+ KOMBU_UTILS_CACHED_PROPERTY("kombu.utils.cached_property"),
+
+ DATACLASSES_DATACLASS("dataclasses.dataclass");
private final QualifiedName myQualifiedName;
diff --git a/python/testData/inspections/PyArgumentListInspection/InitializingDataclass/a.py b/python/testData/inspections/PyArgumentListInspection/InitializingDataclass/a.py
new file mode 100644
index 000000000000..1097d94e02fb
--- /dev/null
+++ b/python/testData/inspections/PyArgumentListInspection/InitializingDataclass/a.py
@@ -0,0 +1,13 @@
+import dataclasses
+
+@dataclasses.dataclass
+class A:
+ x: int
+ y: str
+ z: float = 0.0
+
+A()
+A(1)
+A(1, "a")
+A(1, "a", 1.0)
+A(1, "a", 1.0, "b")
\ No newline at end of file
diff --git a/python/testData/inspections/PyArgumentListInspection/InitializingDataclass/dataclasses.py b/python/testData/inspections/PyArgumentListInspection/InitializingDataclass/dataclasses.py
new file mode 100644
index 000000000000..3a2b30d36707
--- /dev/null
+++ b/python/testData/inspections/PyArgumentListInspection/InitializingDataclass/dataclasses.py
@@ -0,0 +1,3 @@
+def dataclass(_cls=None, *, init=True, repr=True, eq=True, order=False,
+ hash=None, frozen=False):
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/PyTypeCheckerInspection/InitializingDataclass/a.py b/python/testData/inspections/PyTypeCheckerInspection/InitializingDataclass/a.py
new file mode 100644
index 000000000000..386645bb2d70
--- /dev/null
+++ b/python/testData/inspections/PyTypeCheckerInspection/InitializingDataclass/a.py
@@ -0,0 +1,13 @@
+import dataclasses
+
+@dataclasses.dataclass
+class A:
+ x: int
+ y: str
+ z: float = 0.0
+
+A(1, "a")
+A("a", 1)
+
+A(1, "a", 1.0)
+A("a", 1, "b")
\ No newline at end of file
diff --git a/python/testData/inspections/PyTypeCheckerInspection/InitializingDataclass/dataclasses.py b/python/testData/inspections/PyTypeCheckerInspection/InitializingDataclass/dataclasses.py
new file mode 100644
index 000000000000..3a2b30d36707
--- /dev/null
+++ b/python/testData/inspections/PyTypeCheckerInspection/InitializingDataclass/dataclasses.py
@@ -0,0 +1,3 @@
+def dataclass(_cls=None, *, init=True, repr=True, eq=True, order=False,
+ hash=None, frozen=False):
+ pass
\ No newline at end of file
diff --git a/python/testData/paramInfo/InitializingDataclass/a.py b/python/testData/paramInfo/InitializingDataclass/a.py
new file mode 100644
index 000000000000..61ae24848fae
--- /dev/null
+++ b/python/testData/paramInfo/InitializingDataclass/a.py
@@ -0,0 +1,9 @@
+import dataclasses
+
+@dataclasses.dataclass
+class A:
+ x: int
+ y: str
+ z: float = 0.0
+
+A()
\ No newline at end of file
diff --git a/python/testData/paramInfo/InitializingDataclass/dataclasses.py b/python/testData/paramInfo/InitializingDataclass/dataclasses.py
new file mode 100644
index 000000000000..3a2b30d36707
--- /dev/null
+++ b/python/testData/paramInfo/InitializingDataclass/dataclasses.py
@@ -0,0 +1,3 @@
+def dataclass(_cls=None, *, init=True, repr=True, eq=True, order=False,
+ hash=None, frozen=False):
+ pass
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java b/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java
index 48efbff1c88b..301d7539b9af 100644
--- a/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java
+++ b/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java
@@ -707,6 +707,18 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
);
}
+ // PY-27398
+ public void testInitializingDataclass() {
+ runWithLanguageLevel(
+ LanguageLevel.PYTHON37,
+ () -> {
+ final Map marks = loadMultiFileTest(1);
+
+ feignCtrlP(marks.get("").getTextOffset()).check("x: int, y: str, z: float=0.0", new String[]{"x: int, "});
+ }
+ );
+ }
+
/**
* Imitates pressing of Ctrl+P; fails if results are not as expected.
* @param offset offset of 'cursor' where Ctrl+P is pressed.
diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java
index f3fdd48dbc41..532a597bec65 100644
--- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java
@@ -276,4 +276,9 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase {
public void testChainedComparisonsGenericMatching() {
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest);
}
+
+ // PY-27398
+ public void testInitializingDataclass() {
+ runWithLanguageLevel(LanguageLevel.PYTHON37, () -> super.doMultiFileTest());
+ }
}
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java
index 1951579b8db6..11325f399660 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java
@@ -318,4 +318,9 @@ public class PyArgumentListInspectionTest extends PyInspectionTestCase {
public void testAbstractMethod() {
runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTest);
}
+
+ // PY-27398
+ public void testInitializingDataclass() {
+ runWithLanguageLevel(LanguageLevel.PYTHON37, this::doMultiFileTest);
+ }
}