import StringIO import traceback import java.lang #@UnresolvedImport import sys from _pydev_tipper_common import DoFind try: False True except NameError: # version < 2.3 -- didn't have the True/False builtins import __builtin__ setattr(__builtin__, 'True', 1) setattr(__builtin__, 'False', 0) from org.python.core import PyReflectedFunction #@UnresolvedImport from org.python import core #@UnresolvedImport try: xrange except: xrange = range #completion types. TYPE_IMPORT = '0' TYPE_CLASS = '1' TYPE_FUNCTION = '2' TYPE_ATTR = '3' TYPE_BUILTIN = '4' TYPE_PARAM = '5' def _imp(name): try: return __import__(name) except: if '.' in name: sub = name[0:name.rfind('.')] return _imp(sub) else: s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path) raise RuntimeError(s) def Find(name): f = None if name.startswith('__builtin__'): if name == '__builtin__.str': name = 'org.python.core.PyString' elif name == '__builtin__.dict': name = 'org.python.core.PyDictionary' mod = _imp(name) parent = mod foundAs = '' if hasattr(mod, '__file__'): f = mod.__file__ components = name.split('.') old_comp = None for comp in components[1:]: try: #this happens in the following case: #we have mx.DateTime.mxDateTime.mxDateTime.pyd #but after importing it, mx.DateTime.mxDateTime does shadows access to mxDateTime.pyd mod = getattr(mod, comp) except AttributeError: if old_comp != comp: raise if hasattr(mod, '__file__'): f = mod.__file__ else: if len(foundAs) > 0: foundAs = foundAs + '.' foundAs = foundAs + comp old_comp = comp return f, mod, parent, foundAs def formatParamClassName(paramClassName): if paramClassName.startswith('['): if paramClassName == '[C': paramClassName = 'char[]' elif paramClassName == '[B': paramClassName = 'byte[]' elif paramClassName == '[I': paramClassName = 'int[]' elif paramClassName.startswith('[L') and paramClassName.endswith(';'): paramClassName = paramClassName[2:-1] paramClassName += '[]' return paramClassName def GenerateTip(data, log=None): data = data.replace('\n', '') if data.endswith('.'): data = data.rstrip('.') f, mod, parent, foundAs = Find(data) tips = GenerateImportsTipForModule(mod) return f, tips #======================================================================================================================= # Info #======================================================================================================================= class Info: def __init__(self, name, **kwargs): self.name = name self.doc = kwargs.get('doc', None) self.args = kwargs.get('args', ()) #tuple of strings self.varargs = kwargs.get('varargs', None) #string self.kwargs = kwargs.get('kwargs', None) #string self.ret = kwargs.get('ret', None) #string def basicAsStr(self): '''@returns this class information as a string (just basic format) ''' s = 'function:%s args=%s, varargs=%s, kwargs=%s, docs:%s' % \ (str(self.name), str(self.args), str(self.varargs), str(self.kwargs), str(self.doc)) return s def getAsDoc(self): s = str(self.name) if self.doc: s += '\n@doc %s\n' % str(self.doc) if self.args: s += '\n@params ' for arg in self.args: s += str(formatParamClassName(arg)) s += ' ' if self.varargs: s += '\n@varargs ' s += str(self.varargs) if self.kwargs: s += '\n@kwargs ' s += str(self.kwargs) if self.ret: s += '\n@return ' s += str(formatParamClassName(str(self.ret))) return str(s) def isclass(cls): return isinstance(cls, core.PyClass) def ismethod(func): '''this function should return the information gathered on a function @param func: this is the function we want to get info on @return a tuple where: 0 = indicates whether the parameter passed is a method or not 1 = a list of classes 'Info', with the info gathered from the function this is a list because when we have methods from java with the same name and different signatures, we actually have many methods, each with its own set of arguments ''' try: if isinstance(func, core.PyFunction): #ok, this is from python, created by jython #print_ ' PyFunction' def getargs(func_code): """Get information about the arguments accepted by a code object. Three things are returned: (args, varargs, varkw), where 'args' is a list of argument names (possibly containing nested lists), and 'varargs' and 'varkw' are the names of the * and ** arguments or None.""" nargs = func_code.co_argcount names = func_code.co_varnames args = list(names[:nargs]) step = 0 varargs = None if func_code.co_flags & func_code.CO_VARARGS: varargs = func_code.co_varnames[nargs] nargs = nargs + 1 varkw = None if func_code.co_flags & func_code.CO_VARKEYWORDS: varkw = func_code.co_varnames[nargs] return args, varargs, varkw args = getargs(func.func_code) return 1, [Info(func.func_name, args=args[0], varargs=args[1], kwargs=args[2], doc=func.func_doc)] if isinstance(func, core.PyMethod): #this is something from java itself, and jython just wrapped it... #things to play in func: #['__call__', '__class__', '__cmp__', '__delattr__', '__dir__', '__doc__', '__findattr__', '__name__', '_doget', 'im_class', #'im_func', 'im_self', 'toString'] #print_ ' PyMethod' #that's the PyReflectedFunction... keep going to get it func = func.im_func if isinstance(func, PyReflectedFunction): #this is something from java itself, and jython just wrapped it... #print_ ' PyReflectedFunction' infos = [] for i in xrange(len(func.argslist)): #things to play in func.argslist[i]: #'PyArgsCall', 'PyArgsKeywordsCall', 'REPLACE', 'StandardCall', 'args', 'compare', 'compareTo', 'data', 'declaringClass' #'flags', 'isStatic', 'matches', 'precedence'] #print_ ' ', func.argslist[i].data.__class__ #func.argslist[i].data.__class__ == java.lang.reflect.Method if func.argslist[i]: met = func.argslist[i].data name = met.getName() try: ret = met.getReturnType() except AttributeError: ret = '' parameterTypes = met.getParameterTypes() args = [] for j in xrange(len(parameterTypes)): paramTypesClass = parameterTypes[j] try: try: paramClassName = paramTypesClass.getName() except: paramClassName = paramTypesClass.getName(paramTypesClass) except AttributeError: try: paramClassName = repr(paramTypesClass) #should be something like paramClassName = paramClassName.split('\'')[1] except: paramClassName = repr(paramTypesClass) #just in case something else happens... it will at least be visible #if the parameter equals [C, it means it it a char array, so, let's change it a = formatParamClassName(paramClassName) #a = a.replace('[]','Array') #a = a.replace('Object', 'obj') #a = a.replace('String', 's') #a = a.replace('Integer', 'i') #a = a.replace('Char', 'c') #a = a.replace('Double', 'd') args.append(a) #so we don't leave invalid code info = Info(name, args=args, ret=ret) #print_ info.basicAsStr() infos.append(info) return 1, infos except Exception: s = StringIO.StringIO() traceback.print_exc(file=s) return 1, [Info(str('ERROR'), doc=s.getvalue())] return 0, None def ismodule(mod): #java modules... do we have other way to know that? if not hasattr(mod, 'getClass') and not hasattr(mod, '__class__') \ and hasattr(mod, '__name__'): return 1 return isinstance(mod, core.PyModule) def dirObj(obj): ret = [] found = java.util.HashMap() original = obj if hasattr(obj, '__class__'): if obj.__class__ == java.lang.Class: #get info about superclasses classes = [] classes.append(obj) try: c = obj.getSuperclass() except TypeError: #may happen on jython when getting the java.lang.Class class c = obj.getSuperclass(obj) while c != None: classes.append(c) c = c.getSuperclass() #get info about interfaces interfs = [] for obj in classes: try: interfs.extend(obj.getInterfaces()) except TypeError: interfs.extend(obj.getInterfaces(obj)) classes.extend(interfs) #now is the time when we actually get info on the declared methods and fields for obj in classes: try: declaredMethods = obj.getDeclaredMethods() except TypeError: declaredMethods = obj.getDeclaredMethods(obj) try: declaredFields = obj.getDeclaredFields() except TypeError: declaredFields = obj.getDeclaredFields(obj) for i in xrange(len(declaredMethods)): name = declaredMethods[i].getName() ret.append(name) found.put(name, 1) for i in xrange(len(declaredFields)): name = declaredFields[i].getName() ret.append(name) found.put(name, 1) elif isclass(obj.__class__): d = dir(obj.__class__) for name in d: ret.append(name) found.put(name, 1) #this simple dir does not always get all the info, that's why we have the part before #(e.g.: if we do a dir on String, some methods that are from other interfaces such as #charAt don't appear) d = dir(original) for name in d: if found.get(name) != 1: ret.append(name) return ret def formatArg(arg): '''formats an argument to be shown ''' s = str(arg) dot = s.rfind('.') if dot >= 0: s = s[dot + 1:] s = s.replace(';', '') s = s.replace('[]', 'Array') if len(s) > 0: c = s[0].lower() s = c + s[1:] return s def Search(data): '''@return file, line, col ''' data = data.replace('\n', '') if data.endswith('.'): data = data.rstrip('.') f, mod, parent, foundAs = Find(data) try: return DoFind(f, mod), foundAs except: return DoFind(f, parent), foundAs def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, filter=lambda name:True): ''' @param obj_to_complete: the object from where we should get the completions @param dirComps: if passed, we should not 'dir' the object and should just iterate those passed as a parameter @param getattr: the way to get a given object from the obj_to_complete (used for the completer) @param filter: a callable that receives the name and decides if it should be appended or not to the results @return: list of tuples, so that each tuple represents a completion with: name, doc, args, type (from the TYPE_* constants) ''' ret = [] if dirComps is None: dirComps = dirObj(obj_to_complete) for d in dirComps: if d is None: continue if not filter(d): continue args = '' doc = '' retType = TYPE_BUILTIN try: obj = getattr(obj_to_complete, d) except (AttributeError, java.lang.NoClassDefFoundError): #jython has a bug in its custom classloader that prevents some things from working correctly, so, let's see if #we can fix that... (maybe fixing it in jython itself would be a better idea, as this is clearly a bug) #for that we need a custom classloader... we have references from it in the below places: # #http://mindprod.com/jgloss/classloader.html #http://www.javaworld.com/javaworld/jw-03-2000/jw-03-classload-p2.html #http://freshmeat.net/articles/view/1643/ # #note: this only happens when we add things to the sys.path at runtime, if they are added to the classpath #before the run, everything goes fine. # #The code below ilustrates what I mean... # #import sys #sys.path.insert(1, r"C:\bin\eclipse310\plugins\org.junit_3.8.1\junit.jar" ) # #import junit.framework #print_ dir(junit.framework) #shows the TestCase class here # #import junit.framework.TestCase # #raises the error: #Traceback (innermost last): # File "", line 1, in ? #ImportError: No module named TestCase # #whereas if we had added the jar to the classpath before, everything would be fine by now... ret.append((d, '', '', retType)) #that's ok, private things cannot be gotten... continue else: isMet = ismethod(obj) if isMet[0]: info = isMet[1][0] try: args, vargs, kwargs = info.args, info.varargs, info.kwargs doc = info.getAsDoc() r = '' for a in (args): if len(r) > 0: r += ', ' r += formatArg(a) args = '(%s)' % (r) except TypeError: traceback.print_exc() args = '()' retType = TYPE_FUNCTION elif isclass(obj): retType = TYPE_CLASS elif ismodule(obj): retType = TYPE_IMPORT #add token and doc to return - assure only strings. ret.append((d, doc, args, retType)) return ret if __name__ == "__main__": sys.path.append(r'D:\dev_programs\eclipse_3\310\eclipse\plugins\org.junit_3.8.1\junit.jar') sys.stdout.write('%s\n' % Find('junit.framework.TestCase'))