PageRenderTime 29ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/org.python.pydev/PySrc/pycompletionserver.py

https://github.com/aparo/Pydev
Python | 381 lines | 313 code | 42 blank | 26 comment | 32 complexity | 9db19f08e05294751b12a9b4764f50d9 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. #@PydevCodeAnalysisIgnore
  2. '''
  3. @author Fabio Zadrozny
  4. '''
  5. IS_PYTHON3K = 0
  6. try:
  7. import __builtin__
  8. except ImportError:
  9. import builtins as __builtin__ # Python 3.0
  10. IS_PYTHON3K = 1
  11. try:
  12. True
  13. False
  14. except NameError:
  15. #If it's not defined, let's define it now.
  16. setattr(__builtin__, 'True', 1) #Python 3.0 does not accept __builtin__.True = 1 in its syntax
  17. setattr(__builtin__, 'False', 0)
  18. try:
  19. from java.lang import Thread
  20. IS_JYTHON = True
  21. SERVER_NAME = 'jycompletionserver'
  22. import jyimportsTipper #as importsTipper #changed to be backward compatible with 1.5
  23. importsTipper = jyimportsTipper
  24. except ImportError:
  25. #it is python
  26. IS_JYTHON = False
  27. SERVER_NAME = 'pycompletionserver'
  28. from threading import Thread
  29. import importsTipper
  30. import sys
  31. if sys.platform == "darwin":
  32. #See: https://sourceforge.net/projects/pydev/forums/forum/293649/topic/3454227
  33. try:
  34. import _CF #Don't fail if it doesn't work.
  35. except:
  36. pass
  37. #initial sys.path
  38. _sys_path = []
  39. for p in sys.path:
  40. #changed to be compatible with 1.5
  41. _sys_path.append(p)
  42. #initial sys.modules
  43. _sys_modules = {}
  44. for name, mod in sys.modules.items():
  45. _sys_modules[name] = mod
  46. import traceback
  47. import time
  48. try:
  49. import StringIO
  50. except:
  51. import io as StringIO #Python 3.0
  52. try:
  53. from urllib import quote_plus, unquote_plus
  54. except ImportError:
  55. from urllib.parse import quote_plus, unquote_plus #Python 3.0
  56. INFO1 = 1
  57. INFO2 = 2
  58. WARN = 4
  59. ERROR = 8
  60. DEBUG = INFO1 | ERROR
  61. def dbg(s, prior):
  62. if prior & DEBUG != 0:
  63. sys.stdout.write('%s\n' % (s,))
  64. # f = open('c:/temp/test.txt', 'a')
  65. # print_ >> f, s
  66. # f.close()
  67. import pydev_localhost
  68. HOST = pydev_localhost.get_localhost() # Symbolic name meaning the local host
  69. MSG_KILL_SERVER = '@@KILL_SERVER_END@@'
  70. MSG_COMPLETIONS = '@@COMPLETIONS'
  71. MSG_END = 'END@@'
  72. MSG_INVALID_REQUEST = '@@INVALID_REQUEST'
  73. MSG_JYTHON_INVALID_REQUEST = '@@JYTHON_INVALID_REQUEST'
  74. MSG_CHANGE_DIR = '@@CHANGE_DIR:'
  75. MSG_OK = '@@MSG_OK_END@@'
  76. MSG_BIKE = '@@BIKE'
  77. MSG_PROCESSING = '@@PROCESSING_END@@'
  78. MSG_PROCESSING_PROGRESS = '@@PROCESSING:%sEND@@'
  79. MSG_IMPORTS = '@@IMPORTS:'
  80. MSG_PYTHONPATH = '@@PYTHONPATH_END@@'
  81. MSG_CHANGE_PYTHONPATH = '@@CHANGE_PYTHONPATH:'
  82. MSG_SEARCH = '@@SEARCH'
  83. BUFFER_SIZE = 1024
  84. currDirModule = None
  85. def CompleteFromDir(dir):
  86. '''
  87. This is necessary so that we get the imports from the same dir where the file
  88. we are completing is located.
  89. '''
  90. global currDirModule
  91. if currDirModule is not None:
  92. del sys.path[currDirModule]
  93. sys.path.insert(0, dir)
  94. def ChangePythonPath(pythonpath):
  95. '''Changes the pythonpath (clears all the previous pythonpath)
  96. @param pythonpath: string with paths separated by |
  97. '''
  98. split = pythonpath.split('|')
  99. sys.path = []
  100. for path in split:
  101. path = path.strip()
  102. if len(path) > 0:
  103. sys.path.append(path)
  104. class KeepAliveThread(Thread):
  105. def __init__(self, socket):
  106. Thread.__init__(self)
  107. self.socket = socket
  108. self.processMsgFunc = None
  109. self.lastMsg = None
  110. def run(self):
  111. time.sleep(0.1)
  112. def send(s, msg):
  113. if IS_PYTHON3K:
  114. s.send(bytearray(msg, 'utf-8'))
  115. else:
  116. s.send(msg)
  117. while self.lastMsg == None:
  118. if self.processMsgFunc != None:
  119. s = MSG_PROCESSING_PROGRESS % quote_plus(self.processMsgFunc())
  120. sent = send(self.socket, s)
  121. else:
  122. sent = send(self.socket, MSG_PROCESSING)
  123. if sent == 0:
  124. sys.exit(0) #connection broken
  125. time.sleep(0.1)
  126. sent = send(self.socket, self.lastMsg)
  127. if sent == 0:
  128. sys.exit(0) #connection broken
  129. class Processor:
  130. def __init__(self):
  131. # nothing to do
  132. return
  133. def removeInvalidChars(self, msg):
  134. try:
  135. msg = str(msg)
  136. except UnicodeDecodeError:
  137. pass
  138. if msg:
  139. try:
  140. return quote_plus(msg)
  141. except:
  142. sys.stdout.write('error making quote plus in %s\n' % (msg,))
  143. raise
  144. return ' '
  145. def formatCompletionMessage(self, defFile, completionsList):
  146. '''
  147. Format the completions suggestions in the following format:
  148. @@COMPLETIONS(modFile(token,description),(token,description),(token,description))END@@
  149. '''
  150. compMsg = []
  151. compMsg.append('%s' % defFile)
  152. for tup in completionsList:
  153. compMsg.append(',')
  154. compMsg.append('(')
  155. compMsg.append(str(self.removeInvalidChars(tup[0]))) #token
  156. compMsg.append(',')
  157. compMsg.append(self.removeInvalidChars(tup[1])) #description
  158. if(len(tup) > 2):
  159. compMsg.append(',')
  160. compMsg.append(self.removeInvalidChars(tup[2])) #args - only if function.
  161. if(len(tup) > 3):
  162. compMsg.append(',')
  163. compMsg.append(self.removeInvalidChars(tup[3])) #TYPE
  164. compMsg.append(')')
  165. return '%s(%s)%s' % (MSG_COMPLETIONS, ''.join(compMsg), MSG_END)
  166. class T(Thread):
  167. def __init__(self, thisP, serverP):
  168. Thread.__init__(self)
  169. self.thisPort = thisP
  170. self.serverPort = serverP
  171. self.socket = None #socket to send messages.
  172. self.processor = Processor()
  173. def connectToServer(self):
  174. import socket
  175. self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  176. s.connect((HOST, self.serverPort))
  177. def getCompletionsMessage(self, defFile, completionsList):
  178. '''
  179. get message with completions.
  180. '''
  181. return self.processor.formatCompletionMessage(defFile, completionsList)
  182. def getTokenAndData(self, data):
  183. '''
  184. When we receive this, we have 'token):data'
  185. '''
  186. token = ''
  187. for c in data:
  188. if c != ')':
  189. token = token + c
  190. else:
  191. break;
  192. return token, data.lstrip(token + '):')
  193. def run(self):
  194. # Echo server program
  195. try:
  196. import socket
  197. import _pydev_log
  198. log = _pydev_log.Log()
  199. dbg(SERVER_NAME + ' creating socket' , INFO1)
  200. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  201. s.bind((HOST, self.thisPort))
  202. s.listen(1) #socket to receive messages.
  203. #we stay here until we are connected.
  204. #we only accept 1 client.
  205. #the exit message for the server is @@KILL_SERVER_END@@
  206. dbg(SERVER_NAME + ' waiting for connection' , INFO1)
  207. conn, addr = s.accept()
  208. time.sleep(0.5) #wait a little before connecting to JAVA server
  209. dbg(SERVER_NAME + ' waiting to java client' , INFO1)
  210. #after being connected, create a socket as a client.
  211. self.connectToServer()
  212. dbg(SERVER_NAME + ' Connected by ' + str(addr), INFO1)
  213. while 1:
  214. data = ''
  215. returnMsg = ''
  216. keepAliveThread = KeepAliveThread(self.socket)
  217. while data.find(MSG_END) == -1:
  218. received = conn.recv(BUFFER_SIZE)
  219. if len(received) == 0:
  220. sys.exit(0) #ok, connection ended
  221. if IS_PYTHON3K:
  222. data = data + received.decode('utf-8')
  223. else:
  224. data = data + received
  225. try:
  226. try:
  227. if data.find(MSG_KILL_SERVER) != -1:
  228. dbg(SERVER_NAME + ' kill message received', INFO1)
  229. #break if we received kill message.
  230. self.ended = True
  231. sys.exit(0)
  232. dbg(SERVER_NAME + ' starting keep alive thread', INFO2)
  233. keepAliveThread.start()
  234. if data.find(MSG_PYTHONPATH) != -1:
  235. comps = []
  236. for p in _sys_path:
  237. comps.append((p, ' '))
  238. returnMsg = self.getCompletionsMessage(None, comps)
  239. else:
  240. data = data[:data.rfind(MSG_END)]
  241. if data.startswith(MSG_IMPORTS):
  242. data = data.replace(MSG_IMPORTS, '')
  243. data = unquote_plus(data)
  244. defFile, comps = importsTipper.GenerateTip(data, log)
  245. returnMsg = self.getCompletionsMessage(defFile, comps)
  246. elif data.startswith(MSG_CHANGE_PYTHONPATH):
  247. data = data.replace(MSG_CHANGE_PYTHONPATH, '')
  248. data = unquote_plus(data)
  249. ChangePythonPath(data)
  250. returnMsg = MSG_OK
  251. elif data.startswith(MSG_SEARCH):
  252. data = data.replace(MSG_SEARCH, '')
  253. data = unquote_plus(data)
  254. (f, line, col), foundAs = importsTipper.Search(data)
  255. returnMsg = self.getCompletionsMessage(f, [(line, col, foundAs)])
  256. elif data.startswith(MSG_CHANGE_DIR):
  257. data = data.replace(MSG_CHANGE_DIR, '')
  258. data = unquote_plus(data)
  259. CompleteFromDir(data)
  260. returnMsg = MSG_OK
  261. elif data.startswith(MSG_BIKE):
  262. returnMsg = MSG_INVALID_REQUEST #No longer supported.
  263. else:
  264. returnMsg = MSG_INVALID_REQUEST
  265. except SystemExit:
  266. returnMsg = self.getCompletionsMessage(None, [('Exit:', 'SystemExit', '')])
  267. keepAliveThread.lastMsg = returnMsg
  268. raise
  269. except:
  270. dbg(SERVER_NAME + ' exception occurred', ERROR)
  271. s = StringIO.StringIO()
  272. traceback.print_exc(file=s)
  273. err = s.getvalue()
  274. dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
  275. returnMsg = self.getCompletionsMessage(None, [('ERROR:', '%s\nLog:%s' % (err, log.GetContents()), '')])
  276. finally:
  277. log.Clear()
  278. keepAliveThread.lastMsg = returnMsg
  279. conn.close()
  280. self.ended = True
  281. sys.exit(0) #connection broken
  282. except SystemExit:
  283. raise
  284. #No need to log SystemExit error
  285. except:
  286. s = StringIO.StringIO()
  287. exc_info = sys.exc_info()
  288. traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], limit=None, file=s)
  289. err = s.getvalue()
  290. dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
  291. raise
  292. if __name__ == '__main__':
  293. thisPort = int(sys.argv[1]) #this is from where we want to receive messages.
  294. serverPort = int(sys.argv[2])#this is where we want to write messages.
  295. t = T(thisPort, serverPort)
  296. dbg(SERVER_NAME + ' will start', INFO1)
  297. t.start()
  298. time.sleep(5)
  299. t.join()