Improve ignoring access to namedtuple's protected members (PY-27148)

This commit is contained in:
Semyon Proshev
2018-01-17 22:06:35 +03:00
parent a250961d03
commit fcefe43db9
5 changed files with 68 additions and 3 deletions
@@ -91,7 +91,6 @@ public class PyProtectedMemberInspection extends PyInspection {
}
private void checkReference(@NotNull final PyReferenceExpression node, @NotNull final PyExpression qualifier) {
if (myTypeEvalContext.getType(qualifier) instanceof PyNamedTupleType) return;
final String name = node.getName();
final List<LocalQuickFix> quickFixes = new ArrayList<>();
quickFixes.add(new PyRenameElementQuickFix());
@@ -5,6 +5,7 @@ import com.intellij.psi.PsiReference
import com.jetbrains.python.PyNames
import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType
import com.jetbrains.python.codeInsight.stdlib.PyStdlibClassMembersProvider
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
import com.jetbrains.python.inspections.PyInspectionExtension
import com.jetbrains.python.psi.PyElement
import com.jetbrains.python.psi.PyFunction
@@ -15,6 +16,10 @@ import com.jetbrains.python.psi.types.TypeEvalContext
class PyStdlibInspectionExtension : PyInspectionExtension() {
companion object {
private val NAMEDTUPLE_SPECIAL_ATTRIBUTES = setOf("_make", "_asdict", "_replace", "_source", "_fields")
}
override fun ignoreInitNewSignatures(original: PyFunction, complementary: PyFunction): Boolean {
return PyNames.TYPE_ENUM == complementary.containingClass?.qualifiedName
}
@@ -39,4 +44,25 @@ class PyStdlibInspectionExtension : PyInspectionExtension() {
return false
}
override fun ignoreProtectedSymbol(expression: PyReferenceExpression, context: TypeEvalContext): Boolean {
val qualifier = expression.qualifier
if (qualifier != null && expression.referencedName in NAMEDTUPLE_SPECIAL_ATTRIBUTES) {
val qualifierType = context.getType(qualifier)
if (qualifierType is PyNamedTupleType) {
return true
}
val isTypingNT: (PyClassLikeType?) -> Boolean =
{ it is PyNamedTupleType || it != null && PyTypingTypeProvider.NAMEDTUPLE == it.classQName }
if (qualifierType is PyClassLikeType && qualifierType.getAncestorTypes(context).find(isTypingNT) != null) {
return true
}
}
return false
}
}
@@ -1,6 +1,20 @@
from collections import namedtuple
i = namedtuple('Point', ['x', 'y'], verbose=True)
i._replace( **{"a":"a"})
class Cat1(namedtuple("Cat", "name age")):
pass
c1 = Cat1("name", 5)
print(c1._make)
print(c1._asdict)
print(c1._replace)
print(c1._source)
print(c1._fields)
Cat2 = namedtuple("Cat2", "name age")
c2 = Cat2("name", 5)
print(c2._make)
print(c2._asdict)
print(c2._replace)
print(c2._source)
print(c2._fields)
@@ -0,0 +1,21 @@
from typing import NamedTuple
class Cat1(NamedTuple):
name: str
age: int
c1 = Cat1("name", 5)
print(c1._make)
print(c1._asdict)
print(c1._replace)
print(c1._source)
print(c1._fields)
Cat2 = NamedTuple("Cat2", name=str, age=int)
c2 = Cat2("name", 5)
print(c2._make)
print(c2._asdict)
print(c2._replace)
print(c2._source)
print(c2._fields)
@@ -79,6 +79,11 @@ public class PyProtectedMemberInspectionTest extends PyInspectionTestCase {
runWithLanguageLevel(LanguageLevel.PYTHON35, this::doMultiFileTest);
}
// PY-27148
public void testTypingNamedTuple() {
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest);
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {