Files
openide/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py
Rustam Vishnyakov 123242c4b2 EditorConfig documentation test
GitOrigin-RevId: fd52ace3d7a32ecd02c2c5ab90e077967604c15e
2019-06-16 04:03:21 +03:00

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