240 lines
8.3 KiB
Python
240 lines
8.3 KiB
Python
def __rope_start_everything():
|
|
import os
|
|
import sys
|
|
import socket
|
|
|
|
try:
|
|
import cPickle as pickle
|
|
except ImportError:
|
|
import pickle
|
|
import marshal
|
|
import inspect
|
|
import types
|
|
import threading
|
|
import rope.base.utils.pycompat as pycompat
|
|
import base64
|
|
import hashlib
|
|
import hmac
|
|
|
|
class _MessageSender(object):
|
|
def send_data(self, data):
|
|
pass
|
|
|
|
class _SocketSender(_MessageSender):
|
|
def __init__(self, port, key):
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.connect(("127.0.0.1", port))
|
|
self.my_file = s.makefile("wb")
|
|
self.key = base64.b64decode(key)
|
|
|
|
def send_data(self, data):
|
|
if not self.my_file.closed:
|
|
pickled_data = base64.b64encode(
|
|
pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
|
|
)
|
|
dgst = hmac.new(self.key, pickled_data, hashlib.sha256).digest()
|
|
self.my_file.write(base64.b64encode(dgst) + b":" + pickled_data + b"\n")
|
|
|
|
def close(self):
|
|
self.my_file.close()
|
|
|
|
class _FileSender(_MessageSender):
|
|
def __init__(self, file_name):
|
|
self.my_file = open(file_name, "wb")
|
|
|
|
def send_data(self, data):
|
|
if not self.my_file.closed:
|
|
marshal.dump(data, self.my_file)
|
|
|
|
def close(self):
|
|
self.my_file.close()
|
|
|
|
def _cached(func):
|
|
cache = {}
|
|
|
|
def newfunc(self, arg):
|
|
if arg in cache:
|
|
return cache[arg]
|
|
result = func(self, arg)
|
|
cache[arg] = result
|
|
return result
|
|
|
|
return newfunc
|
|
|
|
class _FunctionCallDataSender(object):
|
|
def __init__(self, send_info, project_root):
|
|
self.project_root = project_root
|
|
if send_info[0].isdigit():
|
|
port, key = send_info.split(":", 1)
|
|
self.sender = _SocketSender(int(port), key)
|
|
else:
|
|
self.sender = _FileSender(send_info)
|
|
|
|
def global_trace(frame, event, arg):
|
|
# HACK: Ignoring out->in calls
|
|
# This might lose some information
|
|
if self._is_an_interesting_call(frame):
|
|
return self.on_function_call
|
|
|
|
sys.settrace(global_trace)
|
|
threading.settrace(global_trace)
|
|
|
|
def on_function_call(self, frame, event, arg):
|
|
if event != "return":
|
|
return
|
|
args = []
|
|
returned = ("unknown",)
|
|
code = frame.f_code
|
|
for argname in code.co_varnames[: code.co_argcount]:
|
|
try:
|
|
argvalue = self._object_to_persisted_form(frame.f_locals[argname])
|
|
args.append(argvalue)
|
|
except (TypeError, AttributeError):
|
|
args.append(("unknown",))
|
|
try:
|
|
returned = self._object_to_persisted_form(arg)
|
|
except (TypeError, AttributeError):
|
|
pass
|
|
try:
|
|
data = (
|
|
self._object_to_persisted_form(frame.f_code),
|
|
tuple(args),
|
|
returned,
|
|
)
|
|
self.sender.send_data(data)
|
|
except (TypeError):
|
|
pass
|
|
return self.on_function_call
|
|
|
|
def _is_an_interesting_call(self, frame):
|
|
# if frame.f_code.co_name in ['?', '<module>']:
|
|
# return False
|
|
# return not frame.f_back or
|
|
# not self._is_code_inside_project(frame.f_back.f_code)
|
|
if not self._is_code_inside_project(frame.f_code) and (
|
|
not frame.f_back
|
|
or not self._is_code_inside_project(frame.f_back.f_code)
|
|
):
|
|
return False
|
|
return True
|
|
|
|
def _is_code_inside_project(self, code):
|
|
source = self._path(code.co_filename)
|
|
return (
|
|
source is not None
|
|
and os.path.exists(source)
|
|
and _realpath(source).startswith(self.project_root)
|
|
)
|
|
|
|
@_cached
|
|
def _get_persisted_code(self, object_):
|
|
source = self._path(object_.co_filename)
|
|
if not os.path.exists(source):
|
|
raise TypeError("no source")
|
|
return ("defined", _realpath(source), str(object_.co_firstlineno))
|
|
|
|
@_cached
|
|
def _get_persisted_class(self, object_):
|
|
try:
|
|
return (
|
|
"defined",
|
|
_realpath(inspect.getsourcefile(object_)),
|
|
object_.__name__,
|
|
)
|
|
except (TypeError, AttributeError):
|
|
return ("unknown",)
|
|
|
|
def _get_persisted_builtin(self, object_):
|
|
if isinstance(object_, pycompat.string_types):
|
|
return ("builtin", "str")
|
|
if isinstance(object_, list):
|
|
holding = None
|
|
if len(object_) > 0:
|
|
holding = object_[0]
|
|
return ("builtin", "list", self._object_to_persisted_form(holding))
|
|
if isinstance(object_, dict):
|
|
keys = None
|
|
values = None
|
|
if len(object_) > 0:
|
|
# @todo - fix it properly, why is __locals__ being
|
|
# duplicated ?
|
|
keys = [key for key in object_.keys() if key != "__locals__"][0]
|
|
values = object_[keys]
|
|
return (
|
|
"builtin",
|
|
"dict",
|
|
self._object_to_persisted_form(keys),
|
|
self._object_to_persisted_form(values),
|
|
)
|
|
if isinstance(object_, tuple):
|
|
objects = []
|
|
if len(object_) < 3:
|
|
for holding in object_:
|
|
objects.append(self._object_to_persisted_form(holding))
|
|
else:
|
|
objects.append(self._object_to_persisted_form(object_[0]))
|
|
return tuple(["builtin", "tuple"] + objects)
|
|
if isinstance(object_, set):
|
|
holding = None
|
|
if len(object_) > 0:
|
|
for o in object_:
|
|
holding = o
|
|
break
|
|
return ("builtin", "set", self._object_to_persisted_form(holding))
|
|
return ("unknown",)
|
|
|
|
def _object_to_persisted_form(self, object_):
|
|
if object_ is None:
|
|
return ("none",)
|
|
if isinstance(object_, types.CodeType):
|
|
return self._get_persisted_code(object_)
|
|
if isinstance(object_, types.FunctionType):
|
|
return self._get_persisted_code(object_.__code__)
|
|
if isinstance(object_, types.MethodType):
|
|
return self._get_persisted_code(object_.__func__.__code__)
|
|
if isinstance(object_, types.ModuleType):
|
|
return self._get_persisted_module(object_)
|
|
if isinstance(object_, pycompat.string_types + (list, dict, tuple, set)):
|
|
return self._get_persisted_builtin(object_)
|
|
if isinstance(object_, type):
|
|
return self._get_persisted_class(object_)
|
|
return ("instance", self._get_persisted_class(type(object_)))
|
|
|
|
@_cached
|
|
def _get_persisted_module(self, object_):
|
|
path = self._path(object_.__file__)
|
|
if path and os.path.exists(path):
|
|
return ("defined", _realpath(path))
|
|
return ("unknown",)
|
|
|
|
def _path(self, path):
|
|
if path.endswith(".pyc"):
|
|
path = path[:-1]
|
|
if path.endswith(".py"):
|
|
return path
|
|
|
|
def close(self):
|
|
self.sender.close()
|
|
sys.settrace(None)
|
|
|
|
def _realpath(path):
|
|
return os.path.realpath(os.path.abspath(os.path.expanduser(path)))
|
|
|
|
send_info = sys.argv[1]
|
|
project_root = sys.argv[2]
|
|
file_to_run = sys.argv[3]
|
|
run_globals = globals()
|
|
run_globals.update(
|
|
{"__name__": "__main__", "__builtins__": __builtins__, "__file__": file_to_run}
|
|
)
|
|
|
|
if send_info != "-":
|
|
data_sender = _FunctionCallDataSender(send_info, project_root)
|
|
del sys.argv[1:4]
|
|
pycompat.execfile(file_to_run, run_globals)
|
|
if send_info != "-":
|
|
data_sender.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
__rope_start_everything()
|