mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Collect return types when tracing call signatures
This commit is contained in:
@@ -58,10 +58,9 @@ each command has a format:
|
||||
* PYDB - pydevd, the python end
|
||||
'''
|
||||
|
||||
from _pydev_imps import _pydev_threading as threading
|
||||
from _pydev_bundle.pydev_imports import _queue
|
||||
from _pydev_imps import _pydev_time as time, _pydev_thread
|
||||
from _pydev_imps._pydev_socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR
|
||||
from _pydev_bundle.pydev_imports import _queue
|
||||
from _pydevd_bundle.pydevd_constants import * #@UnusedWildImport
|
||||
|
||||
try:
|
||||
@@ -115,10 +114,9 @@ CMD_REMOVE_DJANGO_EXCEPTION_BREAK = 126
|
||||
CMD_SET_NEXT_STATEMENT = 127
|
||||
CMD_SMART_STEP_INTO = 128
|
||||
CMD_EXIT = 129
|
||||
|
||||
CMD_SIGNATURE_CALL_TRACE = 130
|
||||
|
||||
|
||||
|
||||
CMD_SET_PY_EXCEPTION = 131
|
||||
CMD_GET_FILE_CONTENTS = 132
|
||||
CMD_SET_PROPERTY_TRACE = 133
|
||||
@@ -142,35 +140,36 @@ CMD_RETURN = 502
|
||||
CMD_ERROR = 901
|
||||
|
||||
ID_TO_MEANING = {
|
||||
'101':'CMD_RUN',
|
||||
'102':'CMD_LIST_THREADS',
|
||||
'103':'CMD_THREAD_CREATE',
|
||||
'104':'CMD_THREAD_KILL',
|
||||
'105':'CMD_THREAD_SUSPEND',
|
||||
'106':'CMD_THREAD_RUN',
|
||||
'107':'CMD_STEP_INTO',
|
||||
'108':'CMD_STEP_OVER',
|
||||
'109':'CMD_STEP_RETURN',
|
||||
'110':'CMD_GET_VARIABLE',
|
||||
'111':'CMD_SET_BREAK',
|
||||
'112':'CMD_REMOVE_BREAK',
|
||||
'113':'CMD_EVALUATE_EXPRESSION',
|
||||
'114':'CMD_GET_FRAME',
|
||||
'115':'CMD_EXEC_EXPRESSION',
|
||||
'116':'CMD_WRITE_TO_CONSOLE',
|
||||
'117':'CMD_CHANGE_VARIABLE',
|
||||
'118':'CMD_RUN_TO_LINE',
|
||||
'119':'CMD_RELOAD_CODE',
|
||||
'120':'CMD_GET_COMPLETIONS',
|
||||
'121':'CMD_CONSOLE_EXEC',
|
||||
'122':'CMD_ADD_EXCEPTION_BREAK',
|
||||
'123':'CMD_REMOVE_EXCEPTION_BREAK',
|
||||
'124':'CMD_LOAD_SOURCE',
|
||||
'125':'CMD_ADD_DJANGO_EXCEPTION_BREAK',
|
||||
'126':'CMD_REMOVE_DJANGO_EXCEPTION_BREAK',
|
||||
'127':'CMD_SET_NEXT_STATEMENT',
|
||||
'128':'CMD_SMART_STEP_INTO',
|
||||
'101': 'CMD_RUN',
|
||||
'102': 'CMD_LIST_THREADS',
|
||||
'103': 'CMD_THREAD_CREATE',
|
||||
'104': 'CMD_THREAD_KILL',
|
||||
'105': 'CMD_THREAD_SUSPEND',
|
||||
'106': 'CMD_THREAD_RUN',
|
||||
'107': 'CMD_STEP_INTO',
|
||||
'108': 'CMD_STEP_OVER',
|
||||
'109': 'CMD_STEP_RETURN',
|
||||
'110': 'CMD_GET_VARIABLE',
|
||||
'111': 'CMD_SET_BREAK',
|
||||
'112': 'CMD_REMOVE_BREAK',
|
||||
'113': 'CMD_EVALUATE_EXPRESSION',
|
||||
'114': 'CMD_GET_FRAME',
|
||||
'115': 'CMD_EXEC_EXPRESSION',
|
||||
'116': 'CMD_WRITE_TO_CONSOLE',
|
||||
'117': 'CMD_CHANGE_VARIABLE',
|
||||
'118': 'CMD_RUN_TO_LINE',
|
||||
'119': 'CMD_RELOAD_CODE',
|
||||
'120': 'CMD_GET_COMPLETIONS',
|
||||
'121': 'CMD_CONSOLE_EXEC',
|
||||
'122': 'CMD_ADD_EXCEPTION_BREAK',
|
||||
'123': 'CMD_REMOVE_EXCEPTION_BREAK',
|
||||
'124': 'CMD_LOAD_SOURCE',
|
||||
'125': 'CMD_ADD_DJANGO_EXCEPTION_BREAK',
|
||||
'126': 'CMD_REMOVE_DJANGO_EXCEPTION_BREAK',
|
||||
'127': 'CMD_SET_NEXT_STATEMENT',
|
||||
'128': 'CMD_SMART_STEP_INTO',
|
||||
'129': 'CMD_EXIT',
|
||||
|
||||
'130': 'CMD_SIGNATURE_CALL_TRACE',
|
||||
|
||||
'131': 'CMD_SET_PY_EXCEPTION',
|
||||
@@ -184,14 +183,13 @@ ID_TO_MEANING = {
|
||||
'139': 'CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED',
|
||||
'140': 'CMD_IGNORE_THROWN_EXCEPTION_AT',
|
||||
'141': 'CMD_ENABLE_DONT_TRACE',
|
||||
'143': 'CMD_GET_ARRAY',
|
||||
'144': 'CMD_STEP_INTO_MY_CODE',
|
||||
'145': 'CMD_GET_CONCURRENCY_EVENT',
|
||||
|
||||
'501':'CMD_VERSION',
|
||||
'502':'CMD_RETURN',
|
||||
'901':'CMD_ERROR',
|
||||
|
||||
'143':'CMD_GET_ARRAY',
|
||||
'144':'CMD_STEP_INTO_MY_CODE',
|
||||
'145':'CMD_GET_CONCURRENCY_EVENT',
|
||||
'501': 'CMD_VERSION',
|
||||
'502': 'CMD_RETURN',
|
||||
'901': 'CMD_ERROR',
|
||||
}
|
||||
|
||||
MAX_IO_MSG_SIZE = 1000 #if the io is too big, we'll not send all (could make the debugger too non-responsive)
|
||||
|
||||
@@ -13,13 +13,14 @@ from _pydevd_bundle.pydevd_comm import CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETUR
|
||||
from _pydevd_bundle.pydevd_constants import STATE_SUSPEND, dict_contains, get_thread_id, STATE_RUN, dict_iter_values
|
||||
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised
|
||||
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
|
||||
|
||||
try:
|
||||
from inspect import CO_GENERATOR
|
||||
except:
|
||||
CO_GENERATOR = 0
|
||||
|
||||
try:
|
||||
from _pydevd_bundle.pydevd_signature import send_signature_call_trace
|
||||
from _pydevd_bundle.pydevd_signature import send_signature_call_trace, send_signature_return_trace
|
||||
except ImportError:
|
||||
def send_signature_call_trace(*args, **kwargs):
|
||||
pass
|
||||
@@ -80,6 +81,12 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
|
||||
|
||||
return self.trace_exception
|
||||
|
||||
def trace_return(self, frame, event, arg):
|
||||
if event == 'return':
|
||||
main_debugger, filename, info, thread = self._args
|
||||
send_signature_return_trace(main_debugger, frame, filename, arg)
|
||||
return self.trace_return
|
||||
|
||||
# IFDEF CYTHON
|
||||
# def should_stop_on_exception(self, frame, str event, arg):
|
||||
# cdef PyDBAdditionalThreadInfo info;
|
||||
@@ -274,12 +281,15 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
|
||||
try:
|
||||
# print 'frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event
|
||||
info.is_tracing = True
|
||||
need_trace_return = False
|
||||
|
||||
if main_debugger._finish_debugging_session:
|
||||
return None
|
||||
|
||||
if event == 'call' and main_debugger.signature_factory:
|
||||
send_signature_call_trace(main_debugger, frame, filename)
|
||||
need_trace_return = send_signature_call_trace(main_debugger, frame, filename)
|
||||
if event == 'return' and main_debugger.signature_factory:
|
||||
send_signature_return_trace(main_debugger, frame, filename, arg)
|
||||
|
||||
plugin_manager = main_debugger.plugin
|
||||
|
||||
@@ -341,7 +351,10 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
|
||||
if has_exception_breakpoints:
|
||||
return self.trace_exception
|
||||
else:
|
||||
return None
|
||||
if need_trace_return:
|
||||
return self.trace_return
|
||||
else:
|
||||
return None
|
||||
|
||||
else:
|
||||
#checks the breakpoint to see if there is a context match in some function
|
||||
@@ -361,7 +374,10 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
|
||||
if has_exception_breakpoints:
|
||||
return self.trace_exception
|
||||
else:
|
||||
return None
|
||||
if need_trace_return:
|
||||
return self.trace_return
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
#We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame
|
||||
|
||||
@@ -19,6 +19,7 @@ class Signature(object):
|
||||
self.name = name
|
||||
self.args = []
|
||||
self.args_str = []
|
||||
self.return_type = None
|
||||
|
||||
def add_arg(self, name, type):
|
||||
self.args.append((name, type))
|
||||
@@ -28,32 +29,39 @@ class Signature(object):
|
||||
return "%s %s(%s)"%(self.file, self.name, ", ".join(self.args_str))
|
||||
|
||||
|
||||
def get_type_of_value(value, ignore_module_name=('__main__', '__builtin__', 'builtins')):
|
||||
tp = type(value)
|
||||
class_name = tp.__name__
|
||||
if class_name == 'instance': # old-style classes
|
||||
tp = value.__class__
|
||||
class_name = tp.__name__
|
||||
|
||||
if hasattr(tp, '__module__') and tp.__module__ and tp.__module__ not in ignore_module_name:
|
||||
class_name = "%s.%s"%(tp.__module__, class_name)
|
||||
|
||||
return class_name
|
||||
|
||||
|
||||
class SignatureFactory(object):
|
||||
def __init__(self):
|
||||
self._caller_cache = {}
|
||||
self._ignore_module_name = ('__main__', '__builtin__', 'builtins')
|
||||
self.cache = CallSignatureCache()
|
||||
|
||||
def is_in_scope(self, filename):
|
||||
return not pydevd_utils.not_in_project_roots(filename)
|
||||
|
||||
def create_signature(self, frame):
|
||||
def create_signature(self, frame, with_args=True):
|
||||
try:
|
||||
code = frame.f_code
|
||||
locals = frame.f_locals
|
||||
filename, modulename, funcname = self.file_module_function_of(frame)
|
||||
res = Signature(filename, funcname)
|
||||
for i in xrange(0, code.co_argcount):
|
||||
name = code.co_varnames[i]
|
||||
tp = type(locals[name])
|
||||
class_name = tp.__name__
|
||||
if class_name == 'instance': # old-style classes
|
||||
tp = locals[name].__class__
|
||||
class_name = tp.__name__
|
||||
|
||||
if hasattr(tp, '__module__') and tp.__module__ and tp.__module__ not in self._ignore_module_name:
|
||||
class_name = "%s.%s"%(tp.__module__, class_name)
|
||||
|
||||
res.add_arg(name, class_name)
|
||||
if with_args:
|
||||
for i in xrange(0, code.co_argcount):
|
||||
name = code.co_varnames[i]
|
||||
class_name = get_type_of_value(locals[name])
|
||||
|
||||
res.add_arg(name, class_name)
|
||||
return res
|
||||
except:
|
||||
import traceback
|
||||
@@ -109,6 +117,41 @@ class SignatureFactory(object):
|
||||
|
||||
return filename, modulename, funcname
|
||||
|
||||
|
||||
def get_signature_info(signature):
|
||||
return signature.file, signature.name, ' '.join([arg[1]for arg in signature.args])
|
||||
|
||||
|
||||
def get_frame_info(frame):
|
||||
co = frame.f_code
|
||||
return co.co_name, frame.f_lineno, co.co_filename
|
||||
|
||||
|
||||
class CallSignatureCache(object):
|
||||
def __init__(self):
|
||||
self.cache = {}
|
||||
|
||||
def add(self, signature):
|
||||
filename, name, args_type = get_signature_info(signature)
|
||||
|
||||
if not filename in self.cache:
|
||||
self.cache[filename] = {}
|
||||
|
||||
calls_from_file = self.cache[filename]
|
||||
|
||||
if not name in calls_from_file:
|
||||
calls_from_file[name] = {}
|
||||
|
||||
name_calls = calls_from_file[name]
|
||||
name_calls[args_type] = None
|
||||
|
||||
def is_in_cache(self, signature):
|
||||
filename, name, args_type = get_signature_info(signature)
|
||||
if filename in self.cache and name in self.cache[filename] and args_type in self.cache[filename][name]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def create_signature_message(signature):
|
||||
cmdTextList = ["<xml>"]
|
||||
|
||||
@@ -116,14 +159,40 @@ def create_signature_message(signature):
|
||||
|
||||
for arg in signature.args:
|
||||
cmdTextList.append('<arg name="%s" type="%s"></arg>' % (pydevd_vars.make_valid_xml_value(arg[0]), pydevd_vars.make_valid_xml_value(arg[1])))
|
||||
|
||||
if signature.return_type is not None:
|
||||
cmdTextList.append('<return type="%s"></return>' % (pydevd_vars.make_valid_xml_value(signature.return_type)))
|
||||
|
||||
cmdTextList.append("</call_signature></xml>")
|
||||
cmdText = ''.join(cmdTextList)
|
||||
return NetCommand(CMD_SIGNATURE_CALL_TRACE, 0, cmdText)
|
||||
|
||||
|
||||
def send_signature_call_trace(dbg, frame, filename):
|
||||
if dbg.signature_factory.is_in_scope(filename):
|
||||
dbg.writer.add_command(create_signature_message(dbg.signature_factory.create_signature(frame)))
|
||||
if dbg.signature_factory and dbg.signature_factory.is_in_scope(filename):
|
||||
signature = dbg.signature_factory.create_signature(frame)
|
||||
if dbg.signature_factory.cache is not None:
|
||||
if not dbg.signature_factory.cache.is_in_cache(signature):
|
||||
dbg.signature_factory.cache.add(signature)
|
||||
dbg.writer.add_command(create_signature_message(signature))
|
||||
return True
|
||||
else:
|
||||
# we don't send signature if it is cached
|
||||
return False
|
||||
else:
|
||||
dbg.writer.add_command(create_signature_message(signature))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def send_signature_return_trace(dbg, frame, filename, return_value):
|
||||
if dbg.signature_factory and dbg.signature_factory.is_in_scope(filename):
|
||||
signature = dbg.signature_factory.create_signature(frame, with_args=False)
|
||||
signature.return_type = get_type_of_value(return_value)
|
||||
dbg.writer.add_command(create_signature_message(signature))
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.util.List;
|
||||
public class PySignature {
|
||||
private final String myFile;
|
||||
private final String myFunctionName;
|
||||
|
||||
private NamedParameter myReturnType = null;
|
||||
|
||||
private final List<NamedParameter> myArgs = Lists.newArrayList();
|
||||
|
||||
@@ -46,6 +48,22 @@ public class PySignature {
|
||||
return myArgs;
|
||||
}
|
||||
|
||||
public NamedParameter getReturnType() {
|
||||
return myReturnType;
|
||||
}
|
||||
|
||||
public PySignature addReturnType(@Nullable String returnType) {
|
||||
if (StringUtil.isNotEmpty(returnType)) {
|
||||
if (myReturnType != null) {
|
||||
myReturnType.addType(returnType);
|
||||
}
|
||||
else {
|
||||
myReturnType = new NamedParameter("", returnType);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PySignature addAllArgs(@NotNull PySignature signature) {
|
||||
for (NamedParameter param : signature.getArgs()) {
|
||||
@@ -71,6 +89,11 @@ public class PySignature {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getReturnTypeQualifiedName() {
|
||||
return myReturnType != null ? myReturnType.getTypeQualifiedName() : null;
|
||||
}
|
||||
|
||||
|
||||
public static class NamedParameter {
|
||||
private final String myName;
|
||||
|
||||
@@ -30,6 +30,7 @@ public abstract class AbstractCommand<T> {
|
||||
public static final int LOAD_SOURCE = 124;
|
||||
public static final int SMART_STEP_INTO = 128;
|
||||
public static final int EXIT = 129;
|
||||
|
||||
public static final int CALL_SIGNATURE_TRACE = 130;
|
||||
|
||||
public static final int CMD_SET_PY_EXCEPTION = 131;
|
||||
@@ -45,6 +46,9 @@ public abstract class AbstractCommand<T> {
|
||||
public static final int CMD_ENABLE_DONT_TRACE = 141;
|
||||
|
||||
public static final int SHOW_CONSOLE = 142;
|
||||
public static final int GET_ARRAY = 143;
|
||||
public static final int STEP_INTO_MY_CODE = 144;
|
||||
public static final int LOG_CONCURRENCY_EVENT = 145;
|
||||
|
||||
public static final int ERROR = 901;
|
||||
|
||||
@@ -52,9 +56,7 @@ public abstract class AbstractCommand<T> {
|
||||
public static final String NEW_LINE_CHAR = "@_@NEW_LINE_CHAR@_@";
|
||||
public static final String TAB_CHAR = "@_@TAB_CHAR@_@";
|
||||
|
||||
public static final int GET_ARRAY = 143;
|
||||
public static final int STEP_INTO_MY_CODE = 144;
|
||||
public static final int LOG_CONCURRENCY_EVENT = 145;
|
||||
|
||||
|
||||
@NotNull private final RemoteDebugger myDebugger;
|
||||
private final int myCommandCode;
|
||||
|
||||
@@ -31,10 +31,15 @@ public class ProtocolParser {
|
||||
|
||||
while (reader.hasMoreChildren()) {
|
||||
reader.moveDown();
|
||||
if (!"arg".equals(reader.getNodeName())) {
|
||||
throw new PyDebuggerException("Expected <arg>, found " + reader.getNodeName());
|
||||
if ("arg".equals(reader.getNodeName())) {
|
||||
signature.addArgument(readString(reader, "name", ""), readString(reader, "type", ""));
|
||||
} else
|
||||
if ("return".equals(reader.getNodeName())) {
|
||||
signature.addReturnType(readString(reader, "type", ""));
|
||||
} else {
|
||||
throw new PyDebuggerException("Expected <arg> or <return>, found " + reader.getNodeName());
|
||||
}
|
||||
signature.addArgument(readString(reader, "name", ""), readString(reader, "type", ""));
|
||||
|
||||
reader.moveUp();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.jetbrains.python.debugger;
|
||||
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.jetbrains.python.psi.PyCallable;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
* @author traff
|
||||
*/
|
||||
public class PyCallSignatureTypeProvider extends PyTypeProviderBase {
|
||||
@Override
|
||||
public Ref<PyType> getParameterType(@NotNull final PyNamedParameter param,
|
||||
@NotNull final PyFunction func,
|
||||
@NotNull TypeEvalContext context) {
|
||||
@@ -34,8 +36,25 @@ public class PyCallSignatureTypeProvider extends PyTypeProviderBase {
|
||||
if (typeName != null) {
|
||||
final PyType type = PyTypeParser.getTypeByName(param, typeName);
|
||||
if (type != null) {
|
||||
final PyType evaluatedType = PyDynamicallyEvaluatedType.create(type);
|
||||
return Ref.create(evaluatedType);
|
||||
return Ref.create(PyDynamicallyEvaluatedType.create(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<PyType> getReturnType(@NotNull final PyCallable callable, @NotNull TypeEvalContext context) {
|
||||
if (callable instanceof PyFunction) {
|
||||
PyFunction function = (PyFunction)callable;
|
||||
PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature(function);
|
||||
if (signature != null) {
|
||||
final String typeName = signature.getReturnType().getTypeQualifiedName();
|
||||
if (typeName != null) {
|
||||
final PyType type = PyTypeParser.getTypeByName(function, typeName);
|
||||
if (type != null) {
|
||||
return Ref.create(PyDynamicallyEvaluatedType.create(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
|
||||
else {
|
||||
//noinspection ConstantConditions
|
||||
lines[i] = signatureToString(stringToSignature(file.
|
||||
getCanonicalPath(), lines[i]).addAllArgs(signature));
|
||||
getCanonicalPath(), lines[i]).addAllArgs(signature).addReturnType(signature.getReturnTypeQualifiedName()));
|
||||
}
|
||||
}
|
||||
i++;
|
||||
@@ -140,7 +140,9 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
|
||||
}
|
||||
|
||||
private static String signatureToString(PySignature signature) {
|
||||
return signature.getFunctionName() + "\t" + StringUtil.join(arguments(signature), "\t");
|
||||
return signature.getFunctionName() + "\t" + StringUtil.join(arguments(signature), "\t") +
|
||||
(signature.getReturnType() != null
|
||||
? "\t" + StringUtil.join(signature.getReturnType().getTypesList(), "\t") : "");
|
||||
}
|
||||
|
||||
private static List<String> arguments(PySignature signature) {
|
||||
@@ -248,8 +250,11 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
|
||||
if (var.length == 2) {
|
||||
signature = signature.addArgument(var[0], var[1]);
|
||||
}
|
||||
else if (var.length == 1) {
|
||||
signature = signature.addReturnType(var[0]);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Should be <name>:<type> format. " + parts[i] + " instead.");
|
||||
throw new IllegalStateException("Should be <name>:<type> format for arg or <type> for return type; '" + parts[i] + "' instead.");
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
@@ -259,7 +264,7 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile getFile(@NotNull PySignature signature) {
|
||||
return LocalFileSystem.getInstance().findFileByPath(signature.getFile());
|
||||
return LocalFileSystem.getInstance().refreshAndFindFileByPath(signature.getFile());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -291,7 +296,7 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
}, "Cleaning the cache of dynamically collected types", true, myProject);
|
||||
}, "Cleaning the Cache of Dynamically Collected Types", true, myProject);
|
||||
|
||||
|
||||
String message;
|
||||
|
||||
@@ -275,7 +275,8 @@ public class PyDocstringGenerator {
|
||||
if (param.getType() == null) {
|
||||
String type;
|
||||
if (signature != null) {
|
||||
type = StringUtil.notNullize(signature.getArgTypeQualifiedName(param.getName()));
|
||||
type = StringUtil.notNullize(param.isReturnValue() ? signature.getReturnTypeQualifiedName() :
|
||||
signature.getArgTypeQualifiedName(param.getName()));
|
||||
}
|
||||
else {
|
||||
type = paramTypes.get(paramCoordinates);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(x):
|
||||
return x
|
||||
|
||||
foo(1)
|
||||
@@ -0,0 +1,11 @@
|
||||
def foo(x):
|
||||
"""
|
||||
|
||||
:param x:
|
||||
:type x: int
|
||||
:return:
|
||||
:rtype: int
|
||||
"""
|
||||
return x
|
||||
|
||||
foo(1)
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.jetbrains.env.python;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.testFramework.EditorTestUtil;
|
||||
import com.jetbrains.env.PyEnvTestCase;
|
||||
import com.jetbrains.env.python.debug.PyDebuggerTask;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.debugger.PyDebuggerOptionsProvider;
|
||||
import com.jetbrains.python.debugger.PySignatureCacheManagerImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
|
||||
public class PyDynamicTypesTest extends PyEnvTestCase {
|
||||
public void test1() throws Exception {
|
||||
doTest(getTestName(true) + ".py");
|
||||
}
|
||||
|
||||
private void doTest(final String scriptName) {
|
||||
runPythonTest(new PyDebuggerTask("/" + "dynamicTypes", scriptName) {
|
||||
@Override
|
||||
public void setUp(String testName) throws Exception {
|
||||
super.setUp(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before() throws Exception {
|
||||
PyDebuggerOptionsProvider.getInstance(myFixture.getProject()).setSaveCallSignatures(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after() throws Exception {
|
||||
PyDebuggerOptionsProvider.getInstance(myFixture.getProject()).setSaveCallSignatures(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testing() throws Exception {
|
||||
waitForTerminate();
|
||||
|
||||
edt(() -> {
|
||||
myFixture.configureByFile("dynamicTypes/" + scriptName);
|
||||
|
||||
try {
|
||||
//copy signature attributes from real file to temporary test file
|
||||
PySignatureCacheManagerImpl.CALL_SIGNATURES_ATTRIBUTE.writeAttributeBytes(myFixture.getFile().getVirtualFile(),
|
||||
PySignatureCacheManagerImpl.CALL_SIGNATURES_ATTRIBUTE
|
||||
.readAttributeBytes(LocalFileSystem.getInstance()
|
||||
.refreshAndFindFileByPath(
|
||||
getTestDataPath() +
|
||||
"/dynamicTypes/" +
|
||||
scriptName)));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
EditorTestUtil.setCaretsAndSelection(myFixture.getEditor(), new EditorTestUtil.CaretAndSelectionState(
|
||||
Lists.newArrayList(new EditorTestUtil.CaretInfo(new LogicalPosition(0, 6), null)), null));
|
||||
final IntentionAction action = myFixture.findSingleIntention(PyBundle.message("INTN.doc.string.stub"));
|
||||
myFixture.launchAction(action);
|
||||
myFixture.checkResultByFile("dynamicTypes/" + getTestName(true) + "_after.py");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user