blob: 9513795cb5b7de1f678b2f325546c5465a5c73f5 [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
package com.jetbrains.python;
import com.intellij.testFramework.LightProjectDescriptor;
import com.jetbrains.python.documentation.PythonDocumentationProvider;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Tests for a type system based on mypy's typing module.
*
* @author vlan
*/
public class PyTypingTest extends PyTestCase {
@Nullable
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return ourPy3Descriptor;
}
@Override
public void setUp() throws Exception {
super.setUp();
setLanguageLevel(LanguageLevel.PYTHON32);
}
@Override
public void tearDown() throws Exception {
setLanguageLevel(null);
super.tearDown();
}
public void testClassType() {
doTest("Foo",
"class Foo:" +
" pass\n" +
"\n" +
"def f(expr: Foo):\n" +
" pass\n");
}
public void testClassReturnType() {
doTest("Foo",
"class Foo:" +
" pass\n" +
"\n" +
"def f() -> Foo:\n" +
" pass\n" +
"\n" +
"expr = f()\n");
}
public void testNoneType() {
doTest("None",
"def f(expr: None):\n" +
" pass\n");
}
public void testNoneReturnType() {
doTest("None",
"def f() -> None:\n" +
" return 0\n" +
"expr = f()\n");
}
public void testUnionType() {
doTest("int | str",
"from typing import Union\n" +
"\n" +
"def f(expr: Union[int, str]):\n" +
" pass\n");
}
public void testBuiltinList() {
doTest("list",
"from typing import List\n" +
"\n" +
"def f(expr: List):\n" +
" pass\n");
}
public void testBuiltinListWithParameter() {
doTest("list[int]",
"from typing import List\n" +
"\n" +
"def f(expr: List[int]):\n" +
" pass\n");
}
public void testBuiltinDictWithParameters() {
doTest("dict[str, int]",
"from typing import Dict\n" +
"\n" +
"def f(expr: Dict[str, int]):\n" +
" pass\n");
}
public void testBuiltinTuple() {
doTest("tuple",
"from typing import Tuple\n" +
"\n" +
"def f(expr: Tuple):\n" +
" pass\n");
}
public void testBuiltinTupleWithParameters() {
doTest("(int, str)",
"from typing import Tuple\n" +
"\n" +
"def f(expr: Tuple[int, str]):\n" +
" pass\n");
}
public void testAnyType() {
doTest("unknown",
"from typing import Any\n" +
"\n" +
"def f(expr: Any):\n" +
" pass\n");
}
public void testGenericType() {
doTest("A",
"from typing import typevar\n" +
"\n" +
"T = typevar('A')\n" +
"\n" +
"def f(expr: T):\n" +
" pass\n");
}
public void testGenericBoundedType() {
doTest("T <= int | str",
"from typing import typevar\n" +
"\n" +
"T = typevar('T', values=(int, str))\n" +
"\n" +
"def f(expr: T):\n" +
" pass\n");
}
public void testParameterizedClass() {
doTest("C[int]",
"from typing import Generic, typevar\n" +
"\n" +
"T = typevar('T')\n" +
"\n" +
"class C(Generic[T]):\n" +
" def __init__(self, x: T):\n" +
" pass\n" +
"\n" +
"expr = C(10)\n");
}
public void testParameterizedClassMethod() {
doTest("int",
"from typing import Generic, typevar\n" +
"\n" +
"T = typevar('T')\n" +
"\n" +
"class C(Generic[T]):\n" +
" def __init__(self, x: T):\n" +
" pass\n" +
" def foo(self) -> T:\n" +
" pass\n" +
"\n" +
"expr = C(10).foo()\n");
}
public void testParameterizedClassInheritance() {
doTest("int",
"from typing import Generic, typevar\n" +
"\n" +
"T = typevar('T')\n" +
"\n" +
"class B(Generic[T]):\n" +
" def foo(self) -> T:\n" +
" pass\n" +
"class C(B[T]):\n" +
" def __init__(self, x: T):\n" +
" pass\n" +
"\n" +
"expr = C(10).foo()\n");
}
public void testAnyStrUnification() {
doTest("bytes",
"from typing import AnyStr\n" +
"\n" +
"def foo(x: AnyStr) -> AnyStr:\n" +
" pass\n" +
"\n" +
"expr = foo(b'bar')\n");
}
public void testAnyStrForUnknown() {
doTest("str | bytes",
"from typing import AnyStr\n" +
"\n" +
"def foo(x: AnyStr) -> AnyStr:\n" +
" pass\n" +
"\n" +
"def bar(x):\n" +
" expr = foo(x)\n");
}
public void testFunctionType() {
doTest("(int, str) -> str",
"from typing import Function\n" +
"\n" +
"def foo(expr: Function[[int, str], str]):\n" +
" pass\n");
}
public void testTypeInStringLiteral() {
doTest("C",
"class C:\n" +
" def foo(self, expr: 'C'):\n" +
" pass\n");
}
public void testQualifiedTypeInStringLiteral() {
doTest("str",
"import typing\n" +
"\n" +
"def foo(x: 'typing.AnyStr') -> typing.AnyStr:\n" +
" pass\n" +
"\n" +
"expr = foo('bar')\n");
}
private void doTest(@NotNull String expectedType, @NotNull String text) {
myFixture.copyDirectoryToProject("typing", "");
myFixture.configureByText(PythonFileType.INSTANCE, text);
final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);
final TypeEvalContext codeAnalysis = TypeEvalContext.codeAnalysis(expr.getContainingFile());
final TypeEvalContext userInitiated = TypeEvalContext.userInitiated(expr.getContainingFile()).withTracing();
assertType(expectedType, expr, codeAnalysis, "code analysis");
assertType(expectedType, expr, userInitiated, "user initiated");
}
private static void assertType(String expectedType, PyExpression expr, TypeEvalContext context, String contextName) {
final PyType actual = context.getType(expr);
final String actualType = PythonDocumentationProvider.getTypeName(actual, context);
assertEquals("Failed in " + contextName + " context", expectedType, actualType);
}
}