mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Handle namedtuples unpacking (PY-4351)
This commit is contained in:
committed by
Semyon Proshev
parent
f397c98170
commit
80b3375629
@@ -19,7 +19,7 @@ import java.util.*;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType {
|
||||
public class PyNamedTupleType extends PyTupleType implements PyCallableType {
|
||||
|
||||
@NotNull
|
||||
private final PsiElement myDeclaration;
|
||||
@@ -38,7 +38,11 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
|
||||
@NotNull String name,
|
||||
@NotNull Map<String, FieldTypeAndDefaultValue> fields,
|
||||
@NotNull DefinitionLevel definitionLevel) {
|
||||
super(tupleClass, definitionLevel != DefinitionLevel.INSTANCE);
|
||||
super(tupleClass,
|
||||
Collections.unmodifiableList(ContainerUtil.map(fields.values(), typeAndValue -> typeAndValue.getType())),
|
||||
false,
|
||||
definitionLevel != DefinitionLevel.INSTANCE);
|
||||
|
||||
myDeclaration = declaration;
|
||||
myFields = Collections.unmodifiableMap(fields);
|
||||
myName = name;
|
||||
@@ -127,10 +131,6 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getElementCount() {
|
||||
return myFields.size();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, FieldTypeAndDefaultValue> getFields() {
|
||||
return myFields;
|
||||
@@ -152,7 +152,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
|
||||
@NotNull
|
||||
private Map<String, FieldTypeAndDefaultValue> takeFieldsTypesFromCallSiteIfNeeded(@NotNull TypeEvalContext context,
|
||||
@NotNull PyCallSiteExpression callSite) {
|
||||
if (StreamEx.ofValues(myFields).allMatch(typeAndValue -> typeAndValue.getType() == null)) {
|
||||
if (StreamEx.of(getElementTypes()).allMatch(Objects::isNull)) {
|
||||
final List<PyExpression> arguments = callSite.getArguments(null);
|
||||
|
||||
if (arguments.size() == myFields.size()) {
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// 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.documentation;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
@@ -21,6 +7,7 @@ import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
@@ -233,7 +220,10 @@ public class PyTypeModelBuilder {
|
||||
myVisited.put(type, null); //mark as evaluating
|
||||
|
||||
TypeModel result = null;
|
||||
if (type instanceof PyTupleType) {
|
||||
if (type instanceof PyNamedTupleType) {
|
||||
result = NamedType.nameOrAny(type);
|
||||
}
|
||||
else if (type instanceof PyTupleType) {
|
||||
final PyTupleType tupleType = (PyTupleType)type;
|
||||
|
||||
final List<PyType> elementTypes = tupleType.isHomogeneous()
|
||||
|
||||
@@ -183,8 +183,8 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
|
||||
final PyTupleExpression targetTuple = PsiTreeUtil.findChildOfType(lhs, PyTupleExpression.class, false);
|
||||
if (value != null && targetTuple != null) {
|
||||
final PyType assignedType = PyTypeChecker.toNonWeakType(context.getType(value), context);
|
||||
if (assignedType instanceof PyTupleType) {
|
||||
final PyType t = PyTypeChecker.getTargetTypeFromTupleAssignment(this, targetTuple, (PyTupleType)assignedType);
|
||||
if (assignedType != null) {
|
||||
final PyType t = PyTypeChecker.getTargetTypeFromTupleAssignment(this, targetTuple, assignedType, context);
|
||||
if (t != null) {
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// 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.psi.types;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -54,11 +40,16 @@ public class PyTupleType extends PyClassTypeImpl implements PyCollectionType {
|
||||
}
|
||||
|
||||
public PyTupleType(@NotNull PyClass tupleClass, @NotNull List<PyType> elementTypes, boolean homogeneous) {
|
||||
super(tupleClass, false);
|
||||
this(tupleClass, elementTypes, homogeneous, false);
|
||||
}
|
||||
|
||||
protected PyTupleType(@NotNull PyClass tupleClass, @NotNull List<PyType> elementTypes, boolean homogeneous, boolean isDefinition) {
|
||||
super(tupleClass, isDefinition);
|
||||
myElementTypes = elementTypes;
|
||||
myHomogeneous = homogeneous;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getName() {
|
||||
if (myHomogeneous) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyTypeProvider;
|
||||
@@ -704,6 +705,26 @@ public class PyTypeChecker {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PyType getTargetTypeFromTupleAssignment(@NotNull PyTargetExpression target,
|
||||
@NotNull PyTupleExpression parentTuple,
|
||||
@NotNull PyType assignedType,
|
||||
@NotNull TypeEvalContext context) {
|
||||
if (assignedType instanceof PyTupleType) {
|
||||
return getTargetTypeFromTupleAssignment(target, parentTuple, (PyTupleType)assignedType);
|
||||
}
|
||||
else if (assignedType instanceof PyClassLikeType) {
|
||||
return StreamEx
|
||||
.of(((PyClassLikeType)assignedType).getAncestorTypes(context))
|
||||
.select(PyNamedTupleType.class)
|
||||
.findFirst()
|
||||
.map(t -> getTargetTypeFromTupleAssignment(target, parentTuple, t))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PyType getTargetTypeFromTupleAssignment(@NotNull PyTargetExpression target, @NotNull PyTupleExpression parentTuple,
|
||||
@NotNull PyTupleType assignedTupleType) {
|
||||
|
||||
@@ -37,7 +37,7 @@ def list_tuple(cond):
|
||||
|
||||
"%s %s" % named_tuple_func(True)
|
||||
"%s %s" % named_tuple_func(False)
|
||||
"%s %s %s" % named_tuple_func(False)
|
||||
"%s %s %s" % <warning descr="Too few arguments for format string">named_tuple_func(False)</warning>
|
||||
|
||||
"%s" % primitive_types_func(True)
|
||||
"%s %s" % <warning descr="Too few arguments for format string">primitive_types_func(True)</warning>
|
||||
|
||||
@@ -2217,6 +2217,44 @@ public class PyTypeTest extends PyTestCase {
|
||||
"expr = User(\"name\", 13).age");
|
||||
}
|
||||
|
||||
// PY-4351
|
||||
public void testTypingNTInheritorUnpacking() {
|
||||
doTest("int",
|
||||
"from typing import NamedTuple\n" +
|
||||
"class User(NamedTuple(\"User\", [(\"name\", str), (\"age\", int)])):\n" +
|
||||
" pass\n" +
|
||||
"y2, expr = User(\"name\", 13)");
|
||||
}
|
||||
|
||||
// PY-4351
|
||||
public void testTypingNTTargetUnpacking() {
|
||||
doTest("int",
|
||||
"from typing import NamedTuple\n" +
|
||||
"Point2 = NamedTuple('Point', [('x', int), ('y', str)])\n" +
|
||||
"p2 = Point2(1, \"1\")\n" +
|
||||
"expr, y2 = p2");
|
||||
}
|
||||
|
||||
// PY-4351
|
||||
public void testCollectionsNTInheritorUnpacking() {
|
||||
// Seems that this case won't be supported because
|
||||
// it requires to update ancestor, not class itself, for every `User(...)` call
|
||||
doTest("Any",
|
||||
"from collections import namedtuple\n" +
|
||||
"class User(namedtuple(\"User\", \"name ags\")):\n" +
|
||||
" pass\n" +
|
||||
"y1, expr = User(\"name\", 13)");
|
||||
}
|
||||
|
||||
// PY-4351
|
||||
public void testCollectionsNTTargetUnpacking() {
|
||||
doTest("int",
|
||||
"from collections import namedtuple\n" +
|
||||
"Point = namedtuple('Point', ['x', 'y'])\n" +
|
||||
"p1 = Point(1, '1')\n" +
|
||||
"expr, y1 = p1");
|
||||
}
|
||||
|
||||
// PY-18791
|
||||
public void testCallOnProperty() {
|
||||
runWithLanguageLevel(
|
||||
|
||||
Reference in New Issue
Block a user