Extending Jython
Extending Jython means writing Jython modules in Java. Jython modules is used here to mean Java classes that specifically behave like Jython modules. There is a distinction between this and just writing Java classes. Jython can use most any Java class, so there is no requirement to take additional steps to make that Java class look and act like a Jython module. However, there are situations where design begs for a true Jython module. The distinction occurs when a class should allow Jythonisms such as ordered and keyword parameters.
Listing 1 shows a sample Jython module written in Java. You can see that it is nothing more than a Java class. Although the mymod class in listing 1 is very simple, it demonstrates many aspects of creating Jython modules in Java. The parts of interest are the ClassDictInit interface, the related classDictInit method, static modifiers on each method, the __doc__ strings, and the PyException.
Listing 1A Jython Module Written in Java
// file mymod.java package org.python.demo.modules; import org.python.core.*; public class mymod implements ClassDictInit { public static void classDictInit(PyObject dict) { dict.__setitem__("__doc__", new PyString("Test class to confirm " + "builtin module")); dict.__delitem__("classDictInit"); } public static PyString __doc__fibTest = new PyString("fibTest(iteration) "+ "-> integer"); public static int fibTest(PyObject[] args, String[] kw) { ArgParser ap = new ArgParser("fibTest", args, kw, "iteration"); int iteration = ap.getInt(0); if (iteration < 1) throw new PyException(Py.ValueError, new PyString("Only integers >=1 allowed")); if (iteration == 1 || iteration == 2) return iteration; return fibTest(new PyObject[] { new PyInteger(iteration-1) }, new String[0]) + fibTest(new PyObject[] { new PyInteger(iteration-2) }, new String[0]); } }
The following is example output from running the mymod class as a module within Jython's interactive shell:
>>> from org.python.demo.modules import mymod >>> import time >>> >>> def test(method, iteration): ... t1 = time.time() results = apply(method, (iteration,)) ... print "The 25th fibonacci iteration is: ", results ... print "Time elapsed: ", time.time() - t1 ... >>> test(mymod.fibTest, 25) The 25th fibonacci iteration is: 121393 Time elapsed: 0.8799999952316284
If we continue this example with a comparable fibonacci function written in Jython, it clarifies one advantage to writing modules in Java- performance. You could additionally minimize some of the instantiations in the fibTest method if you passed on implementing Jython's flexible parameter scheme and use just public static int fibTest(int iteration). But even without that tweak, the Java sample above outperforms the Jython version shown here:
>>> def fib(iteration): ... if iteration < 1: raise ValueError, "Iteration must be >=1" ... if iteration < 3: return iteration ... return fib(iteration - 1) + fib(iteration -2) ... >>> test(fib, 25) The 25th fibonacci iteration is: 121393 Time elapsed: 1.590000033378601
ClassDictInit
A Jython module written as a Java class may control the module's __dict__ attribute if it implements the ClassDictInit interface. A class that implements the ClassDictInit interface must have a method like the following:
public static void classDictInit(PyObject dict)
Jython calls the classDictInit method when the class is initialized, allowing it to control the attribute names visible within Jython and their implementations. The mymod class in Listing 1 uses the classDictInit to set a __doc__ string and to remove itself, classDictInit, from names visible from Jython. In reality, this is unnecessary because the __doc__ string could be defined as follows:
public static String __doc__="Test class to confirm builtin module";
Without the __doc__ string assignment in classDictInit, the classDictInit method is not needed and would not appear in Jython anyway. The real usefulness of ClassDictInit comes when you have a complicated module implemented in multiple classes or a class includes multiple attributes you need to hide from Jython. All this is done by changing the dictionary object passed to the classDictInit method.
There is an additional means for controlling a Java method's visibility in Jython that is promising because of its simplicity, but is somewhat experimental at the time this was written. This involves the exception org.python.core.PyIgnoreMethodTag. The exception is never actually thrown, but any Java method that declares that it throws this exception is automatically removed from Jython's view.