blob: 2f9a1820fde4690aac6c954d7e3aa5142c08148e [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001import inspect
2import trace
3import os
4
5trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue17143 (PY-8706)
6import gc
7from pydevd_comm import CMD_SIGNATURE_CALL_TRACE, NetCommand
8import pydevd_vars
9
10class Signature(object):
11 def __init__(self, file, name):
12 self.file = file
13 self.name = name
14 self.args = []
15 self.args_str = []
16
17 def add_arg(self, name, type):
18 self.args.append((name, type))
19 self.args_str.append("%s:%s"%(name, type))
20
21 def __str__(self):
22 return "%s %s(%s)"%(self.file, self.name, ", ".join(self.args_str))
23
24
25class SignatureFactory(object):
26 def __init__(self):
27 self._caller_cache = {}
28 self.project_roots = os.getenv('PYCHARM_PROJECT_ROOTS', '').split(os.pathsep)
29
30 def is_in_scope(self, filename):
31 filename = os.path.normcase(filename)
32 for root in self.project_roots:
33 root = os.path.normcase(root)
34 if filename.startswith(root):
35 return True
36 return False
37
38
39
40 def create_signature(self, frame):
41 try:
42 code = frame.f_code
43 locals = frame.f_locals
44 filename, modulename, funcname = self.file_module_function_of(frame)
45 res = Signature(filename, funcname)
46 for i in range(0, code.co_argcount):
47 name = code.co_varnames[i]
48 tp = type(locals[name])
49 class_name = tp.__name__
50 if class_name == 'instance':
51 tp = locals[name].__class__
52 class_name = tp.__name__
53
54 if tp.__module__ and tp.__module__ != '__main__':
55 class_name = "%s.%s"%(tp.__module__, class_name)
56
57 res.add_arg(name, class_name)
58 return res
59 except:
60 import traceback
61 traceback.print_exc()
62
63
64 def file_module_function_of(self, frame): #this code is take from trace module and fixed to work with new-style classes
65 code = frame.f_code
66 filename = code.co_filename
67 if filename:
68 modulename = trace.modname(filename)
69 else:
70 modulename = None
71
72 funcname = code.co_name
73 clsname = None
74 if code in self._caller_cache:
75 if self._caller_cache[code] is not None:
76 clsname = self._caller_cache[code]
77 else:
78 self._caller_cache[code] = None
79 ## use of gc.get_referrers() was suggested by Michael Hudson
80 # all functions which refer to this code object
81 funcs = [f for f in gc.get_referrers(code)
82 if inspect.isfunction(f)]
83 # require len(func) == 1 to avoid ambiguity caused by calls to
84 # new.function(): "In the face of ambiguity, refuse the
85 # temptation to guess."
86 if len(funcs) == 1:
87 dicts = [d for d in gc.get_referrers(funcs[0])
88 if isinstance(d, dict)]
89 if len(dicts) == 1:
90 classes = [c for c in gc.get_referrers(dicts[0])
91 if hasattr(c, "__bases__") or inspect.isclass(c)]
92 elif len(dicts) > 1: #new-style classes
93 classes = [c for c in gc.get_referrers(dicts[1])
94 if hasattr(c, "__bases__") or inspect.isclass(c)]
95 else:
96 classes = []
97
98 if len(classes) == 1:
99 # ditto for new.classobj()
100 clsname = classes[0].__name__
101 # cache the result - assumption is that new.* is
102 # not called later to disturb this relationship
103 # _caller_cache could be flushed if functions in
104 # the new module get called.
105 self._caller_cache[code] = clsname
106
107
108 if clsname is not None:
109 funcname = "%s.%s" % (clsname, funcname)
110
111 return filename, modulename, funcname
112
113def create_signature_message(signature):
114 cmdTextList = ["<xml>"]
115
116 cmdTextList.append('<call_signature file="%s" name="%s">' % (pydevd_vars.makeValidXmlValue(signature.file), pydevd_vars.makeValidXmlValue(signature.name)))
117
118 for arg in signature.args:
119 cmdTextList.append('<arg name="%s" type="%s"></arg>' % (pydevd_vars.makeValidXmlValue(arg[0]), pydevd_vars.makeValidXmlValue(arg[1])))
120
121 cmdTextList.append("</call_signature></xml>")
122 cmdText = ''.join(cmdTextList)
123 return NetCommand(CMD_SIGNATURE_CALL_TRACE, 0, cmdText)
124
125def sendSignatureCallTrace(dbg, frame, filename):
126 if dbg.signature_factory:
127 if dbg.signature_factory.is_in_scope(filename):
128 dbg.writer.addCommand(create_signature_message(dbg.signature_factory.create_signature(frame)))
129
130
131