Enable pyi-stubs for collections module (PY-23259, PY-21415, PY-17865, PY-17206)

Infer `tuple` class type when `collections.namedtuple` could not be analyzed.
Infer `namedtuple` class type when `typing.NamedTuple` could not be analyzed.
Create callable type for `typing.Callable`.
Update ignoring `__getitem__` for ancestors, docstrings and annotations.
Infer superclass collection type correctly.
This commit is contained in:
Semyon Proshev
2018-01-19 18:34:31 +03:00
parent a4e9b63426
commit 49d3aade75
18 changed files with 600 additions and 70 deletions
@@ -199,7 +199,8 @@ public class PyNames {
public static final String CALLABLE_BUILTIN = "callable";
public static final String NAMEDTUPLE = "namedtuple";
public static final String COLLECTIONS = "collections";
public static final String COLLECTIONS_NAMEDTUPLE = COLLECTIONS + "." + NAMEDTUPLE;
public static final String COLLECTIONS_NAMEDTUPLE_PY2 = COLLECTIONS + "." + NAMEDTUPLE;
public static final String COLLECTIONS_NAMEDTUPLE_PY3 = COLLECTIONS + "." + INIT + "." + NAMEDTUPLE;
public static final String FORMAT = "format";
@@ -65,7 +65,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
if (args.length == 1 && args[0] instanceof PyReferenceExpression) {
final PyReferenceExpression target = (PyReferenceExpression)args[0];
pushAssertion(target, myPositive, false, context -> PyTypeParser.getTypeByName(target, "collections." + PyNames.CALLABLE, context));
pushAssertion(target, myPositive, false, context -> new PyCallableTypeImpl(null, null));
}
}
else if (node.isCalleeText(PyNames.ISSUBCLASS)) {
@@ -411,7 +411,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
private static PyNamedTupleType getNamedTupleFunctionType(@NotNull PyFunction function,
@NotNull TypeEvalContext context,
@NotNull PyCallExpression call) {
if (PyNames.COLLECTIONS_NAMEDTUPLE.equals(function.getQualifiedName())) {
if (ArrayUtil.contains(function.getQualifiedName(), PyNames.COLLECTIONS_NAMEDTUPLE_PY2, PyNames.COLLECTIONS_NAMEDTUPLE_PY3)) {
return getNamedTupleTypeFromAST(call, context, PyNamedTupleType.DefinitionLevel.NT_FUNCTION);
}
@@ -42,7 +42,7 @@ object PyTypeShed {
private val ONLY_SUPPORTED_PY2_MINOR = 7
private val SUPPORTED_PY3_MINORS = 2..7
val WHITE_LIST = setOf(TYPING, "six", "__builtin__", "builtins", "exceptions", "types", "datetime", "functools", "shutil", "re", "time",
"argparse", "uuid", "threading", "signal")
"argparse", "uuid", "threading", "signal", "collections")
private val BLACK_LIST = setOf<String>()
/**
@@ -15,28 +15,53 @@
*/
package com.jetbrains.python.codeInsight.typing
import com.intellij.psi.PsiReference
import com.jetbrains.python.PyNames
import com.jetbrains.python.inspections.PyInspectionExtension
import com.jetbrains.python.psi.PyElement
import com.jetbrains.python.psi.PyReferenceExpression
import com.jetbrains.python.psi.PySubscriptionExpression
import com.jetbrains.python.psi.PyTargetExpression
import com.jetbrains.python.psi.impl.PyBuiltinCache
import com.jetbrains.python.psi.impl.references.PyOperatorReference
import com.jetbrains.python.psi.resolve.PyResolveContext
import com.jetbrains.python.psi.types.PyClassLikeType
import com.jetbrains.python.psi.types.PyClassType
import com.jetbrains.python.psi.types.PyType
import com.jetbrains.python.psi.types.TypeEvalContext
class PyTypingInspectionExtension : PyInspectionExtension() {
override fun ignoreUnresolvedMember(type: PyType, name: String, context: TypeEvalContext): Boolean {
return name == PyNames.GETITEM &&
type is PyClassLikeType &&
type.isDefinition &&
!isBuiltin(type) &&
isGenericItselfOrDescendant(type, context)
override fun ignoreUnresolvedReference(node: PyElement, reference: PsiReference, context: TypeEvalContext): Boolean {
if (node is PySubscriptionExpression && reference is PyOperatorReference && node.referencedName == PyNames.GETITEM) {
val operand = node.operand
val type = context.getType(operand)
if (type is PyClassLikeType && type.isDefinition && isGenericItselfOrDescendant(type, context)) {
// `true` is not returned for the cases like `typing.List[int]`
// because these types contain builtins as a class
if (!isBuiltin(type)) return true
// here is the check that current element is like `typing.List[int]`
// but be careful: builtin collections inherit `typing.Generic` in typeshed
if (operand is PyReferenceExpression) {
val resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context)
val resolveResults = operand.getReference(resolveContext).multiResolve(false)
if (resolveResults
.asSequence()
.map { it.element }
.any { it is PyTargetExpression && PyTypingTypeProvider.BUILTIN_COLLECTION_CLASSES.containsKey(it.qualifiedName) }) {
return true
}
}
}
}
return false
}
private fun isGenericItselfOrDescendant(type: PyClassLikeType,
context: TypeEvalContext): Boolean {
return PyTypingTypeProvider.GENERIC_CLASSES.contains(type.classQName) ||
type.getAncestorTypes(context).any { PyTypingTypeProvider.GENERIC_CLASSES.contains(it.classQName) }
private fun isGenericItselfOrDescendant(type: PyClassLikeType, context: TypeEvalContext): Boolean {
return PyTypingTypeProvider.GENERIC_CLASSES.contains(type.classQName) || PyTypingTypeProvider.isGeneric(type, context)
}
private fun isBuiltin(type: PyClassLikeType): Boolean {
@@ -69,6 +69,14 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
public static final String TYPE = "typing.Type";
public static final String ANY = "typing.Any";
private static final String CALLABLE = "typing.Callable";
private static final String LIST = "typing.List";
private static final String DICT = "typing.Dict";
private static final String DEFAULT_DICT = "typing.DefaultDict";
private static final String SET = "typing.Set";
private static final String FROZEN_SET = "typing.FrozenSet";
private static final String COUNTER = "typing.Counter";
private static final String DEQUE = "typing.Deque";
private static final String TUPLE = "typing.Tuple";
private static final String CLASSVAR = "typing.ClassVar";
public static final String NAMEDTUPLE_SIMPLE = "NamedTuple";
@@ -85,12 +93,18 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
public static final Pattern TYPE_COMMENT_PATTERN = Pattern.compile("# *type: *(.*)");
private static final ImmutableMap<String, String> COLLECTION_CLASSES = ImmutableMap.<String, String>builder()
.put("typing.List", "list")
.put("typing.Dict", "dict")
.put("typing.Set", PyNames.SET)
.put("typing.FrozenSet", "frozenset")
.put("typing.Tuple", PyNames.TUPLE)
public static final ImmutableMap<String, String> BUILTIN_COLLECTION_CLASSES = ImmutableMap.<String, String>builder()
.put(LIST, "list")
.put(DICT, "dict")
.put(SET, PyNames.SET)
.put(FROZEN_SET, "frozenset")
.put(TUPLE, PyNames.TUPLE)
.build();
private static final ImmutableMap<String, String> COLLECTIONS_CLASSES = ImmutableMap.<String, String>builder()
.put(DEFAULT_DICT, "collections.DefaultDict")
.put(COUNTER, "collections.Counter")
.put(DEQUE, "collections.Deque")
.build();
public static final ImmutableMap<String, String> TYPING_COLLECTION_CLASSES = ImmutableMap.<String, String>builder()
@@ -116,17 +130,21 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
.add("typing.Any")
.add("typing.TypeVar")
.add(GENERIC)
.add("typing.Tuple")
.add(TUPLE)
.add(CALLABLE)
.add("typing.Type")
.add("typing.no_type_check")
.add("typing.Union")
.add("typing.Optional")
.add("typing.List")
.add("typing.Dict")
.add("typing.DefaultDict")
.add("typing.Set")
.add(LIST)
.add(DICT)
.add(DEFAULT_DICT)
.add(SET)
.add(FROZEN_SET)
.add(PROTOCOL)
.add(CLASSVAR)
.add(COUNTER)
.add(DEQUE)
.build();
@Nullable
@@ -144,6 +162,12 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
return createTypingProtocolType();
}
}
// Check for the exact name in advance for performance reasons
if ("Callable".equals(referenceExpression.getName())) {
if (resolveToQualifiedNames(referenceExpression, context).contains(CALLABLE)) {
return createTypingCallableType();
}
}
return null;
}
@@ -328,6 +352,11 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
return new PyCustomType(PROTOCOL, null, false);
}
@NotNull
private static PyType createTypingCallableType() {
return new PyCallableTypeImpl(null, null);
}
private static boolean omitFirstParamInTypeComment(@NotNull PyFunction func, @NotNull PyFunctionTypeAnnotation annotation) {
return func.getContainingClass() != null && func.getModifier() != PyFunction.Modifier.STATICMETHOD &&
annotation.getParameterTypeList().getParameterTypes().size() < func.getParameterList().getParameters().length;
@@ -441,6 +470,16 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
if (PROTOCOL.equals(target.getQualifiedName())) {
return createTypingProtocolType();
}
// Depends on typing.Callable defined as a target expression
if (CALLABLE.equals(target.getQualifiedName())) {
return createTypingCallableType();
}
final PyType collection = getCollection(target, context);
if (collection instanceof PyInstantiableType) {
return ((PyInstantiableType)collection).toClass();
}
final Ref<PyType> annotatedType = getTypeFromTargetExpressionAnnotation(target, context);
if (annotatedType != null) {
return annotatedType.get();
@@ -563,8 +602,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
@NotNull
@Override
public Map<PyType, PyType> getGenericSubstitutions(@NotNull PyClass cls, @NotNull TypeEvalContext context) {
final Context ctx = new Context(context);
if (!isGeneric(cls, ctx)) {
if (!isGeneric(cls, context)) {
return Collections.emptyMap();
}
final Map<PyType, PyType> results = new HashMap<>();
@@ -577,6 +615,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
results.putAll(superSubstitutions);
}
if (superClass != null) {
final Context ctx = new Context(context);
final List<PyType> superGenerics = collectGenericTypes(superClass, ctx);
final List<PyExpression> indices = subscriptionExpr != null ? getSubscriptionIndices(subscriptionExpr) : Collections.emptyList();
for (int i = 0; i < superGenerics.size(); i++) {
@@ -649,7 +688,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
@NotNull
private static List<PyType> collectGenericTypes(@NotNull PyClass cls, @NotNull Context context) {
if (!isGeneric(cls, context)) {
if (!isGeneric(cls, context.getTypeContext())) {
return Collections.emptyList();
}
final TypeEvalContext typeEvalContext = context.getTypeContext();
@@ -667,8 +706,8 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
.toList();
}
private static boolean isGeneric(@NotNull PyClass cls, @NotNull Context context) {
for (PyClassLikeType ancestor : cls.getAncestorTypes(context.getTypeContext())) {
public static boolean isGeneric(@NotNull PyWithAncestors descendant, @NotNull TypeEvalContext context) {
for (PyClassLikeType ancestor : descendant.getAncestorTypes(context)) {
if (ancestor != null && GENERIC_CLASSES.contains(ancestor.getClassQName())) {
return true;
}
@@ -723,9 +762,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
if (parameterizedType != null) {
return Ref.create(parameterizedType);
}
final PyType builtinCollection = getBuiltinCollection(resolved, context.getTypeContext());
if (builtinCollection != null) {
return Ref.create(builtinCollection);
final PyType collection = getCollection(resolved, context.getTypeContext());
if (collection != null) {
return Ref.create(collection);
}
final PyType genericType = getGenericTypeFromTypeVar(resolved, context);
if (genericType != null) {
@@ -1019,10 +1058,16 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
}
@Nullable
private static PyType getBuiltinCollection(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
final String collectionName = getQualifiedName(element);
final String builtinName = COLLECTION_CLASSES.get(collectionName);
return builtinName != null ? PyTypeParser.getTypeByName(element, builtinName, context) : null;
private static PyType getCollection(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
final String typingName = getQualifiedName(element);
final String builtinName = BUILTIN_COLLECTION_CLASSES.get(typingName);
if (builtinName != null) return PyTypeParser.getTypeByName(element, builtinName, context);
final String collectionName = COLLECTIONS_CLASSES.get(typingName);
if (collectionName != null) return PyTypeParser.getTypeByName(element, collectionName, context);
return null;
}
@NotNull
@@ -148,6 +148,7 @@ public class PyiUtil {
return PyUtil.as(PyResolveImportUtil.resolveQualifiedName(name, context)
.stream()
.findFirst()
.map(PyUtil::turnDirIntoInit)
.orElse(null), PyiFile.class);
}
@@ -161,6 +162,7 @@ public class PyiUtil {
return PyUtil.as(PyResolveImportUtil.resolveQualifiedName(name, context)
.stream()
.findFirst()
.map(PyUtil::turnDirIntoInit)
.orElse(null), PyFile.class);
}
@@ -16,4 +16,6 @@ class Z(Y[T]):
pass
Z[int]
a: Z[int]
Alias = Z[int]
Z[int]()
@@ -0,0 +1,4 @@
from b import A
A().append()
# <ref>
@@ -0,0 +1,4 @@
from typing import List
class A(List[int]):
pass
@@ -0,0 +1,5 @@
from typing import List
class A(List[int]):
pass
A().append()
# <ref>
@@ -0,0 +1,4 @@
from typing import NamedTuple
from b import fields
nt = NamedTuple("name", **fields)
@@ -0,0 +1,169 @@
def test_namedtuple():
from collections import namedtuple
assert namedtuple("Point", "x y")(1, 2).x == 1
assert namedtuple(u"Point", u"x y", verbose=False, rename=False)(1, 2).x == 1
assert namedtuple(b"Point", b"x y", verbose=True, rename=True)(1, 2).x == 1
assert namedtuple("Point", ["x", "y"])(1, 2).x == 1
assert namedtuple(u"Point", [u"x", u"y"])(1, 2).x == 1
assert namedtuple(b"Point", [b"x", b"y"])(1, 2).x == 1
assert namedtuple("Point", [b"x", u"y"])(1, 2).x == 1
Point = namedtuple('Point', 'x y')
p = Point(1, 2)
assert p == Point(1, 2)
assert p._replace(y=3.14).y == 3.14
assert p._asdict()['x'] == 1
assert p._fields == ('x', 'y')
assert p == (1, 2)
assert (p.x, p.y) == (1, 2)
assert p[0] + p[1] == 3
assert p.index(1) == 0
assert Point._make([1, 3.14]).y == 3.14
def test_deque():
from collections import deque
d = deque([2])
assert list(deque([1, 2, 3])) == [1, 2, 3]
assert list(deque([1, 2, 3], 2)) == [2, 3]
assert deque([1, 2, 3]).maxlen is None
assert deque([1, 2, 3], 2).maxlen == 2
d.append(3)
d.appendleft(1)
assert list(d) == [1, 2, 3]
d.clear()
assert len(d) == 0
d.extend([1, 2, 3])
d.extendleft([5, 3, 1])
assert d.count(3) == 2
assert list(d) == [1, 3, 5, 1, 2, 3]
assert d.pop() == 3
assert d.popleft() == 1
d.remove(5)
assert list(d) == [3, 1, 2]
d.reverse()
assert list(d) == [2, 1, 3]
d.rotate(1)
assert list(d) == [3, 2, 1]
d.rotate(-2)
assert list(d) == [1, 3, 2]
assert len(d) == 3
assert 3 in d
assert d[1] == 3
d[1] = 4
assert list(d) == [1, 4, 2]
assert list(reversed(d)) == [2, 4, 1]
def test_counter():
from collections import Counter
c = Counter()
assert Counter("abc") == {"a": 1, "b": 1, "c": 1}
assert Counter({"a": 1, "b": 2, "c": 3}) == {"a": 1, "b": 2, "c": 3}
assert Counter(a=1, b=2, c=3) == {"a": 1, "b": 2, "c": 3}
c["abc"] = 1
c["def"] = 2
assert c == {"abc": 1, "def": 2}
c["def"] = 0
assert c == {"abc": 1, "def": 0}
del c["def"]
assert c["ghi"] == 0
c.update({"ghi": 2})
assert list(c.elements()) == ["abc", "ghi", "ghi"]
c.update(["a", "a", "b", "a", "a", "a", "b", "a"])
assert c == {"abc": 1, "ghi": 2, "a": 6, "b": 2}
c.update(a=-3, b=-1)
assert c == {"abc": 1, "ghi": 2, "a": 3, "b": 1}
assert c.most_common(2) == [("a", 3), ("ghi", 2)]
assert c.most_common() == [("a", 3), ("ghi", 2), ("abc", 1), ("b", 1)]
c.subtract({"abc": 1, "ghi": -2, "a": 3, "b": -1})
assert c == {"abc": 0, "ghi": 4, "a": 0, "b": 2}
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
assert c + d == Counter({'a': 4, 'b': 3})
assert c - d == Counter({'a': 2})
assert c & d == Counter({'a': 1, 'b': 1})
assert c | d == Counter({'a': 3, 'b': 2})
c = Counter(a=3, b=1)
c += d
assert c == Counter({'a': 4, 'b': 3})
c = Counter(a=3, b=1)
c -= d
assert c == Counter({'a': 2})
c = Counter(a=3, b=1)
c &= d
assert c == Counter({'a': 1, 'b': 1})
c = Counter(a=3, b=1)
c |= d
assert c == Counter({'a': 3, 'b': 2})
c = Counter(a=3, b=1)
c.subtract(["a", "b"])
assert c == {"a": 2, "b": 0}
def test_ordered_dict():
from collections import OrderedDict
od = OrderedDict([("a", 1), ("b", 2), ("c", 3), ("d", 4)])
assert od.popitem() == ("d", 4)
assert od == OrderedDict([("a", 1), ("b", 2), ("c", 3)])
assert od.popitem(last=False) == ("a", 1)
assert od == OrderedDict([("b", 2), ("c", 3)])
assert od.popitem(last=True) == ("c", 3)
assert od == OrderedDict([("b", 2)])
assert od == {"b": 2}
def test_defaultdict():
from collections import defaultdict
assert defaultdict() == {}
assert defaultdict(k1=1, k2=2) == {"k1": 1, "k2": 2}
assert defaultdict(lambda: 1) == {}
assert defaultdict(lambda: 2, {"k1": 1, "k2": 2}) == {"k1": 1, "k2": 2}
assert defaultdict(lambda: 3, [("k1", 1), ("k2", 2)]) == {"k1": 1, "k2": 2}
assert defaultdict(None) == {}
assert defaultdict(None, {"k1": 1, "k2": 2}) == {"k1": 1, "k2": 2}
assert defaultdict(None, [("k1", 1), ("k2", 2)]) == {"k1": 1, "k2": 2}
assert defaultdict(lambda: 4).__missing__("key") == 4
def test_abc():
from collections import (Container, Hashable, Iterable, Iterator, Sized, Callable, Sequence, MutableSequence, Set,
MutableSet, Mapping, MutableMapping, MappingView, ItemsView, KeysView, ValuesView)
assert [Container, Hashable, Iterable, Iterator, Sized, Callable, Sequence, MutableSequence, Set, MutableSet,
Mapping, MutableMapping, MappingView, ItemsView, KeysView, ValuesView]
@@ -1,14 +0,0 @@
def test_namedtuple():
from collections import namedtuple
Point = namedtuple('Point', 'x y')
p = Point(1, 2)
assert p == Point(1, 2)
assert p == (1, 2)
assert p._replace(y=3.14).y == 3.14
assert p._asdict()['x'] == 1
assert (p.x, p.y) == (1, 2)
assert p[0] + p[1] == 3
assert p.index(1) == 0
assert Point._make([1, 3.14]).y == 3.14
@@ -0,0 +1,242 @@
def test_namedtuple():
from collections import namedtuple
import sys
if sys.version_info >= (3, 6):
assert namedtuple("Point", "x y", verbose=True, rename=True, module="m1")(1, 2).x == 1
assert namedtuple(u"Point", u"x y", verbose=False, rename=False, module="m2")(1, 2).x == 1
assert namedtuple(u"Point", u"x y", verbose=False, rename=False, module=None)(1, 2).x == 1
assert namedtuple("Point", ["x", "y"])(1, 2).x == 1
assert namedtuple(u"Point", [u"x", u"y"])(1, 2).x == 1
else:
assert namedtuple("Point", "x y", verbose=True, rename=True)(1, 2).x == 1
assert namedtuple(u"Point", u"x y", verbose=False, rename=False)(1, 2).x == 1
assert namedtuple("Point", ["x", "y"], True, True)(1, 2).x == 1
assert namedtuple(u"Point", [u"x", u"y"], False, False)(1, 2).x == 1
Point = namedtuple('Point', 'x y')
p = Point(1, 2)
assert p == Point(1, 2)
assert p._replace(y=3.14).y == 3.14
assert p._asdict()['x'] == 1
assert p._fields == ('x', 'y')
assert p._source is not None
assert p == (1, 2)
assert (p.x, p.y) == (1, 2)
assert p[0] + p[1] == 3
assert p.index(1) == 0
assert Point._make([1, 3.14]).y == 3.14
def test_deque():
from collections import deque
import sys
d = deque([2])
assert list(deque([1, 2, 3])) == [1, 2, 3]
assert list(deque([1, 2, 3], 2)) == [2, 3]
assert deque([1, 2, 3]).maxlen is None
assert deque([1, 2, 3], 2).maxlen == 2
d.append(3)
d.appendleft(1)
assert list(d) == [1, 2, 3]
d.clear()
assert len(d) == 0
if sys.version_info >= (3, 5):
copy = d.copy()
assert copy == d
assert copy is not d
d.extend([1, 2, 3])
d.extendleft([5, 3, 1])
assert d.count(3) == 2
assert list(d) == [1, 3, 5, 1, 2, 3]
assert d.pop() == 3
assert d.popleft() == 1
d.remove(5)
assert list(d) == [3, 1, 2]
d.reverse()
assert list(d) == [2, 1, 3]
d.rotate(1)
assert list(d) == [3, 2, 1]
d.rotate(-2)
assert list(d) == [1, 3, 2]
assert len(d) == 3
assert 3 in d
assert d[1] == 3
d[1] = 4
assert list(d) == [1, 4, 2]
assert list(reversed(d)) == [2, 4, 1]
if sys.version_info >= (3, 5):
d.insert(len(d), 4)
assert list(d) == [1, 4, 2, 4]
assert d.index(4) == 1
assert d.index(4, 2) == 3
assert d.index(4, 2, len(d)) == 3
assert d + deque([5, 6]) == deque(list(d) + [5, 6])
assert d * 2 == deque([1, 4, 2, 4, 1, 4, 2, 4])
d *= 2
assert list(d) == [1, 4, 2, 4, 1, 4, 2, 4]
def test_chain_map():
from collections import ChainMap
ChainMap()
cm = ChainMap({"a": 1, "b": 2})
assert cm.maps is not None
assert cm.new_child().maps is not None
assert cm.new_child({"c": 3}).maps is not None
assert cm.parents.maps is not None
cm["d"] = 4
del cm["a"]
assert cm["a"] is None
assert cm["b"] == 2
assert cm["d"] == 4
assert list(iter(cm)) == ["b", "d", "c"]
assert len(cm) == 3
def test_counter():
from collections import Counter
c = Counter()
assert Counter("abc") == {"a": 1, "b": 1, "c": 1}
assert Counter({"a": 1, "b": 2, "c": 3}) == {"a": 1, "b": 2, "c": 3}
assert Counter(a=1, b=2, c=3) == {"a": 1, "b": 2, "c": 3}
c["abc"] = 1
c["def"] = 2
assert c == {"abc": 1, "def": 2}
c["def"] = 0
assert c == {"abc": 1, "def": 0}
del c["def"]
assert c["ghi"] == 0
c.update({"ghi": 2})
assert list(c.elements()) == ["abc", "ghi", "ghi"]
c.update(["a", "a", "b", "a", "a", "a", "b", "a"])
assert c == {"abc": 1, "ghi": 2, "a": 6, "b": 2}
c.update(a=-3, b=-1)
assert c == {"abc": 1, "ghi": 2, "a": 3, "b": 1}
assert c.most_common(2) == [("a", 3), ("ghi", 2)]
assert c.most_common() == [("a", 3), ("ghi", 2), ("abc", 1), ("b", 1)]
c.subtract({"abc": 1, "ghi": -2, "a": 3, "b": -1})
assert c == {"abc": 0, "ghi": 4, "a": 0, "b": 2}
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
assert c + d == Counter({'a': 4, 'b': 3})
assert c - d == Counter({'a': 2})
assert c & d == Counter({'a': 1, 'b': 1})
assert c | d == Counter({'a': 3, 'b': 2})
c = Counter(a=3, b=1)
c += d
assert c == Counter({'a': 4, 'b': 3})
c = Counter(a=3, b=1)
c -= d
assert c == Counter({'a': 2})
c = Counter(a=3, b=1)
c &= d
assert c == Counter({'a': 1, 'b': 1})
c = Counter(a=3, b=1)
c |= d
assert c == Counter({'a': 3, 'b': 2})
c = Counter(a=3, b=1)
c.subtract(["a", "b"])
assert c == {"a": 2, "b": 0}
c = Counter(a=2, b=-4)
assert +c == {"a": 2}
assert -c == {"b": 4}
def test_ordered_dict():
from collections import OrderedDict
od = OrderedDict([("a", 1), ("b", 2), ("c", 3), ("d", 4)])
assert od.popitem() == ("d", 4)
assert od == OrderedDict([("a", 1), ("b", 2), ("c", 3)])
assert od.popitem(last=False) == ("a", 1)
assert od == OrderedDict([("b", 2), ("c", 3)])
assert od.popitem(last=True) == ("c", 3)
assert od == OrderedDict([("b", 2)])
assert od == {"b": 2}
od = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
od.move_to_end("a")
assert list(od.keys()) == ["b", "c", "a"]
od.move_to_end("c", last=True)
assert list(od.keys()) == ["b", "a", "c"]
od.move_to_end("a", last=False)
assert list(od.keys()) == ["a", "b", "c"]
def test_defaultdict():
from collections import defaultdict
assert defaultdict() == {}
assert defaultdict(k1=1, k2=2) == {"k1": 1, "k2": 2}
assert defaultdict(lambda: 1) == {}
assert defaultdict(lambda: 2, {"k1": 1, "k2": 2}) == {"k1": 1, "k2": 2}
assert defaultdict(lambda: 3, [("k1", 1), ("k2", 2)]) == {"k1": 1, "k2": 2}
assert defaultdict(None) == {}
assert defaultdict(None, {"k1": 1, "k2": 2}) == {"k1": 1, "k2": 2}
assert defaultdict(None, [("k1", 1), ("k2", 2)]) == {"k1": 1, "k2": 2}
assert defaultdict(lambda: 4).__missing__("key") == 4
def test_abc():
from collections import (Container, Hashable, Iterable, Iterator, Reversible, Generator, Sized, Callable,
Collection, Sequence, MutableSequence, ByteString, Set, MutableSet, Mapping,
MutableMapping, MappingView, ItemsView, KeysView, ValuesView, Awaitable, Coroutine,
AsyncIterable, AsyncIterator, AsyncGenerator)
assert [Container, Hashable, Iterable, Iterator, Reversible, Generator, Sized, Callable,
Collection, Sequence, MutableSequence, ByteString, Set, MutableSet, Mapping,
MutableMapping, MappingView, ItemsView, KeysView, ValuesView, Awaitable, Coroutine,
AsyncIterable, AsyncIterator, AsyncGenerator]
from collections.abc import (Container, Hashable, Iterable, Iterator, Reversible, Generator, Sized, Callable,
Collection, Sequence, MutableSequence, ByteString, Set, MutableSet, Mapping,
MutableMapping, MappingView, ItemsView, KeysView, ValuesView, Awaitable, Coroutine,
AsyncIterable, AsyncIterator, AsyncGenerator)
assert [Container, Hashable, Iterable, Iterator, Reversible, Generator, Sized, Callable,
Collection, Sequence, MutableSequence, ByteString, Set, MutableSet, Mapping,
MutableMapping, MappingView, ItemsView, KeysView, ValuesView, Awaitable, Coroutine,
AsyncIterable, AsyncIterator, AsyncGenerator]
@@ -1236,4 +1236,15 @@ public class PyResolveTest extends PyResolveTestCase {
final PyFunction function = assertInstanceOf(doResolve(), PyFunction.class);
assertEquals(4, function.getTextOffset());
}
// PY-23259
public void testTypingListInheritor() {
assertResolvesTo(PyFunction.class, "append");
}
// PY-23259
public void testImportedTypingListInheritor() {
myFixture.copyDirectoryToProject("resolve/" + getTestName(false), "");
assertResolvesTo(PyFunction.class, "append");
}
}
@@ -17,10 +17,15 @@ import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.util.QualifiedName;
import com.intellij.testFramework.TestDataPath;
import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyFileImpl;
import com.jetbrains.python.psi.resolve.PyResolveImportUtil;
import com.jetbrains.python.psi.stubs.*;
import com.jetbrains.python.psi.types.PyCallableType;
import com.jetbrains.python.psi.types.PyClassType;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.toolbox.Maybe;
@@ -28,6 +33,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
/**
* @author max
@@ -464,12 +470,12 @@ public class PyStubsTest extends PyTestCase {
doTestNamedTupleArguments();
}
public void _testImportedNamedTupleName() {
doTestUnsupportedNamedTuple();
public void testImportedNamedTupleName() {
doTestUnsupportedNamedTuple(getTestFile());
}
public void _testImportedNamedTupleFields() {
doTestUnsupportedNamedTuple();
public void testImportedNamedTupleFields() {
doTestUnsupportedNamedTuple(getTestFile());
}
public void testFullyQualifiedTypingNamedTuple() {
@@ -512,12 +518,12 @@ public class PyStubsTest extends PyTestCase {
doTestTypingNamedTupleArguments();
}
public void _testImportedTypingNamedTupleName() {
doTestUnsupportedNamedTuple();
public void testImportedTypingNamedTupleName() {
doTestUnsupportedTypingNamedTuple(getTestFile());
}
public void _testImportedTypingNamedTupleFields() {
doTestUnsupportedNamedTuple();
public void testImportedTypingNamedTupleFields() {
doTestUnsupportedTypingNamedTuple(getTestFile());
}
public void testFullyQualifiedTypingNamedTupleKwargs() {
@@ -552,12 +558,12 @@ public class PyStubsTest extends PyTestCase {
doTestTypingNamedTupleArguments();
}
public void _testImportedTypingNamedTupleKwargsName() {
doTestUnsupportedNamedTuple();
public void testImportedTypingNamedTupleKwargsName() {
doTestUnsupportedTypingNamedTuple(getTestFile());
}
public void _testImportedTypingNamedTupleKwargsFields() {
doTestUnsupportedNamedTuple();
public void testImportedTypingNamedTupleKwargsFields() {
doTestUnsupportedTypingNamedTuple(getTestFile());
}
private void doTestNamedTuple(@NotNull QualifiedName expectedCalleeName) {
@@ -600,7 +606,32 @@ public class PyStubsTest extends PyTestCase {
doTestNamedTuple(expectedName, expectedFieldsNames, expectedFieldsTypes, typeFromAst);
}
private void doTestUnsupportedNamedTuple() {
private void doTestUnsupportedNamedTuple(@NotNull PsiElement anchor) {
doTestUnsupportedNT(
(typeFromAst, context) -> {
assertInstanceOf(typeFromAst, PyCallableType.class);
final PyType returnType = ((PyCallableType)typeFromAst).getReturnType(context);
assertEquals(PyBuiltinCache.getInstance(anchor).getTupleType(), returnType);
}
);
}
private void doTestUnsupportedTypingNamedTuple(@NotNull PsiElement anchor) {
final QualifiedName typingNTName = QualifiedName.fromDottedString(PyTypingTypeProvider.NAMEDTUPLE);
final PsiElement member = PyResolveImportUtil.resolveTopLevelMember(typingNTName, PyResolveImportUtil.fromFoothold(anchor));
assertInstanceOf(member, PyClass.class);
doTestUnsupportedNT(
(typeFromAst, context) -> {
assertInstanceOf(typeFromAst, PyClassType.class);
assertEquals(member, ((PyClassType)typeFromAst).getPyClass());
}
);
}
private void doTestUnsupportedNT(@NotNull BiConsumer<PyType, TypeEvalContext> typeFromAstChecker) {
final PyFile file = getTestFile();
final PyTargetExpression attribute = file.findTopLevelAttribute("nt");
@@ -613,8 +644,8 @@ public class PyStubsTest extends PyTestCase {
final FileASTNode astNode = file.getNode();
assertNotNull(astNode);
final PyType typeFromAst = TypeEvalContext.userInitiated(myFixture.getProject(), file).getType(attribute);
assertNull(typeFromAst);
final TypeEvalContext context = TypeEvalContext.userInitiated(myFixture.getProject(), file);
typeFromAstChecker.accept(context.getType(attribute), context);
}
private static void doTestNamedTuple(@NotNull String expectedName,
@@ -1410,8 +1410,7 @@ public class PyTypeTest extends PyTestCase {
}
// PY-20797
// TODO: Enable after switching to collections stub from Typeshed
public void _testValueOfEmptyDefaultDict() {
public void testValueOfEmptyDefaultDict() {
doTest("list",
"from collections import defaultdict\n" +
"expr = defaultdict(lambda: [])['x']\n");