runmod.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. def __rope_start_everything():
  2. import os
  3. import sys
  4. import socket
  5. import cPickle as pickle
  6. import marshal
  7. import inspect
  8. import types
  9. import threading
  10. class _MessageSender(object):
  11. def send_data(self, data):
  12. pass
  13. class _SocketSender(_MessageSender):
  14. def __init__(self, port):
  15. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  16. s.connect(('127.0.0.1', port))
  17. self.my_file = s.makefile('w')
  18. def send_data(self, data):
  19. if not self.my_file.closed:
  20. pickle.dump(data, self.my_file)
  21. def close(self):
  22. self.my_file.close()
  23. class _FileSender(_MessageSender):
  24. def __init__(self, file_name):
  25. self.my_file = open(file_name, 'wb')
  26. def send_data(self, data):
  27. if not self.my_file.closed:
  28. marshal.dump(data, self.my_file)
  29. def close(self):
  30. self.my_file.close()
  31. def _cached(func):
  32. cache = {}
  33. def newfunc(self, arg):
  34. if arg in cache:
  35. return cache[arg]
  36. result = func(self, arg)
  37. cache[arg] = result
  38. return result
  39. return newfunc
  40. class _FunctionCallDataSender(object):
  41. def __init__(self, send_info, project_root):
  42. self.project_root = project_root
  43. if send_info.isdigit():
  44. self.sender = _SocketSender(int(send_info))
  45. else:
  46. self.sender = _FileSender(send_info)
  47. def global_trace(frame, event, arg):
  48. # HACK: Ignoring out->in calls
  49. # This might lose some information
  50. if self._is_an_interesting_call(frame):
  51. return self.on_function_call
  52. sys.settrace(global_trace)
  53. threading.settrace(global_trace)
  54. def on_function_call(self, frame, event, arg):
  55. if event != 'return':
  56. return
  57. args = []
  58. returned = ('unknown',)
  59. code = frame.f_code
  60. for argname in code.co_varnames[:code.co_argcount]:
  61. try:
  62. args.append(self._object_to_persisted_form(frame.f_locals[argname]))
  63. except (TypeError, AttributeError):
  64. args.append(('unknown',))
  65. try:
  66. returned = self._object_to_persisted_form(arg)
  67. except (TypeError, AttributeError):
  68. pass
  69. try:
  70. data = (self._object_to_persisted_form(frame.f_code),
  71. tuple(args), returned)
  72. self.sender.send_data(data)
  73. except (TypeError):
  74. pass
  75. return self.on_function_call
  76. def _is_an_interesting_call(self, frame):
  77. #if frame.f_code.co_name in ['?', '<module>']:
  78. # return False
  79. #return not frame.f_back or not self._is_code_inside_project(frame.f_back.f_code)
  80. if not self._is_code_inside_project(frame.f_code) and \
  81. (not frame.f_back or not self._is_code_inside_project(frame.f_back.f_code)):
  82. return False
  83. return True
  84. def _is_code_inside_project(self, code):
  85. source = self._path(code.co_filename)
  86. return source is not None and os.path.exists(source) and \
  87. _realpath(source).startswith(self.project_root)
  88. @_cached
  89. def _get_persisted_code(self, object_):
  90. source = self._path(object_.co_filename)
  91. if not os.path.exists(source):
  92. raise TypeError('no source')
  93. return ('defined', _realpath(source), str(object_.co_firstlineno))
  94. @_cached
  95. def _get_persisted_class(self, object_):
  96. try:
  97. return ('defined', _realpath(inspect.getsourcefile(object_)),
  98. object_.__name__)
  99. except (TypeError, AttributeError):
  100. return ('unknown',)
  101. def _get_persisted_builtin(self, object_):
  102. if isinstance(object_, (str, unicode)):
  103. return ('builtin', 'str')
  104. if isinstance(object_, list):
  105. holding = None
  106. if len(object_) > 0:
  107. holding = object_[0]
  108. return ('builtin', 'list', self._object_to_persisted_form(holding))
  109. if isinstance(object_, dict):
  110. keys = None
  111. values = None
  112. if len(object_) > 0:
  113. keys = object_.keys()[0]
  114. values = object_[keys]
  115. return ('builtin', 'dict',
  116. self._object_to_persisted_form(keys),
  117. self._object_to_persisted_form(values))
  118. if isinstance(object_, tuple):
  119. objects = []
  120. if len(object_) < 3:
  121. for holding in object_:
  122. objects.append(self._object_to_persisted_form(holding))
  123. else:
  124. objects.append(self._object_to_persisted_form(object_[0]))
  125. return tuple(['builtin', 'tuple'] + objects)
  126. if isinstance(object_, set):
  127. holding = None
  128. if len(object_) > 0:
  129. for o in object_:
  130. holding = o
  131. break
  132. return ('builtin', 'set', self._object_to_persisted_form(holding))
  133. return ('unknown',)
  134. def _object_to_persisted_form(self, object_):
  135. if object_ is None:
  136. return ('none',)
  137. if isinstance(object_, types.CodeType):
  138. return self._get_persisted_code(object_)
  139. if isinstance(object_, types.FunctionType):
  140. return self._get_persisted_code(object_.func_code)
  141. if isinstance(object_, types.MethodType):
  142. return self._get_persisted_code(object_.im_func.func_code)
  143. if isinstance(object_, types.ModuleType):
  144. return self._get_persisted_module(object_)
  145. if isinstance(object_, (str, unicode, list, dict, tuple, set)):
  146. return self._get_persisted_builtin(object_)
  147. if isinstance(object_, (types.TypeType, types.ClassType)):
  148. return self._get_persisted_class(object_)
  149. return ('instance', self._get_persisted_class(type(object_)))
  150. @_cached
  151. def _get_persisted_module(self, object_):
  152. path = self._path(object_.__file__)
  153. if path and os.path.exists(path):
  154. return ('defined', _realpath(path))
  155. return ('unknown',)
  156. def _path(self, path):
  157. if path.endswith('.pyc'):
  158. path = path[:-1]
  159. if path.endswith('.py'):
  160. return path
  161. def close(self):
  162. self.sender.close()
  163. sys.settrace(None)
  164. def _realpath(path):
  165. return os.path.realpath(os.path.abspath(os.path.expanduser(path)))
  166. send_info = sys.argv[1]
  167. project_root = sys.argv[2]
  168. file_to_run = sys.argv[3]
  169. run_globals = globals()
  170. run_globals.update({'__name__': '__main__',
  171. '__builtins__': __builtins__,
  172. '__file__': file_to_run})
  173. if send_info != '-':
  174. data_sender = _FunctionCallDataSender(send_info, project_root)
  175. del sys.argv[1:4]
  176. execfile(file_to_run, run_globals)
  177. if send_info != '-':
  178. data_sender.close()
  179. if __name__ == '__main__':
  180. __rope_start_everything()