3

I'm having trouble trying to run jython code embedded in a compiled groovy application. The same jython code works fine when it is embedded in a java application (The Grinder 3.1)

In the groovy code I use the org.python.util.PythonInterpreter class (from jython 2.2.1) to create a callable instance of a class called TestRunner (this is a requirement from The Grinder).

Illustrative jython code example:

class TestRunner:
    def __init__(self):
        doinitstuff()
    def __call__():
        a = A()
        a.work()

class A:
    def __init__(self):
        self.b = B()

    def work(self):
        print "Calling methodcall"
        self.b.methodcall()

class B:
    def __init__(self):
        self.webservice = WebServiceStubImplementedInJava()
        print str(self.webservice)

    def methodcall(self):
        print "In methodcall"
        try:
            return self.webservice.soapmethod()
        except:
            log_error()
            raise

Here is the output when I run the above code:

  1. The TestRunners __call__() method will invoke the work() method of a class A instance, and the webservice stub's toString output is printed.
  2. The "Calling methodcall" message is printed.
  3. The "In methodcall" message is never printed, and instead I get: AttributeError: 'javainstance' object has no attribute '__call__'. The stacktrace ends with self.b.methodcall()

Do you have any idea why the invocation of self.b.methodcall() should result in AttributeError: 'javainstance' object has no attribute __call__

Adding some context to the problem...

  1. I'm trying to use a Groovy class to execute the work that a Grinder worker thread would perform when we performance test our product.
    • I use groovy just for the sake of "less verbose code", but might have to switch over to plain old java if it's groovy that causes the problem.
  2. The reason for doing this is that I need to find out which files are actually used by Grinder for a given test scenario.
    • We have hundreds of *.py files and configuration files etc, but only a subset of them are used for one specific test scenario. All of them are used in some test scenario.
    • This makes it quite hard for a "beginner" to understand how to configure a test so I'm trying to build a "test configurator wizard" that set up a test scenario without forcing the user/tester to edit all config files manually.
    • This wizard will collect the relevant files from a "repository" and put them in a folder where the "Grinder Console" can present them to the user.

So, the way I use to find out which files are used by Grinder is to use AOP (AspectJ) to capture all calls to java.io.FileInputStream(java.io.File) from any code in the org.python.util and org.python.core packages. The "advice" I apply to these join-points is to print the file name to System.out. I use load-time weaving for this, so I can run the groovy/java/jython code with our without AOP enabled. The AttributeErrorproblem occurs regardless if I have AOP enabled or not.

I have a vague suspicion that the AttributeError problem could be caused by some classloader mismatch when a "groovy" class executes the PythonInterpreter methods, but I'm far from sure about this. I'm not sure if groovy is doing any kind of runtime bytecode editing when it loads classes and if that confuses the PythonInterpreter.

The groovy code itself is precompiled so I use the regular java.exe to launch the process.

Ola
  • 31
  • 3
  • Well obviously the error message is because something is trying to access the `__call__()` member of a `javainstance` object, and that member does not exist. Given that the above code is not doing anything with a `javainstance` object or a `__call__()` method in the place you say the error is, there must be some extra details from the real code that are missing here. Could you create a copy of your whole project and then distil it right down to the smallest possible code that still produces the error? – Cam Jackson Sep 14 '11 at 06:01
  • Also, a nitpick: Your classes should inherit from object, although your real code may be doing this already and you just forgot to put it here. – Cam Jackson Sep 14 '11 at 06:02
  • Here is a stacktrace from my attempt to run the Grinder (jython) script from Groovy: Traceback (most recent call last): File "", line 80, in __call__ File "C:\Users\ola\Documents\workspaces\QA_dev\Performance\Grinder\.\actors\admin_soaptester_mc.py", line 214, in work f(self) File "C:\Users\ola\Documents\workspaces\QA_dev\Performance\Grinder\.\actors\admin_soaptester_mc.py", line 103, in _getMoneyStats self.soap.getTotalBalance() AttributeError: 'javainstance' object has no attribute '__call__' – Ola Sep 16 '11 at 07:58
  • The admin_soaptester_mc.py is of course represented by the "A" class in the code example – Ola Sep 16 '11 at 08:07
  • @camjackson About the "nitpick", none of the jython classes in the real code specifies that they extend object. It still works (in Grinder). Should it be `class A(object):` or `import java.lang.Object\nclass A(Object):` to be formally correct? – Ola Sep 16 '11 at 08:15
  • Another thing about the admin_soaptester_mc class is that it collects references to it's methods in an array and the work method randomly selects one of these methods from the array and executes it. That's why the stacktrace says `in work\n f(self)` – Ola Sep 16 '11 at 08:24
  • @camjackson About the first comment, I can try to reproduce the error with as little code as possible, but it will take a while. And I can't "publish" it with all details about the inner workings of our product, but I will try to obfuscate the code a bit but still show the "flow". I fear it will be quite a lot of code anyway so I'm not sure how to "publish" it on stackoverflow.com – Ola Sep 16 '11 at 08:36
  • I am referring to the *Python* type, `object`. Inheriting from it is done to make your own classes behave more like built-in types, among other things. You don't need to `import` anything to inherit from it. More info [here](http://docs.python.org/release/2.5.2/ref/node33.html). – Cam Jackson Sep 18 '11 at 23:30
  • Hmmm. I'm afraid I'm not very familiar with the Java world, so you've lost me a little. My only Java experience is through Jython, which I'm only using because I wanted to use Swing. I don't think I'm going to be much help here. The reason I suggested reproducing the error with a subset of your *actual* code was that the sample you posted doesn't really reveal much. I.e- I can copy it into an editor, replace `WebServiceStubImplementedInJava` with say, a string, and everything works fine when I instantiate a `TestRunner` and call its `__call__()`. The problem is clearly in some other code. – Cam Jackson Sep 18 '11 at 23:49
  • One thing I did notice in your code is that `TestRunner.__call__()` does not have `self` as its first parameter, which it should. Although, Jython should warn you about this, so I suspect that's just a mistake in your sample code, and not the real problem. – Cam Jackson Sep 18 '11 at 23:52
  • If I got the information in the question right, then the error message is from Jython. So it is most probably the Jython environment, that has not been set up correctly. You mentioned a possible classloader problem. I can tell you as much that Groovy does not do runtime bytecode editing. But what I cannot tell you is what class loader structure is in use here. For that I need the Groovy side of this. And that you don't show – blackdrag May 22 '12 at 07:43

0 Answers0