mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-17 07:20:53 +07:00
207 lines
7.0 KiB
Python
207 lines
7.0 KiB
Python
|
|
try:
|
|
import trace
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue17143 (PY-8706)
|
|
|
|
import os
|
|
from _pydevd_bundle.pydevd_comm import CMD_SIGNATURE_CALL_TRACE, NetCommand
|
|
from _pydevd_bundle import pydevd_xml
|
|
from _pydevd_bundle.pydevd_constants import xrange, dict_iter_items
|
|
from _pydevd_bundle import pydevd_utils
|
|
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
|
|
|
|
|
|
class Signature(object):
|
|
def __init__(self, file, name):
|
|
self.file = file
|
|
self.name = name
|
|
self.args = []
|
|
self.args_str = []
|
|
self.return_type = None
|
|
|
|
def add_arg(self, name, type):
|
|
self.args.append((name, type))
|
|
self.args_str.append("%s:%s"%(name, type))
|
|
|
|
def set_args(self, frame, recursive=False):
|
|
self.args = []
|
|
|
|
code = frame.f_code
|
|
locals = frame.f_locals
|
|
|
|
for i in xrange(0, code.co_argcount):
|
|
name = code.co_varnames[i]
|
|
class_name = get_type_of_value(locals[name], recursive=recursive)
|
|
|
|
self.add_arg(name, class_name)
|
|
|
|
def __str__(self):
|
|
return "%s %s(%s)"%(self.file, self.name, ", ".join(self.args_str))
|
|
|
|
|
|
def get_type_of_value(value, ignore_module_name=('__main__', '__builtin__', 'builtins'), recursive=False):
|
|
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)
|
|
|
|
if class_name == 'list':
|
|
class_name = 'List'
|
|
if len(value) > 0 and recursive:
|
|
class_name += '[%s]' % get_type_of_value(value[0], recursive=recursive)
|
|
return class_name
|
|
|
|
if class_name == 'dict':
|
|
class_name = 'Dict'
|
|
if len(value) > 0 and recursive:
|
|
for (k, v) in dict_iter_items(value):
|
|
class_name += '[%s, %s]' % (get_type_of_value(k, recursive=recursive),
|
|
get_type_of_value(v, recursive=recursive))
|
|
break
|
|
return class_name
|
|
|
|
if class_name == 'tuple':
|
|
class_name = 'Tuple'
|
|
if len(value) > 0 and recursive:
|
|
class_name += '['
|
|
class_name += ', '.join(get_type_of_value(v, recursive=recursive) for v in value)
|
|
class_name += ']'
|
|
|
|
return class_name
|
|
|
|
|
|
def _modname(path):
|
|
"""Return a plausible module name for the path"""
|
|
base = os.path.basename(path)
|
|
filename, ext = os.path.splitext(base)
|
|
return filename
|
|
|
|
|
|
class SignatureFactory(object):
|
|
def __init__(self):
|
|
self._caller_cache = {}
|
|
self.cache = CallSignatureCache()
|
|
|
|
def is_in_scope(self, filename):
|
|
return pydevd_utils.in_project_roots(filename)
|
|
|
|
def create_signature(self, frame, filename, with_args=True):
|
|
try:
|
|
_, modulename, funcname = self.file_module_function_of(frame)
|
|
signature = Signature(filename, funcname)
|
|
if with_args:
|
|
signature.set_args(frame, recursive=True)
|
|
return signature
|
|
except:
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
|
|
def file_module_function_of(self, frame): #this code is take from trace module and fixed to work with new-style classes
|
|
code = frame.f_code
|
|
filename = code.co_filename
|
|
if filename:
|
|
modulename = _modname(filename)
|
|
else:
|
|
modulename = None
|
|
|
|
funcname = code.co_name
|
|
clsname = None
|
|
if code in self._caller_cache:
|
|
if self._caller_cache[code] is not None:
|
|
clsname = self._caller_cache[code]
|
|
else:
|
|
self._caller_cache[code] = None
|
|
clsname = get_clsname_for_code(code, frame)
|
|
if clsname is not None:
|
|
# cache the result - assumption is that new.* is
|
|
# not called later to disturb this relationship
|
|
# _caller_cache could be flushed if functions in
|
|
# the new module get called.
|
|
self._caller_cache[code] = clsname
|
|
|
|
if clsname is not None:
|
|
funcname = "%s.%s" % (clsname, funcname)
|
|
|
|
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)
|
|
calls_from_file = self.cache.setdefault(filename, {})
|
|
name_calls = calls_from_file.setdefault(name, {})
|
|
name_calls[args_type] = None
|
|
|
|
def is_in_cache(self, signature):
|
|
filename, name, args_type = get_signature_info(signature)
|
|
if args_type in self.cache.get(filename, {}).get(name, {}):
|
|
return True
|
|
return False
|
|
|
|
|
|
def create_signature_message(signature):
|
|
cmdTextList = ["<xml>"]
|
|
|
|
cmdTextList.append('<call_signature file="%s" name="%s">' % (pydevd_xml.make_valid_xml_value(signature.file), pydevd_xml.make_valid_xml_value(signature.name)))
|
|
|
|
for arg in signature.args:
|
|
cmdTextList.append('<arg name="%s" type="%s"></arg>' % (pydevd_xml.make_valid_xml_value(arg[0]), pydevd_xml.make_valid_xml_value(arg[1])))
|
|
|
|
if signature.return_type is not None:
|
|
cmdTextList.append('<return type="%s"></return>' % (pydevd_xml.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 and dbg.signature_factory.is_in_scope(filename):
|
|
signature = dbg.signature_factory.create_signature(frame, filename)
|
|
if signature is not None:
|
|
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, filename, with_args=False)
|
|
signature.return_type = get_type_of_value(return_value, recursive=True)
|
|
dbg.writer.add_command(create_signature_message(signature))
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|