blob: 2fdd53903fb1f8ad56241f19fd8c920f41d87c8a [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001#@PydevCodeAnalysisIgnore
2'''
Tor Norbyec667c1f2014-05-28 17:06:51 -07003@author Fabio Zadrozny
Tor Norbye3a2425a2013-11-04 10:16:08 -08004'''
5IS_PYTHON3K = 0
6try:
7 import __builtin__
8except ImportError:
Tor Norbyec667c1f2014-05-28 17:06:51 -07009 import builtins as __builtin__ # Python 3.0
Tor Norbye3a2425a2013-11-04 10:16:08 -080010 IS_PYTHON3K = 1
11
12try:
Tor Norbyec667c1f2014-05-28 17:06:51 -070013 True
14 False
15except NameError:
16 # If it's not defined, let's define it now.
17 setattr(__builtin__, 'True', 1) # Python 3.0 does not accept __builtin__.True = 1 in its syntax
Tor Norbye3a2425a2013-11-04 10:16:08 -080018 setattr(__builtin__, 'False', 0)
19
20import pydevd_constants
21
22try:
23 from java.lang import Thread
24 IS_JYTHON = True
25 SERVER_NAME = 'jycompletionserver'
Tor Norbyec667c1f2014-05-28 17:06:51 -070026 import _pydev_jy_imports_tipper # as _pydev_imports_tipper #changed to be backward compatible with 1.5
27 _pydev_imports_tipper = _pydev_jy_imports_tipper
Tor Norbye3a2425a2013-11-04 10:16:08 -080028
29except ImportError:
Tor Norbyec667c1f2014-05-28 17:06:51 -070030 # it is python
Tor Norbye3a2425a2013-11-04 10:16:08 -080031 IS_JYTHON = False
32 SERVER_NAME = 'pycompletionserver'
33 if pydevd_constants.USE_LIB_COPY:
34 from _pydev_threading import Thread
35 else:
36 from threading import Thread
Tor Norbyec667c1f2014-05-28 17:06:51 -070037 import _pydev_imports_tipper
Tor Norbye3a2425a2013-11-04 10:16:08 -080038
39
40if pydevd_constants.USE_LIB_COPY:
41 import _pydev_socket as socket
42else:
43 import socket
44
45import sys
46if sys.platform == "darwin":
Tor Norbyec667c1f2014-05-28 17:06:51 -070047 # See: https://sourceforge.net/projects/pydev/forums/forum/293649/topic/3454227
Tor Norbye3a2425a2013-11-04 10:16:08 -080048 try:
Tor Norbyec667c1f2014-05-28 17:06:51 -070049 import _CF # Don't fail if it doesn't work -- do it because it must be loaded on the main thread! @UnresolvedImport @UnusedImport
Tor Norbye3a2425a2013-11-04 10:16:08 -080050 except:
51 pass
52
53
Tor Norbyec667c1f2014-05-28 17:06:51 -070054# initial sys.path
Tor Norbye3a2425a2013-11-04 10:16:08 -080055_sys_path = []
56for p in sys.path:
Tor Norbyec667c1f2014-05-28 17:06:51 -070057 # changed to be compatible with 1.5
Tor Norbye3a2425a2013-11-04 10:16:08 -080058 _sys_path.append(p)
59
Tor Norbyec667c1f2014-05-28 17:06:51 -070060# initial sys.modules
Tor Norbye3a2425a2013-11-04 10:16:08 -080061_sys_modules = {}
62for name, mod in sys.modules.items():
63 _sys_modules[name] = mod
64
65
66import traceback
67
68if pydevd_constants.USE_LIB_COPY:
69 import _pydev_time as time
70else:
71 import time
72
73try:
74 import StringIO
75except:
76 import io as StringIO #Python 3.0
77
78try:
79 from urllib import quote_plus, unquote_plus
80except ImportError:
81 from urllib.parse import quote_plus, unquote_plus #Python 3.0
82
83INFO1 = 1
84INFO2 = 2
85WARN = 4
86ERROR = 8
87
88DEBUG = INFO1 | ERROR
89
90def dbg(s, prior):
91 if prior & DEBUG != 0:
92 sys.stdout.write('%s\n' % (s,))
93# f = open('c:/temp/test.txt', 'a')
94# print_ >> f, s
95# f.close()
96
97import pydev_localhost
98HOST = pydev_localhost.get_localhost() # Symbolic name meaning the local host
99
100MSG_KILL_SERVER = '@@KILL_SERVER_END@@'
101MSG_COMPLETIONS = '@@COMPLETIONS'
102MSG_END = 'END@@'
103MSG_INVALID_REQUEST = '@@INVALID_REQUEST'
104MSG_JYTHON_INVALID_REQUEST = '@@JYTHON_INVALID_REQUEST'
105MSG_CHANGE_DIR = '@@CHANGE_DIR:'
106MSG_OK = '@@MSG_OK_END@@'
Tor Norbye3a2425a2013-11-04 10:16:08 -0800107MSG_IMPORTS = '@@IMPORTS:'
108MSG_PYTHONPATH = '@@PYTHONPATH_END@@'
109MSG_CHANGE_PYTHONPATH = '@@CHANGE_PYTHONPATH:'
Tor Norbyec667c1f2014-05-28 17:06:51 -0700110MSG_JEDI = '@@MSG_JEDI:'
Tor Norbye3a2425a2013-11-04 10:16:08 -0800111MSG_SEARCH = '@@SEARCH'
112
113BUFFER_SIZE = 1024
114
115
116
117currDirModule = None
118
Tor Norbyec667c1f2014-05-28 17:06:51 -0700119def CompleteFromDir(directory):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800120 '''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700121 This is necessary so that we get the imports from the same directory where the file
Tor Norbye3a2425a2013-11-04 10:16:08 -0800122 we are completing is located.
123 '''
124 global currDirModule
125 if currDirModule is not None:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700126 if len(sys.path) > 0 and sys.path[0] == currDirModule:
127 del sys.path[0]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800128
Tor Norbyec667c1f2014-05-28 17:06:51 -0700129 currDirModule = directory
130 sys.path.insert(0, directory)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800131
132
133def ChangePythonPath(pythonpath):
134 '''Changes the pythonpath (clears all the previous pythonpath)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700135
Tor Norbye3a2425a2013-11-04 10:16:08 -0800136 @param pythonpath: string with paths separated by |
137 '''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700138
Tor Norbye3a2425a2013-11-04 10:16:08 -0800139 split = pythonpath.split('|')
140 sys.path = []
141 for path in split:
142 path = path.strip()
143 if len(path) > 0:
144 sys.path.append(path)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800145
Tor Norbyec667c1f2014-05-28 17:06:51 -0700146
Tor Norbye3a2425a2013-11-04 10:16:08 -0800147class Processor:
148
149 def __init__(self):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700150 # nothing to do
151 return
152
Tor Norbye3a2425a2013-11-04 10:16:08 -0800153 def removeInvalidChars(self, msg):
154 try:
155 msg = str(msg)
156 except UnicodeDecodeError:
157 pass
Tor Norbyec667c1f2014-05-28 17:06:51 -0700158
Tor Norbye3a2425a2013-11-04 10:16:08 -0800159 if msg:
160 try:
161 return quote_plus(msg)
162 except:
163 sys.stdout.write('error making quote plus in %s\n' % (msg,))
164 raise
165 return ' '
Tor Norbyec667c1f2014-05-28 17:06:51 -0700166
Tor Norbye3a2425a2013-11-04 10:16:08 -0800167 def formatCompletionMessage(self, defFile, completionsList):
168 '''
169 Format the completions suggestions in the following format:
170 @@COMPLETIONS(modFile(token,description),(token,description),(token,description))END@@
171 '''
172 compMsg = []
173 compMsg.append('%s' % defFile)
174 for tup in completionsList:
175 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700176
Tor Norbye3a2425a2013-11-04 10:16:08 -0800177 compMsg.append('(')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700178 compMsg.append(str(self.removeInvalidChars(tup[0]))) # token
Tor Norbye3a2425a2013-11-04 10:16:08 -0800179 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700180 compMsg.append(self.removeInvalidChars(tup[1])) # description
Tor Norbye3a2425a2013-11-04 10:16:08 -0800181
182 if(len(tup) > 2):
183 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700184 compMsg.append(self.removeInvalidChars(tup[2])) # args - only if function.
185
Tor Norbye3a2425a2013-11-04 10:16:08 -0800186 if(len(tup) > 3):
187 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700188 compMsg.append(self.removeInvalidChars(tup[3])) # TYPE
189
Tor Norbye3a2425a2013-11-04 10:16:08 -0800190 compMsg.append(')')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700191
Tor Norbye3a2425a2013-11-04 10:16:08 -0800192 return '%s(%s)%s' % (MSG_COMPLETIONS, ''.join(compMsg), MSG_END)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700193
Tor Norbye3a2425a2013-11-04 10:16:08 -0800194
195class T(Thread):
196
Tor Norbyec667c1f2014-05-28 17:06:51 -0700197 def __init__(self, port):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800198 Thread.__init__(self)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700199 self.ended = False
200 self.port = port
201 self.socket = None # socket to send messages.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800202 self.processor = Processor()
203
204
205 def connectToServer(self):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700206 import socket
207
Tor Norbye3a2425a2013-11-04 10:16:08 -0800208 self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
209 try:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700210 s.connect((HOST, self.port))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800211 except:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700212 sys.stderr.write('Error on connectToServer with parameters: host: %s port: %s\n' % (HOST, self.port))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800213 raise
214
215 def getCompletionsMessage(self, defFile, completionsList):
216 '''
217 get message with completions.
218 '''
219 return self.processor.formatCompletionMessage(defFile, completionsList)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700220
Tor Norbye3a2425a2013-11-04 10:16:08 -0800221 def getTokenAndData(self, data):
222 '''
223 When we receive this, we have 'token):data'
224 '''
225 token = ''
226 for c in data:
227 if c != ')':
228 token = token + c
229 else:
230 break;
Tor Norbyec667c1f2014-05-28 17:06:51 -0700231
Tor Norbye3a2425a2013-11-04 10:16:08 -0800232 return token, data.lstrip(token + '):')
233
Tor Norbyec667c1f2014-05-28 17:06:51 -0700234 def emulated_sendall(self, msg):
235 MSGLEN = 1024 * 20
236
237 totalsent = 0
238 while totalsent < MSGLEN:
239 sent = self.socket.send(msg[totalsent:])
240 if sent == 0:
241 return
242 totalsent = totalsent + sent
243
244
245 def send(self, msg):
246 if not hasattr(self.socket, 'sendall'):
247 #Older versions (jython 2.1)
248 self.emulated_sendall(msg)
249 else:
250 if IS_PYTHON3K:
251 self.socket.sendall(bytearray(msg, 'utf-8'))
252 else:
253 self.socket.sendall(msg)
254
255
Tor Norbye3a2425a2013-11-04 10:16:08 -0800256 def run(self):
257 # Echo server program
258 try:
259 import _pydev_log
260 log = _pydev_log.Log()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700261
262 dbg(SERVER_NAME + ' connecting to java server on %s (%s)' % (HOST, self.port) , INFO1)
263 # after being connected, create a socket as a client.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800264 self.connectToServer()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700265
266 dbg(SERVER_NAME + ' Connected to java server', INFO1)
267
268
269 while not self.ended:
Tor Norbye3a2425a2013-11-04 10:16:08 -0800270 data = ''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700271
Tor Norbye3a2425a2013-11-04 10:16:08 -0800272 while data.find(MSG_END) == -1:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700273 received = self.socket.recv(BUFFER_SIZE)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800274 if len(received) == 0:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700275 sys.exit(0) # ok, connection ended
Tor Norbye3a2425a2013-11-04 10:16:08 -0800276 if IS_PYTHON3K:
277 data = data + received.decode('utf-8')
278 else:
279 data = data + received
Tor Norbyec667c1f2014-05-28 17:06:51 -0700280
Tor Norbye3a2425a2013-11-04 10:16:08 -0800281 try:
282 try:
283 if data.find(MSG_KILL_SERVER) != -1:
284 dbg(SERVER_NAME + ' kill message received', INFO1)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700285 # break if we received kill message.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800286 self.ended = True
287 sys.exit(0)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700288
Tor Norbye3a2425a2013-11-04 10:16:08 -0800289 dbg(SERVER_NAME + ' starting keep alive thread', INFO2)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700290
Tor Norbye3a2425a2013-11-04 10:16:08 -0800291 if data.find(MSG_PYTHONPATH) != -1:
292 comps = []
293 for p in _sys_path:
294 comps.append((p, ' '))
Tor Norbyec667c1f2014-05-28 17:06:51 -0700295 self.send(self.getCompletionsMessage(None, comps))
296
Tor Norbye3a2425a2013-11-04 10:16:08 -0800297 else:
298 data = data[:data.rfind(MSG_END)]
Tor Norbyec667c1f2014-05-28 17:06:51 -0700299
Tor Norbye3a2425a2013-11-04 10:16:08 -0800300 if data.startswith(MSG_IMPORTS):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700301 data = data[len(MSG_IMPORTS):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800302 data = unquote_plus(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700303 defFile, comps = _pydev_imports_tipper.GenerateTip(data, log)
304 self.send(self.getCompletionsMessage(defFile, comps))
305
Tor Norbye3a2425a2013-11-04 10:16:08 -0800306 elif data.startswith(MSG_CHANGE_PYTHONPATH):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700307 data = data[len(MSG_CHANGE_PYTHONPATH):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800308 data = unquote_plus(data)
309 ChangePythonPath(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700310 self.send(MSG_OK)
311
312 elif data.startswith(MSG_JEDI):
313 data = data[len(MSG_JEDI):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800314 data = unquote_plus(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700315 line, column, encoding, path, source = data.split('|', 4)
316 try:
317 import jedi # @UnresolvedImport
318 except:
319 self.send(self.getCompletionsMessage(None, [('Error on import jedi', 'Error importing jedi', '')]))
320 else:
321 script = jedi.Script(
322 # Line +1 because it expects lines 1-based (and col 0-based)
323 source=source,
324 line=int(line) + 1,
325 column=int(column),
326 source_encoding=encoding,
327 path=path,
328 )
329 lst = []
330 for completion in script.completions():
331 t = completion.type
332 if t == 'class':
333 t = '1'
334
335 elif t == 'function':
336 t = '2'
337
338 elif t == 'import':
339 t = '0'
340
341 elif t == 'keyword':
342 continue # Keywords are already handled in PyDev
343
344 elif t == 'statement':
345 t = '3'
346
347 else:
348 t = '-1'
349
350 # gen list(tuple(name, doc, args, type))
351 lst.append((completion.name, '', '', t))
352 self.send(self.getCompletionsMessage('empty', lst))
353
354 elif data.startswith(MSG_SEARCH):
355 data = data[len(MSG_SEARCH):]
356 data = unquote_plus(data)
357 (f, line, col), foundAs = _pydev_imports_tipper.Search(data)
358 self.send(self.getCompletionsMessage(f, [(line, col, foundAs)]))
359
Tor Norbye3a2425a2013-11-04 10:16:08 -0800360 elif data.startswith(MSG_CHANGE_DIR):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700361 data = data[len(MSG_CHANGE_DIR):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800362 data = unquote_plus(data)
363 CompleteFromDir(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700364 self.send(MSG_OK)
365
Tor Norbye3a2425a2013-11-04 10:16:08 -0800366 else:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700367 self.send(MSG_INVALID_REQUEST)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800368 except SystemExit:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700369 self.send(self.getCompletionsMessage(None, [('Exit:', 'SystemExit', '')]))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800370 raise
Tor Norbyec667c1f2014-05-28 17:06:51 -0700371
Tor Norbye3a2425a2013-11-04 10:16:08 -0800372 except:
373 dbg(SERVER_NAME + ' exception occurred', ERROR)
374 s = StringIO.StringIO()
375 traceback.print_exc(file=s)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700376
Tor Norbye3a2425a2013-11-04 10:16:08 -0800377 err = s.getvalue()
378 dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700379 self.send(self.getCompletionsMessage(None, [('ERROR:', '%s\nLog:%s' % (err, log.GetContents()), '')]))
380
381
Tor Norbye3a2425a2013-11-04 10:16:08 -0800382 finally:
383 log.Clear()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700384
385 self.socket.close()
Tor Norbye3a2425a2013-11-04 10:16:08 -0800386 self.ended = True
Tor Norbyec667c1f2014-05-28 17:06:51 -0700387 sys.exit(0) # connection broken
388
389
Tor Norbye3a2425a2013-11-04 10:16:08 -0800390 except SystemExit:
391 raise
Tor Norbyec667c1f2014-05-28 17:06:51 -0700392 # No need to log SystemExit error
Tor Norbye3a2425a2013-11-04 10:16:08 -0800393 except:
394 s = StringIO.StringIO()
395 exc_info = sys.exc_info()
396
397 traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], limit=None, file=s)
398 err = s.getvalue()
399 dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
400 raise
401
Tor Norbyec667c1f2014-05-28 17:06:51 -0700402
403
Tor Norbye3a2425a2013-11-04 10:16:08 -0800404if __name__ == '__main__':
405
Tor Norbyec667c1f2014-05-28 17:06:51 -0700406 port = int(sys.argv[1]) # this is from where we want to receive messages.
407
408 t = T(port)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800409 dbg(SERVER_NAME + ' will start', INFO1)
410 t.start()
411 time.sleep(5)
412 t.join()