blob: 154ad630aca32d1381132a8084e71ab70f240cd4 [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
Tor Norbyec3d3a902014-09-04 13:24:04 -070020from pydevd_constants import IS_JYTHON
Tor Norbye3a2425a2013-11-04 10:16:08 -080021
Tor Norbyec3d3a902014-09-04 13:24:04 -070022if IS_JYTHON:
23 import java.lang
Tor Norbye3a2425a2013-11-04 10:16:08 -080024 SERVER_NAME = 'jycompletionserver'
Tor Norbyec667c1f2014-05-28 17:06:51 -070025 import _pydev_jy_imports_tipper # as _pydev_imports_tipper #changed to be backward compatible with 1.5
26 _pydev_imports_tipper = _pydev_jy_imports_tipper
Tor Norbye3a2425a2013-11-04 10:16:08 -080027
Tor Norbyec3d3a902014-09-04 13:24:04 -070028else:
Tor Norbyec667c1f2014-05-28 17:06:51 -070029 # it is python
Tor Norbye3a2425a2013-11-04 10:16:08 -080030 SERVER_NAME = 'pycompletionserver'
Tor Norbyec667c1f2014-05-28 17:06:51 -070031 import _pydev_imports_tipper
Tor Norbye3a2425a2013-11-04 10:16:08 -080032
33
Tor Norbye1aa2e092014-08-20 17:01:23 -070034from _pydev_imps import _pydev_socket as socket
Tor Norbye3a2425a2013-11-04 10:16:08 -080035
36import sys
37if sys.platform == "darwin":
Tor Norbyec667c1f2014-05-28 17:06:51 -070038 # See: https://sourceforge.net/projects/pydev/forums/forum/293649/topic/3454227
Tor Norbye3a2425a2013-11-04 10:16:08 -080039 try:
Tor Norbyec667c1f2014-05-28 17:06:51 -070040 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 -080041 except:
42 pass
43
44
Tor Norbyec667c1f2014-05-28 17:06:51 -070045# initial sys.path
Tor Norbye3a2425a2013-11-04 10:16:08 -080046_sys_path = []
47for p in sys.path:
Tor Norbyec667c1f2014-05-28 17:06:51 -070048 # changed to be compatible with 1.5
Tor Norbye3a2425a2013-11-04 10:16:08 -080049 _sys_path.append(p)
50
Tor Norbyec667c1f2014-05-28 17:06:51 -070051# initial sys.modules
Tor Norbye3a2425a2013-11-04 10:16:08 -080052_sys_modules = {}
53for name, mod in sys.modules.items():
54 _sys_modules[name] = mod
55
56
57import traceback
58
Tor Norbye1aa2e092014-08-20 17:01:23 -070059from _pydev_imps import _pydev_time as time
Tor Norbye3a2425a2013-11-04 10:16:08 -080060
61try:
62 import StringIO
63except:
64 import io as StringIO #Python 3.0
65
66try:
67 from urllib import quote_plus, unquote_plus
68except ImportError:
69 from urllib.parse import quote_plus, unquote_plus #Python 3.0
70
71INFO1 = 1
72INFO2 = 2
73WARN = 4
74ERROR = 8
75
76DEBUG = INFO1 | ERROR
77
78def dbg(s, prior):
79 if prior & DEBUG != 0:
80 sys.stdout.write('%s\n' % (s,))
81# f = open('c:/temp/test.txt', 'a')
82# print_ >> f, s
83# f.close()
Tor Norbye1aa2e092014-08-20 17:01:23 -070084
Tor Norbye3a2425a2013-11-04 10:16:08 -080085import pydev_localhost
86HOST = pydev_localhost.get_localhost() # Symbolic name meaning the local host
87
88MSG_KILL_SERVER = '@@KILL_SERVER_END@@'
89MSG_COMPLETIONS = '@@COMPLETIONS'
90MSG_END = 'END@@'
91MSG_INVALID_REQUEST = '@@INVALID_REQUEST'
92MSG_JYTHON_INVALID_REQUEST = '@@JYTHON_INVALID_REQUEST'
93MSG_CHANGE_DIR = '@@CHANGE_DIR:'
94MSG_OK = '@@MSG_OK_END@@'
Tor Norbye3a2425a2013-11-04 10:16:08 -080095MSG_IMPORTS = '@@IMPORTS:'
96MSG_PYTHONPATH = '@@PYTHONPATH_END@@'
97MSG_CHANGE_PYTHONPATH = '@@CHANGE_PYTHONPATH:'
Tor Norbyec667c1f2014-05-28 17:06:51 -070098MSG_JEDI = '@@MSG_JEDI:'
Tor Norbye3a2425a2013-11-04 10:16:08 -080099MSG_SEARCH = '@@SEARCH'
100
101BUFFER_SIZE = 1024
102
103
104
105currDirModule = None
106
Tor Norbyec667c1f2014-05-28 17:06:51 -0700107def CompleteFromDir(directory):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800108 '''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700109 This is necessary so that we get the imports from the same directory where the file
Tor Norbye3a2425a2013-11-04 10:16:08 -0800110 we are completing is located.
111 '''
112 global currDirModule
113 if currDirModule is not None:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700114 if len(sys.path) > 0 and sys.path[0] == currDirModule:
115 del sys.path[0]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800116
Tor Norbyec667c1f2014-05-28 17:06:51 -0700117 currDirModule = directory
118 sys.path.insert(0, directory)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800119
120
121def ChangePythonPath(pythonpath):
122 '''Changes the pythonpath (clears all the previous pythonpath)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700123
Tor Norbye3a2425a2013-11-04 10:16:08 -0800124 @param pythonpath: string with paths separated by |
125 '''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700126
Tor Norbye3a2425a2013-11-04 10:16:08 -0800127 split = pythonpath.split('|')
128 sys.path = []
129 for path in split:
130 path = path.strip()
131 if len(path) > 0:
132 sys.path.append(path)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800133
Tor Norbyec667c1f2014-05-28 17:06:51 -0700134
Tor Norbye3a2425a2013-11-04 10:16:08 -0800135class Processor:
136
137 def __init__(self):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700138 # nothing to do
139 return
140
Tor Norbye3a2425a2013-11-04 10:16:08 -0800141 def removeInvalidChars(self, msg):
142 try:
143 msg = str(msg)
144 except UnicodeDecodeError:
145 pass
Tor Norbyec667c1f2014-05-28 17:06:51 -0700146
Tor Norbye3a2425a2013-11-04 10:16:08 -0800147 if msg:
148 try:
149 return quote_plus(msg)
150 except:
151 sys.stdout.write('error making quote plus in %s\n' % (msg,))
152 raise
153 return ' '
Tor Norbyec667c1f2014-05-28 17:06:51 -0700154
Tor Norbye3a2425a2013-11-04 10:16:08 -0800155 def formatCompletionMessage(self, defFile, completionsList):
156 '''
157 Format the completions suggestions in the following format:
158 @@COMPLETIONS(modFile(token,description),(token,description),(token,description))END@@
159 '''
160 compMsg = []
161 compMsg.append('%s' % defFile)
162 for tup in completionsList:
163 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700164
Tor Norbye3a2425a2013-11-04 10:16:08 -0800165 compMsg.append('(')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700166 compMsg.append(str(self.removeInvalidChars(tup[0]))) # token
Tor Norbye3a2425a2013-11-04 10:16:08 -0800167 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700168 compMsg.append(self.removeInvalidChars(tup[1])) # description
Tor Norbye3a2425a2013-11-04 10:16:08 -0800169
170 if(len(tup) > 2):
171 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700172 compMsg.append(self.removeInvalidChars(tup[2])) # args - only if function.
173
Tor Norbye3a2425a2013-11-04 10:16:08 -0800174 if(len(tup) > 3):
175 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700176 compMsg.append(self.removeInvalidChars(tup[3])) # TYPE
177
Tor Norbye3a2425a2013-11-04 10:16:08 -0800178 compMsg.append(')')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700179
Tor Norbye3a2425a2013-11-04 10:16:08 -0800180 return '%s(%s)%s' % (MSG_COMPLETIONS, ''.join(compMsg), MSG_END)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700181
Tor Norbyec3d3a902014-09-04 13:24:04 -0700182class Exit(Exception):
183 pass
Tor Norbye3a2425a2013-11-04 10:16:08 -0800184
Tor Norbyec3d3a902014-09-04 13:24:04 -0700185class CompletionServer:
Tor Norbye3a2425a2013-11-04 10:16:08 -0800186
Tor Norbyec667c1f2014-05-28 17:06:51 -0700187 def __init__(self, port):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700188 self.ended = False
189 self.port = port
190 self.socket = None # socket to send messages.
Tor Norbyec3d3a902014-09-04 13:24:04 -0700191 self.exit_process_on_kill = True
Tor Norbye3a2425a2013-11-04 10:16:08 -0800192 self.processor = Processor()
193
194
195 def connectToServer(self):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700196 from _pydev_imps import _pydev_socket as socket
Tor Norbyec667c1f2014-05-28 17:06:51 -0700197
Tor Norbye3a2425a2013-11-04 10:16:08 -0800198 self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
199 try:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700200 s.connect((HOST, self.port))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800201 except:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700202 sys.stderr.write('Error on connectToServer with parameters: host: %s port: %s\n' % (HOST, self.port))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800203 raise
204
205 def getCompletionsMessage(self, defFile, completionsList):
206 '''
207 get message with completions.
208 '''
209 return self.processor.formatCompletionMessage(defFile, completionsList)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700210
Tor Norbye3a2425a2013-11-04 10:16:08 -0800211 def getTokenAndData(self, data):
212 '''
213 When we receive this, we have 'token):data'
214 '''
215 token = ''
216 for c in data:
217 if c != ')':
218 token = token + c
219 else:
220 break;
Tor Norbyec667c1f2014-05-28 17:06:51 -0700221
Tor Norbye3a2425a2013-11-04 10:16:08 -0800222 return token, data.lstrip(token + '):')
223
Tor Norbyec667c1f2014-05-28 17:06:51 -0700224 def emulated_sendall(self, msg):
225 MSGLEN = 1024 * 20
226
227 totalsent = 0
228 while totalsent < MSGLEN:
229 sent = self.socket.send(msg[totalsent:])
230 if sent == 0:
231 return
232 totalsent = totalsent + sent
233
234
235 def send(self, msg):
236 if not hasattr(self.socket, 'sendall'):
237 #Older versions (jython 2.1)
238 self.emulated_sendall(msg)
239 else:
240 if IS_PYTHON3K:
241 self.socket.sendall(bytearray(msg, 'utf-8'))
242 else:
243 self.socket.sendall(msg)
244
245
Tor Norbye3a2425a2013-11-04 10:16:08 -0800246 def run(self):
247 # Echo server program
248 try:
249 import _pydev_log
250 log = _pydev_log.Log()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700251
252 dbg(SERVER_NAME + ' connecting to java server on %s (%s)' % (HOST, self.port) , INFO1)
253 # after being connected, create a socket as a client.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800254 self.connectToServer()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700255
256 dbg(SERVER_NAME + ' Connected to java server', INFO1)
257
258
259 while not self.ended:
Tor Norbye3a2425a2013-11-04 10:16:08 -0800260 data = ''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700261
Tor Norbye3a2425a2013-11-04 10:16:08 -0800262 while data.find(MSG_END) == -1:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700263 received = self.socket.recv(BUFFER_SIZE)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800264 if len(received) == 0:
Tor Norbyec3d3a902014-09-04 13:24:04 -0700265 raise Exit() # ok, connection ended
Tor Norbye3a2425a2013-11-04 10:16:08 -0800266 if IS_PYTHON3K:
267 data = data + received.decode('utf-8')
268 else:
269 data = data + received
Tor Norbyec667c1f2014-05-28 17:06:51 -0700270
Tor Norbye3a2425a2013-11-04 10:16:08 -0800271 try:
272 try:
273 if data.find(MSG_KILL_SERVER) != -1:
274 dbg(SERVER_NAME + ' kill message received', INFO1)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700275 # break if we received kill message.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800276 self.ended = True
Tor Norbyec3d3a902014-09-04 13:24:04 -0700277 raise Exit()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700278
Tor Norbye3a2425a2013-11-04 10:16:08 -0800279 dbg(SERVER_NAME + ' starting keep alive thread', INFO2)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700280
Tor Norbye3a2425a2013-11-04 10:16:08 -0800281 if data.find(MSG_PYTHONPATH) != -1:
282 comps = []
283 for p in _sys_path:
284 comps.append((p, ' '))
Tor Norbyec667c1f2014-05-28 17:06:51 -0700285 self.send(self.getCompletionsMessage(None, comps))
286
Tor Norbye3a2425a2013-11-04 10:16:08 -0800287 else:
288 data = data[:data.rfind(MSG_END)]
Tor Norbyec667c1f2014-05-28 17:06:51 -0700289
Tor Norbye3a2425a2013-11-04 10:16:08 -0800290 if data.startswith(MSG_IMPORTS):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700291 data = data[len(MSG_IMPORTS):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800292 data = unquote_plus(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700293 defFile, comps = _pydev_imports_tipper.GenerateTip(data, log)
294 self.send(self.getCompletionsMessage(defFile, comps))
295
Tor Norbye3a2425a2013-11-04 10:16:08 -0800296 elif data.startswith(MSG_CHANGE_PYTHONPATH):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700297 data = data[len(MSG_CHANGE_PYTHONPATH):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800298 data = unquote_plus(data)
299 ChangePythonPath(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700300 self.send(MSG_OK)
301
302 elif data.startswith(MSG_JEDI):
303 data = data[len(MSG_JEDI):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800304 data = unquote_plus(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700305 line, column, encoding, path, source = data.split('|', 4)
306 try:
307 import jedi # @UnresolvedImport
308 except:
309 self.send(self.getCompletionsMessage(None, [('Error on import jedi', 'Error importing jedi', '')]))
310 else:
311 script = jedi.Script(
312 # Line +1 because it expects lines 1-based (and col 0-based)
313 source=source,
314 line=int(line) + 1,
315 column=int(column),
316 source_encoding=encoding,
317 path=path,
318 )
319 lst = []
320 for completion in script.completions():
321 t = completion.type
322 if t == 'class':
323 t = '1'
324
325 elif t == 'function':
326 t = '2'
327
328 elif t == 'import':
329 t = '0'
330
331 elif t == 'keyword':
332 continue # Keywords are already handled in PyDev
333
334 elif t == 'statement':
335 t = '3'
336
337 else:
338 t = '-1'
339
340 # gen list(tuple(name, doc, args, type))
341 lst.append((completion.name, '', '', t))
342 self.send(self.getCompletionsMessage('empty', lst))
343
344 elif data.startswith(MSG_SEARCH):
345 data = data[len(MSG_SEARCH):]
346 data = unquote_plus(data)
347 (f, line, col), foundAs = _pydev_imports_tipper.Search(data)
348 self.send(self.getCompletionsMessage(f, [(line, col, foundAs)]))
349
Tor Norbye3a2425a2013-11-04 10:16:08 -0800350 elif data.startswith(MSG_CHANGE_DIR):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700351 data = data[len(MSG_CHANGE_DIR):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800352 data = unquote_plus(data)
353 CompleteFromDir(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700354 self.send(MSG_OK)
355
Tor Norbye3a2425a2013-11-04 10:16:08 -0800356 else:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700357 self.send(MSG_INVALID_REQUEST)
Tor Norbyec3d3a902014-09-04 13:24:04 -0700358 except Exit:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700359 self.send(self.getCompletionsMessage(None, [('Exit:', 'SystemExit', '')]))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800360 raise
Tor Norbyec667c1f2014-05-28 17:06:51 -0700361
Tor Norbye3a2425a2013-11-04 10:16:08 -0800362 except:
363 dbg(SERVER_NAME + ' exception occurred', ERROR)
364 s = StringIO.StringIO()
365 traceback.print_exc(file=s)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700366
Tor Norbye3a2425a2013-11-04 10:16:08 -0800367 err = s.getvalue()
368 dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700369 self.send(self.getCompletionsMessage(None, [('ERROR:', '%s\nLog:%s' % (err, log.GetContents()), '')]))
370
371
Tor Norbye3a2425a2013-11-04 10:16:08 -0800372 finally:
373 log.Clear()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700374
375 self.socket.close()
Tor Norbye3a2425a2013-11-04 10:16:08 -0800376 self.ended = True
Tor Norbyec3d3a902014-09-04 13:24:04 -0700377 raise Exit() # connection broken
Tor Norbyec667c1f2014-05-28 17:06:51 -0700378
379
Tor Norbyec3d3a902014-09-04 13:24:04 -0700380 except Exit:
381 if self.exit_process_on_kill:
382 sys.exit(0)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700383 # No need to log SystemExit error
Tor Norbye3a2425a2013-11-04 10:16:08 -0800384 except:
385 s = StringIO.StringIO()
386 exc_info = sys.exc_info()
387
388 traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], limit=None, file=s)
389 err = s.getvalue()
390 dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
391 raise
392
Tor Norbyec667c1f2014-05-28 17:06:51 -0700393
394
Tor Norbye3a2425a2013-11-04 10:16:08 -0800395if __name__ == '__main__':
396
Tor Norbyec667c1f2014-05-28 17:06:51 -0700397 port = int(sys.argv[1]) # this is from where we want to receive messages.
398
Tor Norbyec3d3a902014-09-04 13:24:04 -0700399 t = CompletionServer(port)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800400 dbg(SERVER_NAME + ' will start', INFO1)
Tor Norbyec3d3a902014-09-04 13:24:04 -0700401 t.run()