summaryrefslogtreecommitdiff
path: root/python/helpers/pydev/_pydev_jy_imports_tipper.py
blob: 1691e3e1f1dcf82870002044a4b9ab57764e36a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
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 <type 'object'>
                                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 "<console>", 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'))