Hi Joel,

On 4/4/06, Joel Hedlund <[EMAIL PROTECTED]> wrote:
Hi!

I'm trying learn Jython scripting for PyDev in order to implement feature
request #1450378 (mine :-).

http://sourceforge.net/tracker/index.php?func=detail&aid=1450378&group_id=85796&atid=577332

Fabio gave me a neat starter guide and a code example from the cvs java source
(see feature request page above), but I've got a few questions before I can
start getting productive.

1) How do I find out PyEdit object methods and data members?
What is the best way of finding out the available members and data methods of
an object in Jython scripts for PyDev? I'm a CPython programmer and I'm used to
having dir(), help() and the inspect module for peeking into strange objects,
but none of them seems to do me any good here. Afaik the help() function is not
implemented in Jython 2.1, but why can't I import the inspect module? Also, why
does dir(editor) return an empty list? (example at the end) What's the
recommended way of finding out information on object methods and data members
in Jython?

You can do the same things with jython. You can in your script (as it reloads automatically at runtime) do something as dir(editor) to see the PyEdit methods. Another option would be checking the code itself... I still don't have a good developers guide to explain how to download the code and make the project (altough there is a very 'simple' comment at http://pydev.sourceforge.net/roadmap.html on how to do it).

The 'catch' is that in jython not all things behave exactly as you'd expect, so, you have to make dir(editor.__class__)

2) Where do I find the source for the relevant superclasses?
I tried browsing the PyDev source cvs for the answer to my previous question,
but as I'm not a java black belt I feel a little disoriented. PyEdit objects
apparently have a .getSite() method (line 32 in pyedit_example2.py in the
starter guide), but the org.python.pydev.editor.PyEdit class does not define it.

http://cvs.sourceforge.net/viewcvs.py/pydev/org.python.pydev/src/org/python/pydev/editor/PyEdit.java?rev=1.91&view=markup

PyEdit implements IPyEdit, but how do I get to the source for it (or some docs)?

Well, if you download the code for pydev and create the project you should be able to grab all. So, now that I put all in place, I realized that I have not generated the javadocs for Pydev, so, there is still no default place for it. I'll try to set the developers guide and put the javadocs for it (I'm currently fixing some other bugs, so, if someone else was able to do it, it would be great, otherwise, in about 2-3 weeks I'll try to set it up).


3) How do I affect code in the editor with only a PyEdit object?
From the starter guide I understand that Pydev scripts communicate with PyDev
using an editor object, but the code example I got from Fabio apparently does
not. Instead it uses a PySelection object that it gets as its first argument.
The PyEdit argument is apparently not used.

Well, actually you'd do something such as editor.getDocument() and change things in the returned document (the PySelection is a wrapper to help on many things, as the document interface is kind of 'raw') .

This Jython scripting thing seems really powerful and I hope I'll be able to
produce some nifty things with it once I'm up to speed.

Hope that too!

Example:
$PYEDIT_SCRIPTS/pyedit_test.py:
---------------------------------------------------------------------
assert cmd is not None
assert editor is not None
dir(editor)
import inspect
---------------------------------------------------------------------

Yeah, Jython support to inspect is kind of poor...
So, you'd do dir(editor.__class__), but it might not get all, just for the class, and not superclasses.

 I'm attaching 2 examples that should how you'd get all the members (that's actually taken from the pydev code-completion stuff).
-- just copy and paste in a clean example to see how it would get all the members for the editor.

Cheers,

Fabio

import java

def dirObj(obj):
    ret = []
    found = java.util.HashMap()
    original = obj
    if hasattr(obj, '__class__') and obj.__class__ ==  java.lang.Class:

        #get info about superclasses
        classes = []
        classes.append(obj)
        c = obj.getSuperclass()
        while c != None:
            classes.append(c)
            c = c.getSuperclass()
        
        #get info about interfaces
        interfs = []
        for obj in classes:
            interfs.extend(obj.getInterfaces())
        classes.extend(interfs)
            
        #now is the time when we actually get info on the declared methods and 
fields
        for obj in classes:
            declaredMethods = obj.getDeclaredMethods()
            declaredFields = obj.getDeclaredFields()
            for i in range(len(declaredMethods)):
                name = declaredMethods[i].getName()
                ret.append(name)
                found.put(name, 1)
                
            for i in range(len(declaredFields)):
                name = declaredFields[i].getName()
                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) is not 1:
            ret.append(name)
            
    return ret


for p in dirObj(editor.__class__):
    print p
    
import StringIO
import traceback
from java.lang import StringBuffer
from java.lang import String
import java.lang
import sys

from org.python.core import PyReflectedFunction

from org.python import core

#completion types.
TYPE_UNKNOWN = -1
TYPE_IMPORT = 0
TYPE_CLASS = 1
TYPE_FUNCTION = 2
TYPE_ATTR = 3
TYPE_BUILTIN = 4
TYPE_PARAM = 5

TYPE_BUILTIN_AS_STR = '4'

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)
    if hasattr(mod, '__file__'):
        f = mod.__file__

    components = name.split('.')

    for comp in components[1:]:
        mod = getattr(mod, comp)
        if hasattr(mod, '__file__'):
            f = mod.__file__
    return f, mod

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 ):
    data = data.replace( '\n', '' )
    if data.endswith( '.' ):
        data = data.rstrip( '.' )
    
    f, mod = Find( data )
    tips = GenerateImportsTipForModule( mod )
    return f, tips
    

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+='[EMAIL PROTECTED] %s\n' % str(self.doc)
            
        if self.args:
            s+='[EMAIL PROTECTED] '
            for arg in self.args:
                s+=str(formatParamClassName(arg))
                s+='  '
        
        if self.varargs:
            s+='[EMAIL PROTECTED] '
            s+=str(self.varargs)
            
        if self.kwargs:
            s+='[EMAIL PROTECTED] '
            s+=str(self.kwargs)
            
        if self.ret:
            s+='[EMAIL PROTECTED] '
            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 range(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 range(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, e:
        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__') and obj.__class__ ==  java.lang.Class:

        #get info about superclasses
        classes = []
        classes.append(obj)
        c = obj.getSuperclass()
        while c != None:
            classes.append(c)
            c = c.getSuperclass()
        
        #get info about interfaces
        interfs = []
        for obj in classes:
            interfs.extend(obj.getInterfaces())
        classes.extend(interfs)
            
        #now is the time when we actually get info on the declared methods and 
fields
        for obj in classes:
            declaredMethods = obj.getDeclaredMethods()
            declaredFields = obj.getDeclaredFields()
            for i in range(len(declaredMethods)):
                name = declaredMethods[i].getName()
                ret.append(name)
                found.put(name, 1)
                
            for i in range(len(declaredFields)):
                name = declaredFields[i].getName()
                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) is not 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 GenerateImportsTipForModule( mod ):
    '''
    @param mod: the module from where we should get the completions
    '''
    ret = []
    
    dirComps = dirObj( mod )
    
    dontGetDocsOn = (float, int, str, tuple, list, type)
    for d in dirComps:

        args = ''
        doc = ''
        retType = TYPE_BUILTIN

        try:
            obj = getattr(mod, d)
        except AttributeError:
            #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, '', '', str(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:
                    print 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, str(retType))   )
        
            
    return ret




for p in GenerateImportsTipForModule(editor.__class__):
    print p
    

Reply via email to