I’ve released Jynx 0.2. Jynx is a Jython package which utilizes dynamic Java compilation from Jython and improves on Java scripting. With Jynx 0.2 two major new features are implemented now.
Annotation Extraction
In the initial Jynx 0.1 release an `annotation` object was defined which can be used as a decorator. A Python class such as
@JavaClass class TestClass(Object): @annotation("Test") @signature("public void _()") def test_report_test_failure(self): assertTrue("length of empty list is 0", len([]) != 0) |
equipped with the `JavaClass` decorator is compiled into a Java class on the fly which acts as a proxy for a Python object and provides the correct interface for being used within a Java framework which expects methods of a particular type signature and annotations. The class defined above can be used within JUnit 4.X.
Jynx 0.2 provides a new classmethod `extract` of the annotation class which can be used to extract Java annotation classes and acts as a factory function for Jython annotation objects.
# import Test annotation in JUnit 4.X from org.junit import Test # a Python annotation object Test = annotation.extract(Test) # keep a signature object as a parameter and returns a new Jython # annotation object. The Java code generator will create a method # with the correct signature and the @Test annotation Test = Test(signature("public void _()") @JavaClass class TestClass(Object): @Test def test_report_test_failure(self): assertTrue("length of empty list is 0", len([]) != 0) |
As we see there is no overhead left here. When programming against a Java API / framework, Jython annotations can be defined within a single file and used application wide.
Classpath Manipulation
For reasons which are not completely transparent to me Java doesn’t permit runtime classpath manipulations. The JDK defines an `addURL` method in a special classloader called `URLClassLoader`. This method is protected and cannot generally be accessed without reflection. Internally the Sun JVM uses such a loader class ( or a subclass of it ) and when you are willing to accept a hack and programming against an implementation detail you can use the JVMs default class loader and add new paths to a classpath:
from java.lang import ClassLoader systemLoader = ClassLoader.getSystemClassLoader() systemLoader.addURL("file:///C|junit-4.6.jar") |
Jynx defines a `ClassPath` class and a new `sys` module attribute `classpath`. Adding a file system path `P` to `sys.classpath` results in a method call
systemloader.addURL(URL("file:"+pathname2url(pth))) |
which converts the file system path into a Java URL object and adds it to the classpath. Additionally the same path is added to the `PYTHONPATH` via `sys.path`:
sys.classpath.append(r"C:\junit-4.6.jar") |
The advantage is that each Python package can maintain the Java packages it depends upon and no global `CLASSPATH` environment variable has to be adapted unless a Java or Jython class defines its own class loader.