mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Initial type checker inspection for Python
This commit is contained in:
@@ -1,14 +1,71 @@
|
||||
package com.jetbrains.python.codeInsight.stdlib;
|
||||
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.jetbrains.python.documentation.StructuredDocString;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.PyTypeParser;
|
||||
import com.jetbrains.python.psi.types.PyTypeProviderBase;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyStdlibTypeProvider extends PyTypeProviderBase {
|
||||
private Properties myStdlibTypes = null;
|
||||
private Project myProject = null;
|
||||
private Map<String, PyType> myTypeCache = new HashMap<String, PyType>();
|
||||
|
||||
// TODO: Different databases for various Python versions (2.6, 2.7, 3.2, etc.)
|
||||
|
||||
@Override
|
||||
public PyType getReturnType(PyFunction function, @Nullable PyReferenceExpression callSite, TypeEvalContext context) {
|
||||
final String qname = getQualifiedName(function, callSite);
|
||||
final String key = String.format("%s.return", qname);
|
||||
final PyType cached = getCachedType(function.getProject(), key);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
final StructuredDocString docString = getStructuredDocString(qname);
|
||||
if (docString == null) {
|
||||
return null;
|
||||
}
|
||||
final String s = docString.getReturnType();
|
||||
final PyType result = PyTypeParser.getTypeByName(function, s);
|
||||
myTypeCache.put(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyType getParameterType(PyNamedParameter param, PyFunction func, TypeEvalContext context) {
|
||||
final String name = param.getName();
|
||||
final String qname = getQualifiedName(func, param);
|
||||
final String key = String.format("%s.%s", qname, name);
|
||||
final PyType cached = getCachedType(param.getProject(), key);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
final StructuredDocString docString = getStructuredDocString(qname);
|
||||
if (docString == null) {
|
||||
return null;
|
||||
}
|
||||
final String s = docString.getParamType(name);
|
||||
final PyType result = PyTypeParser.getTypeByName(func, s);
|
||||
myTypeCache.put(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyType getIterationType(PyClass iterable) {
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(iterable);
|
||||
@@ -19,4 +76,45 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private StructuredDocString getStructuredDocString(String qualifiedName) {
|
||||
final Properties db = getStdlibTypes();
|
||||
final String docString = db.getProperty(qualifiedName);
|
||||
return StructuredDocString.parse(docString);
|
||||
}
|
||||
|
||||
private static String getQualifiedName(PyFunction f, PsiElement callSite) {
|
||||
String result = f.getName();
|
||||
final PyClass c = f.getContainingClass();
|
||||
final VirtualFile vfile = f.getContainingFile().getVirtualFile();
|
||||
if (vfile != null) {
|
||||
final String module = ResolveImportUtil.findShortestImportableName(callSite != null ? callSite : f, vfile);
|
||||
result = String.format("%s.%s%s",
|
||||
module,
|
||||
c != null ? c.getName() + "." : "",
|
||||
result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PyType getCachedType(Project project, String key) {
|
||||
if (project != myProject) {
|
||||
myProject = project;
|
||||
myTypeCache.clear();
|
||||
}
|
||||
return myTypeCache.get(key);
|
||||
}
|
||||
|
||||
private Properties getStdlibTypes() {
|
||||
if (myStdlibTypes == null) {
|
||||
myStdlibTypes = new Properties();
|
||||
InputStream s = getClass().getResourceAsStream("StdlibTypes.properties");
|
||||
try {
|
||||
myStdlibTypes.load(s);
|
||||
}
|
||||
catch (IOException ignored) {}
|
||||
}
|
||||
return myStdlibTypes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
# Python 2.7 stdlib
|
||||
|
||||
|
||||
## 5.4. Numeric types
|
||||
|
||||
__builtin__.abs = \
|
||||
:type number: bool or int or long or float or complex \n\
|
||||
:rtype: int \n\
|
||||
|
||||
__builtin__.int.__init__ = \
|
||||
:type x: object \n\
|
||||
:type base: bool or int or long or float or complex or None \n\
|
||||
:rtype: int \n\
|
||||
|
||||
__builtin__.long.__init__ = \
|
||||
:type x: object \n\
|
||||
:type base: bool or int or long or float or complex or None \n\
|
||||
:rtype: long \n\
|
||||
|
||||
__builtin__.float.__init__ = \
|
||||
:type x: object \n\
|
||||
:rtype: float \n\
|
||||
|
||||
__builtin__.complex.__init__ = \
|
||||
:type real: object \n\
|
||||
:type imag: object or None \n\
|
||||
:rtype: complex \n\
|
||||
|
||||
__builtin__.divmod = \
|
||||
:type x: bool or int or long or float or complex \n\
|
||||
:type y: bool or int or long or float or complex \n\
|
||||
:rtype: (int or long or float or complex, int or long or float or complex) \n\
|
||||
|
||||
__builtin__.pow = \
|
||||
:type x: bool or int or long or float or complex \n\
|
||||
:type y: bool or int or long or float or complex \n\
|
||||
:rtype: int or long or float or complex \n\
|
||||
|
||||
__builtin__.round = \
|
||||
:type number: int or long or float \n\
|
||||
:type ndigits: bool or int or long or float or None \n\
|
||||
:rtype: float \n\
|
||||
|
||||
|
||||
## 5.6. Sequence types
|
||||
|
||||
__builtin__.str.capitalize = \
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.center = \
|
||||
:type width: int or long \n\
|
||||
:type fillchar: bytes or None \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.count = \
|
||||
:type sub: bytes \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.str.decode = \
|
||||
:type encoding: unicode or bytes \n\
|
||||
:type errors: unicode or bytes or None \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.str.encode = \
|
||||
:type encoding: unicode or bytes \n\
|
||||
:type errors: unicode or bytes or None \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.endswith = \
|
||||
:type suffix: bytes \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.find = \
|
||||
:type sub: bytes \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.str.index = \
|
||||
:type sub: bytes \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.str.isalnum = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.isalpha = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.isdigit = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.islower = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.isspace = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.istitle = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.isupper = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.join = \
|
||||
:type iterable: object \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.ljust = \
|
||||
:type width: int or long \n\
|
||||
:type fillchar: bytes or None \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.lower = \
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.lstrip = \
|
||||
:type chars: bytes or None \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.partition = \
|
||||
:type sep: bytes \n\
|
||||
:rtype: (bytes, bytes, bytes) \n\
|
||||
|
||||
__builtin__.str.replace = \
|
||||
:type old: bytes \n\
|
||||
:type new: bytes \n\
|
||||
:type count: int or long \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.rfind = \
|
||||
:type sub: bytes \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.str.rindex = \
|
||||
:type sub: bytes \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.str.rpartition = \
|
||||
:type sep: bytes \n\
|
||||
:rtype: (bytes, bytes, bytes) \n\
|
||||
|
||||
__builtin__.str.rsplit = \
|
||||
:type sep: bytes or None \n\
|
||||
:type maxsplit: int or long or None \n\
|
||||
:rtype: list of bytes \n\
|
||||
|
||||
__builtin__.str.rstrip = \
|
||||
:type chars: bytes or None \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.split = \
|
||||
:type sep: bytes or None \n\
|
||||
:type maxsplit: int or long or None \n\
|
||||
:rtype: list of bytes \n\
|
||||
|
||||
__builtin__.str.splitlines = \
|
||||
:type keepends: bool or int or long or None \n\
|
||||
:rtype: list of bytes \n\
|
||||
|
||||
__builtin__.str.startswith = \
|
||||
:type prefix: bytes \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.str.strip = \
|
||||
:type chars: bytes or None \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.swapcase = \
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.title = \
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.translate = \
|
||||
:type table: object \n\
|
||||
:type deletechars: bytes \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.upper = \
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.str.zfill = \
|
||||
:type width: int or long \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.unicode.capitalize = \
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.center = \
|
||||
:type width: int or long \n\
|
||||
:type fillchar: unicode or None \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.count = \
|
||||
:type sub: unicode \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.unicode.decode = \
|
||||
:type encoding: unicode or bytes \n\
|
||||
:type errors: unicode or bytes or None \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.encode = \
|
||||
:type encoding: unicode or bytes \n\
|
||||
:type errors: unicode or bytes or None \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
__builtin__.unicode.endswith = \
|
||||
:type suffix: unicode \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.find = \
|
||||
:type sub: unicode \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.unicode.index = \
|
||||
:type sub: unicode \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.unicode.isalnum = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.isalpha = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.isdecimal = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.isdigit = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.islower = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.isnumeric = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.isspace = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.istitle = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.isupper = \
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.join = \
|
||||
:type iterable: object \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.ljust = \
|
||||
:type width: int or long \n\
|
||||
:type fillchar: unicode or None \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.lower = \
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.lstrip = \
|
||||
:type chars: unicode or None \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.partition = \
|
||||
:type sep: unicode \n\
|
||||
:rtype: (unicode, unicode, unicode) \n\
|
||||
|
||||
__builtin__.unicode.replace = \
|
||||
:type old: unicode \n\
|
||||
:type new: unicode \n\
|
||||
:type count: int or long \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.rfind = \
|
||||
:type sub: unicode \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.unicode.rindex = \
|
||||
:type sub: unicode \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: int or long \n\
|
||||
|
||||
__builtin__.unicode.rpartition = \
|
||||
:type sep: unicode \n\
|
||||
:rtype: (unicode, unicode, unicode) \n\
|
||||
|
||||
__builtin__.unicode.rsplit = \
|
||||
:type sep: unicode or None \n\
|
||||
:type maxsplit: int or long or None \n\
|
||||
:rtype: list of unicode \n\
|
||||
|
||||
__builtin__.unicode.rstrip = \
|
||||
:type chars: unicode or None \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.split = \
|
||||
:type sep: unicode or None \n\
|
||||
:type maxsplit: int or long or None \n\
|
||||
:rtype: list of unicode \n\
|
||||
|
||||
__builtin__.unicode.splitlines = \
|
||||
:type keepends: bool or int or long or None \n\
|
||||
:rtype: list of unicode \n\
|
||||
|
||||
__builtin__.unicode.startswith = \
|
||||
:type prefix: unicode \n\
|
||||
:type start: int or long or None \n\
|
||||
:type end: int or long or None \n\
|
||||
:rtype: bool \n\
|
||||
|
||||
__builtin__.unicode.strip = \
|
||||
:type chars: unicode or None \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.swapcase = \
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.title = \
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.translate = \
|
||||
:type table: object \n\
|
||||
:type deletechars: unicode \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.upper = \
|
||||
:rtype: unicode \n\
|
||||
|
||||
__builtin__.unicode.zfill = \
|
||||
:type width: int or long \n\
|
||||
:rtype: unicode \n\
|
||||
|
||||
|
||||
## 15.2. io
|
||||
|
||||
__builtin__.open = \
|
||||
:type name: unicode or bytes \n\
|
||||
:type mode: unicode or bytes \n\
|
||||
:type buffering: int \n\
|
||||
:type encoding: unicode or None \n\
|
||||
:type errors: unicode or None \n\
|
||||
:rtype: file \n\
|
||||
|
||||
__builtin__.file.read = \
|
||||
:type size: int or None \n\
|
||||
:rtype: unknown \n\
|
||||
|
||||
__builtin__.file.write = \
|
||||
:type p_str: unicode or bytes \n\
|
||||
:rtype: None \n\
|
||||
|
||||
|
||||
## 18.2. json
|
||||
|
||||
json.loads = \
|
||||
:type s: unicode or bytes \n\
|
||||
:type encoding: unicode or bytes \n\
|
||||
:rtype: object \n\
|
||||
|
||||
|
||||
## 18.12. base64
|
||||
|
||||
base64.b64encode = \
|
||||
:type s: bytes \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
base64.b64decode = \
|
||||
:type s: bytes \n\
|
||||
:rtype: bytes \n\
|
||||
|
||||
|
||||
# DEBUG: Experimental
|
||||
|
||||
__builtin__._open_bytes = \
|
||||
:type name: unicode or bytes \n\
|
||||
:type mode: unicode or bytes \n\
|
||||
:type buffering: int \n\
|
||||
:type encoding: unicode or None \n\
|
||||
:type errors: unicode or None \n\
|
||||
:rtype: _io.FileIO \n\
|
||||
|
||||
__builtin__._open_unicode = \
|
||||
:type name: unicode or bytes \n\
|
||||
:type mode: unicode or bytes \n\
|
||||
:type buffering: int \n\
|
||||
:type encoding: unicode or None \n\
|
||||
:type errors: unicode or None \n\
|
||||
:rtype: _io.TextIOWrapper \n\
|
||||
@@ -10,9 +10,11 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.console.PydevConsoleRunner;
|
||||
import com.jetbrains.python.console.PydevDocumentationProvider;
|
||||
@@ -20,6 +22,7 @@ import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
import com.jetbrains.python.psi.types.PyCollectionType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.toolbox.ChainIterable;
|
||||
@@ -27,6 +30,7 @@ import com.jetbrains.python.toolbox.FP;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.methods.HeadMethod;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -85,15 +89,48 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
|
||||
final String name = fun.getName();
|
||||
cat.add("def ").addWith(func_name_wrapper, $(name));
|
||||
cat.add(escaper.apply(PyUtil.getReadableRepr(fun.getParameterList(), false)));
|
||||
if (!PyNames.INIT.equals(name) && !specifiesReturnType(fun.getDocStringExpression())) {
|
||||
final PyType returnType = fun.getReturnType(TypeEvalContext.slow(), null);
|
||||
cat.add(escaper.apply("\nInferred return type: "));
|
||||
if (returnType == null) cat.add("unknown");
|
||||
else cat.add(returnType.getName());
|
||||
if (!PyNames.INIT.equals(name)) {
|
||||
cat.add(escaper.apply("\nInferred type: "));
|
||||
cat.add(escaper.apply(getTypeDescription(fun)));
|
||||
}
|
||||
return cat;
|
||||
}
|
||||
|
||||
public static String getTypeDescription(@NotNull PyFunction fun) {
|
||||
final String UNKNOWN = "unknown";
|
||||
final TypeEvalContext context = TypeEvalContext.slow();
|
||||
final PyType returnType = fun.getReturnType(context, null);
|
||||
return String.format("(%s) -> %s\n",
|
||||
StringUtil.join(fun.getParameterList().getParameters(),
|
||||
new Function<PyParameter, String>() {
|
||||
@Override
|
||||
public String fun(PyParameter p) {
|
||||
final PyNamedParameter np = p.getAsNamed();
|
||||
if (np != null) {
|
||||
String name = UNKNOWN;
|
||||
final PyType t = np.getType(context);
|
||||
if (t != null) {
|
||||
name = getTypeName(t, context);
|
||||
}
|
||||
return String.format("%s: %s", np.getName(), name);
|
||||
}
|
||||
return p.toString();
|
||||
}
|
||||
}, ", "),
|
||||
returnType != null ? getTypeName(returnType, context) : UNKNOWN);
|
||||
}
|
||||
|
||||
public static String getTypeName(@NotNull PyType type, @NotNull TypeEvalContext context) {
|
||||
final String name = type.getName();
|
||||
if (type instanceof PyCollectionType) {
|
||||
final PyType elementType = ((PyCollectionType)type).getElementType(context);
|
||||
if (elementType != null) {
|
||||
return String.format("%s of %s", name, elementType.getName());
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
static ChainIterable<String> describeDecorators(
|
||||
PyDecoratable what, FP.Lambda1<Iterable<String>, Iterable<String>> deco_name_wrapper,
|
||||
String deco_separator, FP.Lambda1<String, String> escaper
|
||||
|
||||
@@ -23,7 +23,7 @@ public abstract class StructuredDocString {
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
if (text.contains(":param ") || text.contains(":rtype ") || text.contains(":type ")) {
|
||||
if (text.contains(":param ") || text.contains(":rtype") || text.contains(":type ")) {
|
||||
return new SphinxDocString(text);
|
||||
}
|
||||
return new EpydocString(text);
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.ResolveResult;
|
||||
import com.jetbrains.python.documentation.PythonDocumentationProvider;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.ImplicitResolveResult;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyTypeCheckerInspection extends PyInspection {
|
||||
private static final Logger LOG = Logger.getInstance(PyTypeCheckerInspection.class.getName());
|
||||
private static Key<Long> TIME_KEY = Key.create("PyTypeCheckerInspection.StartTime");
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
session.putUserData(TIME_KEY, System.nanoTime());
|
||||
}
|
||||
return new PyInspectionVisitor(holder) {
|
||||
// TODO: Show types in tooltips for variables
|
||||
// TODO: Visit decorators with arguments
|
||||
// TODO: Visit operators (requires resolve() for operators)
|
||||
@Override
|
||||
public void visitPyCallExpression(PyCallExpression node) {
|
||||
List<PyFunction> functions = new ArrayList<PyFunction>();
|
||||
final PyExpression callee = node.getCallee();
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
ResolveResult[] results = ((PyReferenceExpression)callee).getReference().multiResolve(false);
|
||||
for (ResolveResult result : results) {
|
||||
if (!(result instanceof ImplicitResolveResult)) {
|
||||
PsiElement e = result.getElement();
|
||||
if (e instanceof PyFunction) {
|
||||
functions.add((PyFunction)e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!functions.isEmpty()) {
|
||||
PyFunction fun = functions.get(0);
|
||||
final TypeEvalContext fastContext = TypeEvalContext.fast();
|
||||
final TypeEvalContext slowContext = TypeEvalContext.slow();
|
||||
final TypeEvalContext context = fun.getContainingFile() == node.getContainingFile() ?
|
||||
slowContext : fastContext;
|
||||
final PyArgumentList args = node.getArgumentList();
|
||||
if (args != null) {
|
||||
final PyArgumentList.AnalysisResult res = args.analyzeCall(context);
|
||||
final Map<PyExpression, PyNamedParameter> mapped = res.getPlainMappedParams();
|
||||
for (Map.Entry<PyExpression, PyNamedParameter> entry : mapped.entrySet()) {
|
||||
final PyNamedParameter p = entry.getValue();
|
||||
if (p.isPositionalContainer() || p.isKeywordContainer()) {
|
||||
// TODO: Support *args, **kwargs
|
||||
continue;
|
||||
}
|
||||
final PyType argType = entry.getKey().getType(slowContext);
|
||||
final PyType paramType = p.getType(context);
|
||||
if (argType != null && paramType != null) {
|
||||
if (!match(paramType, argType, context)) {
|
||||
registerProblem(entry.getKey(), String.format("Expected type '%s', got '%s' instead",
|
||||
PythonDocumentationProvider.getTypeName(paramType, context),
|
||||
PythonDocumentationProvider.getTypeName(argType, slowContext)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean match(PyType superType, PyType subType, TypeEvalContext context) {
|
||||
// TODO: subscriptable types?, module types?, etc.
|
||||
if (superType == null || subType == null) {
|
||||
return true;
|
||||
}
|
||||
if (superType instanceof PyUnionType) {
|
||||
for (PyType t : ((PyUnionType)superType).getMembers()) {
|
||||
if (match(t, subType, context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (subType instanceof PyUnionType) {
|
||||
for (PyType t : ((PyUnionType)subType).getMembers()) {
|
||||
if (!match(superType, t, context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (superType instanceof PyClassType && subType instanceof PyClassType) {
|
||||
final PyClass superClass = ((PyClassType)superType).getPyClass();
|
||||
final PyClass subClass = ((PyClassType)subType).getPyClass();
|
||||
if (superType instanceof PyCollectionType && subType instanceof PyCollectionType) {
|
||||
if (!matchClasses(superClass, subClass)) {
|
||||
return false;
|
||||
}
|
||||
final PyType superElementType = ((PyCollectionType)superType).getElementType(context);
|
||||
final PyType subElementType = ((PyCollectionType)subType).getElementType(context);
|
||||
return match(superElementType, subElementType, context);
|
||||
}
|
||||
else if (superType instanceof PyTupleType && subType instanceof PyTupleType) {
|
||||
final PyTupleType superTupleType = (PyTupleType)superType;
|
||||
final PyTupleType subTupleType = (PyTupleType)subType;
|
||||
if (superTupleType.getElementCount() != subTupleType.getElementCount()) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < superTupleType.getElementCount(); i++) {
|
||||
if (!match(superTupleType.getElementType(i), subTupleType.getElementType(i), context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (matchClasses(superClass, subClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (superType.equals(subType)) {
|
||||
return true;
|
||||
}
|
||||
final String superName = superType.getName();
|
||||
final String subName = subType.getName();
|
||||
// TODO: No inheritance check for builtin numerics at this moment
|
||||
final boolean subIsBool = "bool".equals(subName);
|
||||
final boolean subIsInt = "int".equals(subName);
|
||||
final boolean subIsLong = "long".equals(subName);
|
||||
final boolean subIsFloat = "float".equals(subName);
|
||||
if (superName == null || subName == null ||
|
||||
superName.equals(subName) ||
|
||||
("int".equals(superName) && subIsBool) ||
|
||||
("long".equals(superName) && (subIsBool || subIsInt)) ||
|
||||
("float".equals(superName) && (subIsBool || subIsInt || subIsLong)) ||
|
||||
("complex".equals(superName) && (subIsBool || subIsInt || subIsLong || subIsFloat))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inspectionFinished(LocalInspectionToolSession session, ProblemsHolder problemsHolder) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
final Long startTime = session.getUserData(TIME_KEY);
|
||||
if (startTime != null) {
|
||||
LOG.debug(String.format("[%d] elapsed time: %d ms\n",
|
||||
Thread.currentThread().getId(),
|
||||
(System.nanoTime() - startTime) / 1000000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return "Type checker";
|
||||
}
|
||||
|
||||
private static boolean matchClasses(@Nullable PyClass superClass, @Nullable PyClass subClass) {
|
||||
if (superClass == null || subClass == null || subClass.isSubclass(superClass)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
final String superName = superClass.getName();
|
||||
return superName != null && superName.equals(subClass.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ public class PythonInspectionToolProvider implements InspectionToolProvider {
|
||||
PyArgumentEqualDefaultInspection.class,
|
||||
PySetFunctionToLiteralInspection.class,
|
||||
PyDecoratorInspection.class,
|
||||
PyTypeCheckerInspection.class,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public interface PyYieldExpression extends PyExpression {
|
||||
@Nullable
|
||||
PyExpression getExpression();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileSystemItem;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PySequenceExpression;
|
||||
@@ -164,7 +165,6 @@ public class PyBuiltinCache {
|
||||
/**
|
||||
* Looks for a top-level named item. (Package builtins does not contain any sensible nested names anyway.)
|
||||
* @param name to look for
|
||||
* @param type to look for and cast to (most often, PyFunction or PyClass)
|
||||
* @return found element, or null.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -187,7 +187,7 @@ public class PyBuiltinCache {
|
||||
* Stores the most often used types, returned by getNNNType().
|
||||
*/
|
||||
private final Map<String,PyClassType> myTypeCache = new HashMap<String, PyClassType>();
|
||||
|
||||
|
||||
/**
|
||||
@return
|
||||
*/
|
||||
@@ -249,6 +249,26 @@ public class PyBuiltinCache {
|
||||
return getObjectType("str");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyClassType getBytesType(LanguageLevel level) {
|
||||
if (level.isPy3K()) {
|
||||
return getObjectType("bytes");
|
||||
}
|
||||
else {
|
||||
return getObjectType("str");
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyType getStringType(LanguageLevel level) {
|
||||
if (level.isPy3K()) {
|
||||
return getObjectType("str");
|
||||
}
|
||||
else {
|
||||
return getObjectType("unicode");
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyClassType getBoolType() {
|
||||
return getObjectType("bool");
|
||||
|
||||
@@ -30,8 +30,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Implements PyFunction.
|
||||
@@ -127,27 +126,63 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
|
||||
return new PyClassType(pyClass, false);
|
||||
}
|
||||
}
|
||||
|
||||
for(PyTypeProvider typeProvider: Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
|
||||
final PyType returnType = typeProvider.getReturnType(this, callSite, typeEvalContext);
|
||||
if (returnType != null) {
|
||||
return returnType;
|
||||
}
|
||||
}
|
||||
|
||||
final PyType docStringType = getReturnTypeFromDocString();
|
||||
if (docStringType != null) {
|
||||
return docStringType;
|
||||
}
|
||||
}
|
||||
if (typeEvalContext.allowReturnTypes()) {
|
||||
final PyType yieldType = getYieldStatementType(typeEvalContext);
|
||||
if (yieldType != null) {
|
||||
return yieldType;
|
||||
}
|
||||
return getReturnStatementType(typeEvalContext);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PyType getYieldStatementType(@NotNull final TypeEvalContext context) {
|
||||
PyType elementType = null;
|
||||
final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
|
||||
final PyClass listClass = cache.getClass("list");
|
||||
final PyStatementList statements = getStatementList();
|
||||
final Set<PyType> types = new HashSet<PyType>();
|
||||
if (statements != null && listClass != null) {
|
||||
statements.accept(new PyRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitPyYieldExpression(PyYieldExpression node) {
|
||||
PyType t = node.getType(context);
|
||||
if (t != null) {
|
||||
types.add(t);
|
||||
}
|
||||
else {
|
||||
types.add(cache.getObjectType());
|
||||
}
|
||||
}
|
||||
});
|
||||
final int n = types.size();
|
||||
if (n == 1) {
|
||||
elementType = types.iterator().next();
|
||||
}
|
||||
else if (n > 0) {
|
||||
elementType = new PyUnionType(types);
|
||||
}
|
||||
}
|
||||
if (elementType != null) {
|
||||
return new PyCollectionTypeImpl(listClass, false, elementType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyType getReturnStatementType(TypeEvalContext typeEvalContext) {
|
||||
ReturnVisitor visitor = new ReturnVisitor(typeEvalContext);
|
||||
ReturnVisitor visitor = new ReturnVisitor(this, typeEvalContext);
|
||||
final PyStatementList statements = getStatementList();
|
||||
if (statements != null) {
|
||||
statements.accept(visitor);
|
||||
@@ -224,30 +259,34 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
|
||||
}
|
||||
|
||||
private static class ReturnVisitor extends PyRecursiveElementVisitor {
|
||||
private final PyFunction myFunction;
|
||||
private final TypeEvalContext myContext;
|
||||
private PyType myResult = null;
|
||||
private boolean myHasReturns = false;
|
||||
|
||||
public ReturnVisitor(final TypeEvalContext context) {
|
||||
public ReturnVisitor(PyFunction function, final TypeEvalContext context) {
|
||||
myFunction = function;
|
||||
myContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyReturnStatement(PyReturnStatement node) {
|
||||
final PyExpression expr = node.getExpression();
|
||||
PyType returnType;
|
||||
returnType = expr == null ? PyNoneType.INSTANCE : myContext.getType(expr);
|
||||
if (!myHasReturns) {
|
||||
myResult = returnType;
|
||||
myHasReturns = true;
|
||||
}
|
||||
else {
|
||||
if (myResult == null) {
|
||||
if (PsiTreeUtil.getParentOfType(node, ScopeOwner.class, true) == myFunction) {
|
||||
final PyExpression expr = node.getExpression();
|
||||
PyType returnType;
|
||||
returnType = expr == null ? PyNoneType.INSTANCE : myContext.getType(expr);
|
||||
if (!myHasReturns) {
|
||||
myResult = returnType;
|
||||
myHasReturns = true;
|
||||
}
|
||||
else {
|
||||
if (returnType != null) {
|
||||
myResult = PyUnionType.union(myResult, returnType);
|
||||
if (myResult == null) {
|
||||
myResult = returnType;
|
||||
}
|
||||
else {
|
||||
if (returnType != null) {
|
||||
myResult = PyUnionType.union(myResult, returnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,12 @@ public class PyKeywordArgumentImpl extends PyElementImpl implements PyKeywordArg
|
||||
return node != null ? node.getText() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTNode getKeywordNode() {
|
||||
return getNode().findChildByType(PyTokenTypes.IDENTIFIER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyExpression getValueExpression() {
|
||||
return PsiTreeUtil.getChildOfType(this, PyExpression.class);
|
||||
}
|
||||
@@ -38,6 +40,7 @@ public class PyKeywordArgumentImpl extends PyElementImpl implements PyKeywordArg
|
||||
}
|
||||
|
||||
public PyType getType(@NotNull TypeEvalContext context) {
|
||||
return null;
|
||||
final PyExpression e = getValueExpression();
|
||||
return e != null ? e.getType(context) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.psi.PyElementVisitor;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyYieldExpression;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
@@ -19,7 +21,14 @@ public class PyYieldExpressionImpl extends PyElementImpl implements PyYieldExpre
|
||||
pyVisitor.visitPyYieldExpression(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyExpression getExpression() {
|
||||
return childToPsi(PyElementTypes.EXPRESSIONS, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyType getType(@NotNull TypeEvalContext context) {
|
||||
return null;
|
||||
final PyExpression e = getExpression();
|
||||
return e != null ? e.getType(context) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.jetbrains.python.psi.types;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PySequenceExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
@@ -17,7 +18,7 @@ public class PyLiteralCollectionType extends PyClassType implements PyCollection
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyType getElementType(TypeEvalContext context) {
|
||||
public PyType getElementType(@NotNull TypeEvalContext context) {
|
||||
final PyExpression[] elements = mySequence.getElements();
|
||||
if (elements.length == 0 || elements.length > 10 /* performance */) {
|
||||
return null;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
@@ -43,9 +44,18 @@ public class PyTypeParser {
|
||||
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(anchor);
|
||||
|
||||
if (type.equals("unknown")) {
|
||||
return null;
|
||||
}
|
||||
if (type.equals("string")) {
|
||||
return builtinCache.getStringType(LanguageLevel.forElement(anchor));
|
||||
}
|
||||
if (type.equals("str")) {
|
||||
return builtinCache.getStrType();
|
||||
}
|
||||
if (type.equals("bytes")) {
|
||||
return builtinCache.getBytesType(LanguageLevel.forElement(anchor));
|
||||
}
|
||||
if (type.equals("boolean")) {
|
||||
return builtinCache.getBoolType();
|
||||
}
|
||||
@@ -83,7 +93,7 @@ public class PyTypeParser {
|
||||
return new PyClassType(classes.iterator().next(), false);
|
||||
}
|
||||
}
|
||||
if (CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.is('.')).matchesAllOf(type)) {
|
||||
if (CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.is('.')).or(CharMatcher.is('_')).matchesAllOf(type)) {
|
||||
int pos = type.lastIndexOf('.');
|
||||
if (pos > 0) {
|
||||
String shortName = type.substring(pos+1);
|
||||
@@ -93,6 +103,11 @@ public class PyTypeParser {
|
||||
return new PyClassType(aClass, false);
|
||||
}
|
||||
}
|
||||
for (PyClass aClass : classes) {
|
||||
if (shortName.equals(aClass.getName())) {
|
||||
return new PyClassType(aClass, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
def f1(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10=10, p11='11'):
|
||||
"""
|
||||
:type p1: integer
|
||||
:type p2: integer
|
||||
:type p3: float
|
||||
:type p4: float
|
||||
:type p5: int
|
||||
:type p6: integer
|
||||
:type p7: integer
|
||||
:type p8: int
|
||||
:type p9: int
|
||||
:type p10: int
|
||||
:type p11: string
|
||||
"""
|
||||
return p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + p10 + int(p11)
|
||||
|
||||
|
||||
def test_1():
|
||||
p7 = int('7')
|
||||
f1(1,
|
||||
<warning descr="Expected type 'int', got 'str' instead">'2'</warning>,
|
||||
3.0, 4, 5, int('6'), p7, p8=-8,
|
||||
<warning descr="Expected type 'int', got 'str' instead">p9='foo'</warning>,
|
||||
<warning descr="Expected type 'int', got 'str' instead">p10='foo'</warning>)
|
||||
|
||||
|
||||
def str_to_none(b):
|
||||
"""
|
||||
:type b: str
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def unicode_to_none(s):
|
||||
"""
|
||||
:type s: unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def string_to_none(s):
|
||||
"""
|
||||
:type s: string
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def str_or_unicode_to_none(s):
|
||||
"""
|
||||
:type s: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def test_str_unicode():
|
||||
b1 = 'hello'
|
||||
s1 = u'привет'
|
||||
b2 = str(-1)
|
||||
s2 = unicode(3.14)
|
||||
ENC = 'utf-8'
|
||||
str_to_none(<warning descr="Expected type 'str', got 'unicode' instead">b1.decode(ENC)</warning>)
|
||||
unicode_to_none(b1.decode(ENC))
|
||||
string_to_none(b1.decode(ENC))
|
||||
str_or_unicode_to_none(b1.decode(ENC))
|
||||
b1.encode(ENC)
|
||||
s1.decode(ENC)
|
||||
str_to_none(s1.encode(ENC))
|
||||
unicode_to_none(<warning descr="Expected type 'unicode', got 'str' instead">s1.encode(ENC)</warning>)
|
||||
string_to_none(<warning descr="Expected type 'unicode', got 'str' instead">s1.encode(ENC)</warning>)
|
||||
str_or_unicode_to_none(s1.encode(ENC))
|
||||
b2.decode(ENC)
|
||||
b2.encode(ENC)
|
||||
s2.decode(ENC)
|
||||
s2.encode(ENC)
|
||||
|
||||
|
||||
def f_list_tuple(spam, eggs):
|
||||
"""
|
||||
:type spam: list of string
|
||||
:type eggs: (bool, int, string)
|
||||
"""
|
||||
return spam, eggs
|
||||
|
||||
|
||||
def test_list_tuple():
|
||||
f_list_tuple(<warning descr="Expected type 'list of unicode', got 'list of int' instead">[1, 2, 3]</warning>,
|
||||
(<warning descr="Expected type 'tuple(bool,int,unicode)', got 'tuple(bool,int,str)' instead">False, 2, ''</warning>))
|
||||
|
||||
|
||||
def test_builtin_numerics():
|
||||
abs(False)
|
||||
int(10)
|
||||
long(False)
|
||||
float(False)
|
||||
complex(False)
|
||||
divmod(False, False)
|
||||
divmod(<warning descr="Expected type 'one of (bool, int, long, float, complex)', got 'str' instead">'foo'</warning>,
|
||||
<warning descr="Expected type 'one of (bool, int, long, float, complex)', got 'unicode' instead">u'bar'</warning>)
|
||||
pow(False, True)
|
||||
round(False,
|
||||
<warning descr="Expected type 'one of (bool, int, long, float, None)', got 'str' instead">'foo'</warning>)
|
||||
|
||||
|
||||
def test_generator():
|
||||
def gen(n):
|
||||
for x in xrange(n):
|
||||
yield str(x)
|
||||
def f(xs):
|
||||
"""
|
||||
:type xs: list of int
|
||||
"""
|
||||
return xs
|
||||
return (''.join(gen(10)),
|
||||
f(<warning descr="Expected type 'list of int', got 'list of str' instead">gen(11)</warning>))
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html><body><code>def <b>foo</b>()<br>Inferred return type: None<br>Doc of foo.</code></body></html>
|
||||
<html><body><code>def <b>foo</b>()<br>Inferred type: () -> None<br><br>Doc of foo.</code></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body><code>def <b>foo</b>()<br>Inferred return type: None<br>Doc of foo.</code></body></html>
|
||||
<html><body><code>def <b>foo</b>()<br>Inferred type: () -> None<br><br>Doc of foo.</code></body></html>
|
||||
@@ -1,2 +1,2 @@
|
||||
def foo(arg)
|
||||
Inferred return type: int
|
||||
Inferred type: (arg: unknown) -> int
|
||||
@@ -1,3 +1,3 @@
|
||||
class A
|
||||
def f(self)
|
||||
Inferred return type: int
|
||||
Inferred type: (self: A) -> int
|
||||
@@ -1 +1 @@
|
||||
<html><body><code>def <b>foo</b>()<br>Inferred return type: None<br>Doc of foo.<br>It has two lines.</code></body></html>
|
||||
<html><body><code>def <b>foo</b>()<br>Inferred type: () -> None<br><br>Doc of foo.<br>It has two lines.</code></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body><code><small>class <a href="psi_element://#class#">B</a>(<a href="psi_element://#parent#A">A</a>)</small><br><br>def <b>foo</b>(self)<br>Inferred return type: None</code><br><br><i>Documentation is missing.</i> The following is copied from <code><a href="psi_element://#parent#A">A</a>.foo</code>.<br><br><code>Doc from A.foo.</code></body></html>
|
||||
<html><body><code><small>class <a href="psi_element://#class#">B</a>(<a href="psi_element://#parent#A">A</a>)</small><br><br>def <b>foo</b>(self)<br>Inferred type: (self: B) -> None<br></code><br><br><i>Documentation is missing.</i> The following is copied from <code><a href="psi_element://#parent#A">A</a>.foo</code>.<br><br><code>Doc from A.foo.</code></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body><code><small>class <a href="psi_element://#class#">Foo</a></small><br><br>@<i>deco</i><br>def <b>meth</b>(self)<br>Inferred return type: None<br><br>Doc of meth.<br></code></body></html>
|
||||
<html><body><code><small>class <a href="psi_element://#class#">Foo</a></small><br><br>@<i>deco</i><br>def <b>meth</b>(self)<br>Inferred type: (self: Foo) -> None<br><br><br>Doc of meth.<br></code></body></html>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<br><i>Copied from getter:</i><br>Does things to X<code><br><br>@<i>x.deleter</i><br>def <b>x</b>(self, v)<br>Inferred return type: None<br>Deletes X</code><small><br><br>Deleter of property</small><br></body></html>
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<br><i>Copied from getter:</i><br>Does things to X<code><br><br>@<i>x.deleter</i><br>def <b>x</b>(self, v)<br>Inferred type: (self: A, v: unknown) -> None<br><br>Deletes X</code><small><br><br>Deleter of property</small><br></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>@<i>property</i><br>def <b>x</b>(self)<br>Inferred return type: int<br>Does things to X</code><small><br><br>Getter of property</small><br></body></html>
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>@<i>property</i><br>def <b>x</b>(self)<br>Inferred type: (self: A) -> int<br><br>Does things to X</code><small><br><br>Getter of property</small><br></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<br><i>Copied from getter:</i><br>Does things to X<code><br><br>@<i>x.setter</i><br>def <b>x</b>(self, v)<br>Inferred return type: None<br>Sets X</code><small><br><br>Setter of property</small><br></body></html>
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<br><i>Copied from getter:</i><br>Does things to X<code><br><br>@<i>x.setter</i><br>def <b>x</b>(self, v)<br>Inferred type: (self: A, v: unknown) -> None<br><br>Sets X</code><small><br><br>Setter of property</small><br></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>def <b>__getX</b>(self)<br>Inferred return type: unknown<br>Doc of getter</code><small><br><br>Deleter of property</small><br></body></html>
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>def <b>__getX</b>(self)<br>Inferred type: (self: A) -> unknown<br><br>Doc of getter</code><small><br><br>Deleter of property</small><br></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>def <b>__getX</b>(self)<br>Inferred return type: unknown<br>Doc of getter</code><small><br><br>Getter of property</small><br></body></html>
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>def <b>__getX</b>(self)<br>Inferred type: (self: A) -> unknown<br><br>Doc of getter</code><small><br><br>Getter of property</small><br></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>def <b>__getX</b>(self, x)<br>Inferred return type: None<br>Doc of getter</code><small><br><br>Setter of property</small><br></body></html>
|
||||
<html><body>property <b><code>x</code></b> of class <a href="psi_element://#class#">A</a>(<a href="psi_element://#parent#object">object</a>)<code><br><br>def <b>__getX</b>(self, x)<br>Inferred type: (self: A, x: unknown) -> None<br><br>Doc of getter</code><small><br><br>Setter of property</small><br></body></html>
|
||||
@@ -343,4 +343,10 @@ public class PythonInspectionsTest extends PyLightFixtureTestCase {
|
||||
public void testPyDecoratorInspection() { //PY-3348
|
||||
doHighlightingTest(PyDecoratorInspection.class);
|
||||
}
|
||||
|
||||
public void testPyTypeCheckerInspection() {
|
||||
// TODO: Add a Py3K test
|
||||
setLanguageLevel(LanguageLevel.PYTHON27);
|
||||
doHighlightingTest(PyTypeCheckerInspection.class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user