Screenshot CONTENTS Screenshot

Embedding and Extending Jython in Java

Java graphics chic01.gif Java apps that would benefit from Jython's high-level, dynamic nature can easily employ Jython through an embedding interpreter. Additionally, Jython apps in need of additional speed can contain modules written in Java, allowing them to take advantage of Java's efficient bytecode without affecting module-like functionality. These implementations are dubbed embedding and extending. Both are approaches to merging multiple languages in a single app. Despite the fact that embedding and extending has long been commonplace in many languages, the Jython-Java combination may very well exceed all other languages in this regard. The C implementation of Python is highly praised for its utility in merging languages, or what Python programmers often call "gluing." Jython inherits this success, but also exceeds it by minimizing the seams and gaps between languages. Passing objects between Java and Jython is symmetrically seamless—Java objects can be passed into the interpreter, and Jython objects can be passed out to Java. Not specialized objects—any object. All of this happens without having to incur the cost of customizing these objects for the sake of the other language. Additionally, embedding and initializing the interpreter, setting and fetching objects, and other related tasks are very intuitive and easy with Jython. This ease, Jython's advantages, and the seamless integration of Java and Jython make Jython and Java the pinnacle combination for multi-language apps. The conspicuous organization of this chapter is two sections, one on embedding and the other on extending Jython.

Embedding Jython

Embedding Jython within Java has many uses but is particularly helpful when needing an interactive command interpreter, when creating diverse forms of output (report generation), when employing dynamic configuration, and when specific elements of an app require frequent changes. Because many others are mining additional uses for embedded Jython, this list will no doubt grow. Even ignoring the design motivations, there is cause for embedding Jython just to leverage its advantages. You get increased readability, rapid development, a short learning curve and more with only the addition of an embedded interpreter. The only downsides are that you incur the memory overhead of the interpreter and require additional classes in your app. Three classes from the org.python.util package provide the necessary means to embed Jython in Java. These classes are PythonInterpreter, InteractiveInterpreter, and InteractiveConsole. The order in which they appear also reflects their hierarchy. Each class is a subclass of the previous in the list. Screenshot shows the hierarchy of these classes along with the methods associated with that class.

Screenshot The interpreter hierarchy and associated methods.

Java graphics 09fig01.gif


PythonInterpreter

Embedding Jython most frequently equates to using an instance of the Java class org.python.util.PythonInterpreter within a Java app. The jython.jar file installed with Jython contains this class. Embedding this class within Java requires the addition of but two statements to Java code:

import org.python.util.PythonInterpreter; PythonInterpreter interp = new PythonInterpreter; 


The interp identifier in the preceding example, dubbed the interpreter instance, or just interpreter, is best thought of as a container, or bubble within Java. This analogy applies to most objects, but is especially valuable here because it enforces the distinction between using the Jython environment and using a Jython class. It is inside this bubble that the Jython interpreter runs. Java code surrounding the interpreter is unaware of what is within the bubble, and objects within the bubble are unaware of the outside world. Creating the bubble is extremely simple as noted in the two example lines above, so the concern is for steps preceding the instantiation of the interpreter and how to use it after its instantiation. The following sections detail how to initialize the Jython environment, how to instantiate the interpreter, how to use the interpreter instance, and how to pass Jython and Java object in and out of the embedded interpreter.

Initialization

The Jython environment depends on a number of properties. Properties such as the python.home, python.path, and python.security.respectJavaAccessibility are normally loaded from the registry file at startup or are set with command-line options. These properties and others ensure proper module loading and determine other aspects of Jython's behavior. Embedding Jython within Java allows opportunity for explicitly setting these properties before creating the interpreter object. Properties are set when you use the static initialize method in the PythonInterpreter class. This explicit control over properties, paths and settings is an advantage over using jythonc -compiled classes because you simply have more control when embedding.

Properties and Initialization

You should initialize the PythonInterpreter before creating an interpreter instance. The initialization is when Jython properties are set, when the registry file is processed, and when Jython's package manager searches as caches Java package information. The initialize step is meant literally because it uses the static initialize method in the PythonInterpreter class. Following is the signature for the initialize method:

public static void initialize(Properties preProperties, Properties postProperties, String[] argv)) 


The parameters can also be read as "old properties," "new properties," and "command-line arguments." The old, or preProperties are often set to System.getProperties(). When the preProperties are set to System.getProperties(), they contain those properties that were set with Java's -D command-line switch. The new, or postProperties, are most often set to a new java.util.Properties instance with some entries already added.The third and final argument of the initialize method is a String array. This string array becomes Jython's sys.argv. If Jython is unable to find its home directory, it may not be able to read the registry file, or locate modules. You can ensure an embedded interpreter finds its home directory by setting the python.home property in the initialize method. To do so, you would use the following:

Properties props = new Properties(); props.put("python.home", "/usr/local/jython-2.1"); // *nix //props.put("python.home", "c:\\jython2.1"); // windows PythonInterpreter.initialize(System.getProperties(), props, new String[0]); 


Initializing the PythonInterpreter also processes Jython's registry file (if found). Settings within the registry file create a third set of properties. The three sets are old properties (from System.getProperties() in the above example), registry properties (those defined in the registry file), and the new properties that appear as the second argument to the initialize method ("props" in the preceding example). The order in which these properties appear in the previous sentence has significance. This order determines which property takes precedence when a given property is defined in multiple places. Old properties are loaded first, followed by registry properties, and finally new properties. This precedence means that registry properties override old properties, and that new properties override both registry and system properties. Listing 9.1 is a Java class that embeds the PythonInterpreter. Not only does Listing 9.1 demonstrate the setting of properties and initializing the interpreter, but it also serves as a convenient base class for many of the examples in this chapter. A package called org.python.demo is used with Listing 9.1 and future Java listings to help organize examples. The Embedding class in the listing assumes that a property explicitly declared on the command-line with the -D option has the highest precedence, so it specifically sets command-line properties found in System. getProperties() that begin with the string python in the new properties, postProps. The Embedding class also sets the python.home property in the sysProps object, but only if it was not set on the command-line. This helps ensure that the interpreter finds and processes the registry file. In this scenario, the default python.home property has lowest priority, but that doesn't matter because it is usually not set in the registry. The registry properties load next. Then the new properties, altered to include those set with the -D switch, are set and override any previous values duplicate properties have.

Listing 9.1 A Simple Embedded PythonInterpreter
// file: Embedding.java package org.python.demo; import java.util.Properties; import org.python.util.PythonInterpreter; import java.util.Enumeration; public class Embedding {
 protected PythonInterpreter interp; public static void main(String[] args)) {
 Embedding embed = new Embedding(); embed.initInterpreter(args); embed.test(); } protected void initInterpreter(String[] argv)) {
 // Get preProperties postProperties, and System properties Properties postProps = new Properties(); Properties sysProps = System.getProperties(); // set default python.home property if (sysProps.getProperty("python.home")==null) sysProps.put("python.home", "c:\\jython 2.1a1"); // put System properties (those set with -D) in postProps Enumeration e = sysProps.propertyNames(); while ( e.hasMoreElements() ) {
 String name = (String)e.nextElement(); if (name.startsWith("python.")) postProps.put(name, System.getProperty(name)); } // Here's the initialization step PythonInterpreter.initialize(sysProps, postProps, argv); //instantiate- note that it is AFTER initialize interp = new PythonInterpreter(); } public void test() {
 // Print system state values to confirm proper initialization interp.exec("import sys"); interp.exec("print"); // Add empty line for clarity interp.exec("print 'sys.prefix=', sys.prefix"); interp.exec("print 'sys.argv=', sys.argv"); interp.exec("print 'sys.path=', sys.path"); interp.exec("print 'sys.cachedir=', sys.cachedir"); interp.exec("print"); // Another blank for clarity } } 


The test() method in Listing 9.1 is the method that many following example, will override. Within this method is where you will test the interpreter commands discussed in associated text. The test method in the preceding listing executes some Jython statements with the interpreter's exec method. The Jython statements import Jython's sys module, then use sys to print information confirming properties are set appropriately. The details of using the exec method appear later in the section, but for now it is sufficient to note the careful use of single and double quotations within each exec method. Listing 9.1, and properties that use system paths in general, require special attention to make sure they comply with platform-specific rules for path and directory separators (such as ; and \\ for Windows and : and / for *nix). Also, note that Listing 9.1 assumes your python.home directory is C:\jython-2.1, and that you have sufficient permissions to use that directory. If that is not your python.home directory, change it to the proper value. To compile the Embedding class in Listing 9.1, choose a working directory, and create the org/python/demo directories within it. Then place the Embedding.java file in the {working directory}/org/python/demo directory so the directory matches the package specified. Then from within your working directory, run the following command (replace / with \ for DOS):

javac -classpath /path/to/jython.jar org/python/demo/Embedding.java 


The command to execute the Embedding class should look similar to the following. Remember to use paths specific to your platform:

java -classpath /path/to/jython.jar:. org.python.demo.Embedding 


The following is a sample output from running the Embedding class:

sys.prefix= /usr/local/jython-2.1 sys.argv= [] sys.path= ['.', '/usr/local/jython-2.1/Lib'] sys.cachedir= /usr/local/jython-2.1/cachedir 


The Embedding class supposedly uses properties set on the command line. Using a command line with - D switches added confirms correct property settings. The following demonstrates doing so on Windows. Note that the command is one line, but it wraps to occupy the first two lines below. Also, note that you must set the cachedir to a directory where you have sufficient privileges.

java -Dpython.cachedir="newcache" -Dpython.path="d:\devel\jython" -classpath "c:\jython-2.1\jython.jar";. org.python.demo.Embedding *sys-package-mgr*: processing new jar, 'C:\jython-2.1\jython.jar' *sys-package-mgr*: processing new jar, 'C:\jdk1.3.1\jre\lib\rt.jar' *sys-package-mgr*: processing new jar, 'C:\jdk1.3.1\jre\lib\i18n.jar' *sys-package-mgr*: processing new jar, 'C:\jdk1.3.1\jre\lib\sunrsasign.jar' sys.prefix= c:\jython-2.1 sys.argv= [] sys.path= ['.', 'c:\\jython-2.1\\Lib', 'd:\\devel\\jython'] sys.cachedir= c:\jython-2.1\newcache 


You should see the caching messages if you do not already have a cache directory in the specified location. Searching for Java packages and caching the results obviously occurs when embedding Jython, just as it does when running the Jython program itself. In case timing matters in your apps, this caching occurs at initialization rather than when the PythonInterpreter is instantiated. Note that if you do not supply the system properties to the initialize method (the first parameter in Listing 9.1), the caching noted in the example above would not occur because the interpreter would not have the information required to find the jars.

Instantiating the Interpreter

Listing 9.1 already used the PythonInterpreter's empty constructor, but there are additional constructors that allow setting the namespace and system state of the interpreter. The PythonInterpreter class's three constructor signatures are as follows:

public PythonInterpreter() public PythonInterpreter(PyObject dict) public PythonInterpreter(PyObject dict, PySystemState systemState) 


The first constructor is the intuitive, parameterless version. The second constructor accepts a PyObject as an argument. This PyObject becomes the interpreter's namespace; therefore, it is most frequently an org.python.core. PyStringMap object. A PyDictionary or another dictionary-like PyObject that implements some custom functionality would work as well, but unless there's reason to do otherwise, use a PyStringMap. Besides the potential for customizing the behavior of the namespace object, supplying the interpreter's namespace allows you to set objects in the interpreter's namespace before instantiating it, or store a namespace between script invocations. Setting objects in a PyStringMap, and then passing it to the PythonInterpreter's constructor would look like this:

// the required imports import org.python.core.*; import org.python.util.PythonInterpreter; // Assume the interpreter is already intitialized PyStringMap dict = new PyStringMap(); dict.__setitem__("Name", new PyString("Strategy test")); dict.__setitem__("Context", Py.java2py(new SomeContextClassOrBean())); PythonInterpreter interp = new PythonInterpreter(dict); 


Notice that using Jython objects within Java makes use of Jython's special methods described in , "Advanced Classes." In the preceding example, the PyStringMap's __setitem__ is used to set items in the namespace object. Java obviously does not automatically employ Jython's dynamic special methods (those with two leading and trailing underscores), so you must explicitly employ these special methods where appropriate when using Jython objects within Java. The third constructor for the PythonInterpreter class allows you to set the namespace as well as the PySystemState object when you instantiate the interpreter. Later sections explain more about the PySystemState object, but for now it is enough to know that it is Jython's sys module in its Java persona. Using the PySystemState object to set the sys.path used by an interpreter would require the following lines:

PyStringMap dict = new PyStringMap(); dict.__setitem__("A", new PyInteger(1)); PySystemState sys = new PySystemState(); sys.path.append(new PyString("c:\\jython-2.1\\Lib")); sys.path.append(new PyString("c:\\windows\\desktop")); sys.path.append(new PyString("d:\\cvs")); // instantiate with namespace and system state objects interp = new PythonInterpreter(dict, sys); 


If you embed multiple interpreters, they share the system state. This has implications for using a PySystemState with a constructor because even though the call looks isolated to the one interpreter instance being created, the system state of all embedded interpreters is changed when the PySystemState object is altered. The local namespace, however, is unique to each interpreter instance.

Setting Output and Error Streams

The PythonInterpreter instance has methods for setting the output stream and the error stream. These methods are setOut and setErr. Each of these methods accepts a single argument, but that argument may be a java.io.OutputStream, java.io.Writer, or even any org.python.core.PyObject that is a file-like object. Listing 9.2 makes use of the setErr method to redirect error messages to a file.

Listing 9.2 Setting the Error Stream to a FileWriter
// file: ErrorRedir.java package org.python.demo; import org.python.demo.Embedding; import java.io.*; public class ErrorRedir extends Embedding {
 public static void main(String[] args)) {
 ErrorRedir erd = new ErrorRedir(); erd.initInterpreter(args); erd.test(); } public void test() {
 // Redirect errors to a file try {
 interp.setErr(new FileWriter(new File("errors"))); } catch (IOException e) {
 e.printStackTrace(); } interp.exec("assert 0, 'This should end up in a file.'"); } } 


After compiling Listing 9.2 with javac -classpath c:\path\to\jython.jar;.org\python\demo\ErrorRedir.java and executing it with java -cp c:\path\to\jython.jar;. org.python.demo.ErrorRedir, you should see only the following message in the console:

Exception in thread "main" 


The remaining traceback information appears in the file errors within your current working directory. Changing the error stream and the output stream to a network stream or other resource is also a likely scenario.

PySystemState

In Jython, the sys module contains information about the system state. Within Jython, you can view system state information by importing the sys module and printing variables within it. You can also change objects such as appending to sys.path or assigning new objects to the standard input, output and error objects. When you embed Jython, you can obviously use the sys object within the interpreter instance, but you can also use the sys module outside the interpreter. To use the sys module directory from Java, you must use its Java persona: PySystemState. Note that using methods from the PythonInterpreter class is the recommended API for embedding an interpreter. If an operation can be done with a PythonInterpreter method, that is how it should be done. However, the PySystemState object requires attention because it appears so frequently in Jython's code base, it contains methods that are very important to Jython's package manager (especially when embedding), and it is already familiar to users from using the sys module within Jython, making it an easy tool to use in Java as well. What import sys is in Jython becomes the following in Java:

import org.python.core.*; PySystemState sys = Py.getSystemState(); 


Because the PySystemState class and the Py class both appear in the org.python.core package, this package is first imported. After retrieving the PySystemState object with Py.getSystemState(), you can change the state inside the interpreter from Java code outside the interpreter. Listing 9.3 demonstrates how to append values to the sys.path variable by using the PySystemState object. Changes to the sys.path normally occur by setting the python.path property at initialization, or within Jython code such as the following:

import sys sys.path.append("c:\\windows\\desktop") 


This same operation can be done with the PySystemState object as demonstrated in Listing 9.3.

Listing 9.3 Using the sys Module from Java
// file: SysState.java package org.python.demo; import org.python.demo.Embedding; import org.python.core.*; public class SysState extends Embedding {
 public static void main(String[] args)) {
 SysState s = new SysState(); s.initInterpreter(args); s.test(); } public void test() {
 System.out.println("sys.path before changes to to sys:"); interp.exec("import sys\n" + "print sys.path\n" + "print"); // Get the system state and append to its path PySystemState sys = Py.getSystemState(); sys.path.append(new PyString("c:\\windows\\desktop")); System.out.println("sys.path after changes to sys:"); interp.exec("print sys.path"); } } 


Compile the SysState class with the following command:

javac -classpath c:\path\to\jython.jar;. org.python.demo.SysState.java 


The output from running the SysState class follows. Note that the command to execute the class is only one command (no returns) despite the fact that it wraps onto the second line in the example:

dos>\jdk1.3.1\bin\java -classpath "c:\jython-2.1\jy thon.jar";. org.python.demo.SysState The sys.path before changes to PySystemState: ['.', 'c:\\jython-2.1\\Lib', 'd:\\python20\\lib'] The sys.path after changes to PySystemState: ['.', 'c:\\jython-2.1\\Lib', 'd:\\python20\\lib', 'c:\\windows\\desktop'] 


The second time the example prints sys.path , the additional sys.path entry added through the PySystemState object appears. If you had embedded multiple interpreters, this change to the system state would have affected each of the interpreters. Jython's PySystemState class (sys module) also contains three methods related to class loading: add_package, add_classdir, and add_extdir. These methods become important when embedding Jython because many Java apps will have their own classloaders. When an app's classloader differs from Jython's, Jython doesn't always properly recognize or find certain Java package. This creates situations where Jython needs help in identifying which Java classes it may import, and this is the reason for the three methods mentioned. The add_package method places a specified Java package in Jython's package manager. If company A has an app with its own classloader, and embeds a Jython interpreter within it, the embedded interpreter might not properly identify Java packages such as com.A.python (assuming it exists). The interpreter would be unable to load classes within this package unless company A uses the add_package method to ensure proper package recognition, and thus loading. The following snippet demonstrates:

import org.python.core.*; PySystemState sys = Py.getSystemState(); sys.add_package("com.A.python"); 


The add_package method does not import or load the package. The app's classloader does that work. The add_package method merely adds the package to the list of Java packages from which Jython may import classes. The add_classdir and add_extdir methods are similar in that both add locations to the list where Jython searches for Java packages. The add_classdir method makes packages within a specified directory available. If you have a hierarchy of packages and classes beginning in the *nix directory /usr/java/devel, adding this tree to Jython's package manager is as follows:

import org.python.core.*; PySystemState sys = Py.getSystemState(); sys.add_classdir("/usr/java/devel/"); 


The add_extdir method adds the contents of archives within a directory to the list of locations Jython searches for Java packages. If you store an app's required jar and zip files in the *nix directory /usr/java/lib, adding the contents of all these archives to Jython's package manager is as follows:

import org.python.core.*; PySystemState sys = Py.getSystemState(); sys.add_extdir("/usr/java/lib/"); 


A good example of initializing the interpreter and employing the three add_* methods is the org.python.util.PyServlet class that comes with Jython. Listing 9.4 is an trimmed-down version of just the init() method in PyServlet, and within this method is where the interpreter is initialized and all the required Java packages are added to the package manager.

Listing 9.4 Jython PyServlet Class
public void init() {
 Properties props = new Properties(); if (props.getProperty("python.home") == null && System.getProperty("python.home") == null) {
 props.put("python.home", rootPath + "WEB-INF" + File.separator + "lib"); } PythonInterpreter.initialize(System.getProperties(), props, new String[0]); PySystemState sys = Py.getSystemState(); sys.add_package("javax.servlet"); sys.add_package("javax.servlet.http"); sys.add_package("javax.servlet.jsp"); sys.add_package("javax.servlet.jsp.tagext"); sys.add_classdir(rootPath + "WEB-INF" + File.separator + "classes"); sys.add_extdir(rootPath + "WEB-INF" + File.separator + "lib"); } 


Remember that add_package, add_classdir, and add_extdir do not load anything. The app's classloader does the actual work and the add_* methods merely make additional Java packages available to import. These methods are available in Jython's sys module. This description of the add_* methods appears here because they are most frequently used when embedding, but there is no intent to imply they are restricted only to embedding Jython. You may call these methods from within an embedded Jython interpreter with sys.add_* (after importing sys), and you may have need to use such methods in Jython even when not embedding. Below is a small interactive interpreter session where the sys.add_extdir method is used to make all the packages within the jar files in a Tomcat web server's lib directory importable from Jython (Tomcat is a web server available from Apache's Jakarta project at http://jakarta.apache.org. More on Tomcat appears in , "Server-Side Web Programming"):

>>> import sys >>> sys.add_extdir("c:\\web\\tomcat\\lib") *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\ant.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\jaxp.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\servlet.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\parser.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\webserver.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\jasper.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\zxJDBC.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\tools.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\ecs.jar' *sys-package-mgr*: processing new jar, 'C:\web\tomcat\lib\jython.jar' >>> from javax.servlet import http >>> dir() ['__doc__', '__name__', 'http', 'sys'] 


We see that the http package was made available, but add_extdir makes only packages available, not classes. If the interactive session is continued with an attempt to import a class from ext_dirs, we see the following:

>>> from javax.servlet.http import HttpServlet Traceback (innermost last): File "<console>", line 1, in ? ImportError: cannot import name HttpServlet 


Using the Interpreter

Running code within the interpreter requires the use of one of the interpreter's exec, execfile, or eval methods.

exec

The interpreter's exec method allows you to execute a string of Python code or a pre-compiled code object. When executing a string of Python code, you must use complete, syntactically correct statements in each call to exec. In other words, you cannot exec the first line of a function and complete the function definition in subsequent calls to exec. There are no static restrictions on the length of the string executed. The exec string can contain statements, assignments, functions, and anything normally found in a Jython script file. The exec method does not return any objects: The entire product of executing strings of Jython code remains inside the interpreter bubble. Previous listings in this chapter demonstrate the exec method, but they only used simple statements. Compound statements are more difficult because of their need for special formatting. Formatting strings used in the exec method is part of the embedding craft, but the extent of the formatting is escaping double-quotes or replacing them with single-quotes, adding newlines where required, and using proper indention. In other words, if you print the string you intend to send to the exec method, it should look like a syntactically correct Jython script. Listing 9.5 shows a simple function definition in Jython, and then shows the same thing re-written to work in an embedded interpreter.

Listing 9.5 Formatting Jython for the Interpreter's exec Method
# in Jython def movingAverage(datalist, sampleLen): """movingAverage(list, sampleLen) -> PyList""" add = lambda x, y: x + y return [reduce(add, datalist[x:x+10])/sampleLen for x in range(len(datalist) - sampleLen)] import random L = [random.randint(1,100) for x in range(100)] print movingAverage(L, 10) // in Java interp.exec("def movingAverage(datalist, sampleLen):\n" + " '''movingAverage(list, sampleLength) -> PyList'''\n" + " add = lambda x, y: x + y\n" ++ " return [reduce(add, datalist[x:x+10])/sampleLen " + " for x in range((len(datalist)-sampleLen)]\n" + "import random\n" + "L = [random.randint(1,100) for x in range(100)]\n" + "print movingAverage(L, 10)"); 


The Java version in the preceding example concatenates strings to create a string equivalent to the Jython version. The stacked quotes, or placement of each line's starting quote directly below that on the previous line, eases reproduction of proper Jython indention. The quoting required by the Jython code uses single quotes to avoid confusion with the Java quotes. If double quotes are required within the string, make sure to escape them. The newline (\n) character must also be added where required in the exec string to comply with Jython's syntax. An embedded interpreter often gets code from an external resource such as a network stream, portions of a file or archive resource. Some apps may dynamically generate code used by the interpreter. However, if the code is the entire contents of a file, the interpreter's execfile method fits the situation better than exec.

execfile

The PythonInterpreter's execfile method allows you to execute a file or an InputStream. There are three versions of the execfile method. The first accepts a single string argument that is the name of the file to execute. The second version accepts a single InputStream object. Because there is no name associated with an InputStream, error messages will show <iostream> in the file name location of error messages. The third version of the execfile method accepts an InputStream object and a String object to use in the file name field of error messages. The Java method signatures of these three methods are as follows:

public void execfile(String s) public void execfile(java.io.InputStream s) public void execfile(java.io.InputStream s, String name) 


Listing 9.6 contains a class that implements all three execfile methods. Listing 9.6 requires a command-line argument to specify the file to execute. This argument is not an absolute path to that file, but is instead just the name of a file that actually resides in the user's home directory. The user's home directory is pre-pended to the file name to create the fully qualified path. If my home directory, /home/rbill, contains the file exectest.py, the command-line argument should be just exectest.py. If you are unsure of your home directory, run the following with Jython:

>>> import java >>> print java.lang.System.getProperty("user.home") 


Listing 9.6 Demonstration of the execfile methods
// file: ExecFileTest.java package org.python.demo; import org.python.demo.Embedding; import java.io.*; import org.python.core.*; public class ExecFileTest extends Embedding {
 public static void main(String[] args)) {
 ExecFileTest eft = new ExecFileTest(); eft.initInterpreter(args); eft.test(); } public void test() {
 PySystemState sys = Py.getSystemState(); if (sys.argv.__len__() == 0) {
 System.out.println("Missing filename.\n " + "Usage: ExecFileTest filename"); return; } String home = System.getProperty("user.home"); String filename = home + File.separator + sys.argv.__getitem__(0); // Using a file name with execfile interp.execfile(filename); // Using an InputStream with execfile try {
 FileInputStream s = new FileInputStream(filename); interp.execfile(s); } catch (FileNotFoundException e) {
 e.printStackTrace(); } // Using an InputStream and a name with execfile try {
 FileInputStream s = new FileInputStream(filename); interp.execfile(s, sys.argv.__getitem__(0).toString()); } catch (FileNotFoundException e) {
 e.printStackTrace(); } } } 


You can compile the ExecFileTest.java file with the following command:

javac -classpath c:\path\to\jython.jar;. org\python\demo\ExecFileTest.java 


Create a Python file called test.py within your home directory which contains the line print 'It worked.'. You can execute this file three times in a row—one for each of the execfile methods, with the following command:

java -cp c:\path\to\jython.jar;. org.python.demo.ExecFileTest test.py It worked. It worked. It worked. 


eval

The eval method differs from the exec methods in three ways. The eval method always returns the results of the expression it evaluates as an org.python.core. PyObject—Jython's equivalent to Java's java.lang.Object class. In contrast, the return type of exec and execfile is always void. The interpreter's eval method currently only accepts strings of code. In contrast, the exec method accepts either a String or compiled code object. Finally, the interpreter's eval method only evaluates expressions while the exec methods execute any arbitrary Jython code. Introducing the interpreter's eval method is also a good opportunity to introduce the use of Jython's built-in functions from within Java. The interpreter's eval method is a wrapper around Jython's built-in eval method. The interpreter's eval accepts a string of code, and in turn calls the built-in eval method with two small modifications. The first modification is that it adds the interpreter's local namespace as the second argument to the built-in eval method. The second modification is that it changes the code string from a java.lang.String to an org.python.core.PyString object. The following code shows an eval operation using both the interpreter's eval method and the built-in eval method:

// The interpreter's eval shortcut interp.eval("1 or 0") // The interpreter's eval() // The built-in version __builtin__.eval(new PyString("1 or 0"), interp.getLocals()) 


You can see the Jython's built-in functions are available through the class org.python.core.__builtin__. It is not just the eval method available this way; most of Jython's built-in functions are available through __builtin__. These built-in functions are usable directly from Java. Using Jython's built-in functions in Java often comes in handy when working with PyObjects. Because the eval method always returns a PyObject, you can experiment using built-in methods on the objects that eval returns. The built-in eval function, and thus the interpreter's eval method, evaluates an expression. An eval expression has the same restrictions as lambda expressions: It cannot contain assignments or statements. Instead, the code string must only use functions, methods, data objects, literals, and those operators that do not perform a name-binding operation. Listing 9.7 creates an interactive loop for evaluating expressions with an embedded interpreter. The listing subclasses the Embedding class discussed earlier in this chapter, so it inherits the interpreter initialization and instantiation from the Embedding class. The highlights of Listing 9.7 are that it confirms how the eval method returns a PyObject, and it shows how to use the built-in function type() on that object. The command loop in the EvalTest class also gives an opportunity to examine expressions.

Listing 9.7 An eval Loop
// file: EvalTest.java package org.python.demo; import org.python.demo.Embedding; import java.io.*; import org.python.core.*; public class EvalTest extends Embedding {
 private static BufferedReader terminal; public static void main(String[] args)) {
 EvalTest et = new EvalTest(); et.initInterpreter(args); et.test(); } public void test() {
 System.out.println("Enter strings to evaluate at the prompt"); interact("eval> "); } public void interact(String prompt) {
 terminal = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Enter \"exit\" to quit."); String codeString = ""; while (true) {
 System.out.print(prompt); try {
 codeString = terminal.readLine(); if (codeString.compareTo("exit")==0) System.exit(0); processInput(codeString); } catch (IOException e) {
 e.printStackTrace(); } } } public void processInput(String input) {
 PyObject o = interp.eval(input); System.out.println("Results is of type " + __builtin__.type(o)); System.out.println(o); } } 


Compile with the EvalTest class with the following:

javac -classpath c:\path\to\jython.jar;. org\python\demo\EvalTest.java 


Execute the EvalTest class with the following:

java -cp c:\path\to\jython.jar;. org.python.demo.EvalTest 


Following is sample output from running the EvalTest class:

Enter strings to evaluate at the prompt Enter "exit" to quit. eval> 0 or 10 Results is of type <jclass org.python.core.PyInteger at 6851381> 10 eval> 1 and "A string" Results is of type <jclass org.python.core.PyString at 7739053> A string eval> [x for x in range(2,37) if 37%x == 0] === [] Results is of type <jclass org.python.core.PyInteger at 6851381> 1 eval> a = 10 # Assignments are not allowed Exception in thread "main" Traceback ((innermost last): (no code object) at line 0 File "<string>", line 1 a = 10 # Assignments are not allowed ^ SyntaxError: invalid syntax 


Compiling Code Object for Later Use

Calling the exec and eval methods in the Jython interpreter with strings of code causes the interpreter to first compile, and then to execute that code.The compiling step takes time; therefore, if an app has large strings of Jython code, or code that is used multiple times, it may benefit from compiling this code outside of time-critical sections of the app. Maybe you have a statistics app that cycles through many different data sets but applies the same Jython code to each. Rather than calling exec or eval with a string of Jython code each iterations, it is better to compile that code before the loop, maybe even compile it at startup and squirrel away the compiled code objects for later use. Doing this requires Jython's built-in compile function. Using Jython's built-in compile function from within Java to create a compiled code object involves three steps: import required classes, create a java.lang.String, and compile the String into a code () object. The import must include the __builtin__ and PyCode classes found in the org.python.core package. Accessing Jython's built-in functions occurs through the Java class org.python.core.__builtin__. The compile function requires a java.lang.String object as an argument, and the compile function returns an org.python.core.PyCode object. You will most often see import org.python.core.* used to cover the import requirements. The compile function returns a PyCode object, and this is the compiled object desired. You may execute or evaluate (depending on the third argument given to the compile function) this object at future times without incurring the compile cost. Here are the lines that create a PyCode object for use with the exec method:

// Import required classes (PyCode and __builtin__) import org.python.core.*; // Create a java.lang.String String s = "print 'Hello World'"; // compile with "<>" for the file-name, and "exec" for type of object PyCode code = __builtin__.compile(s, "<>", "exec"); // exec with interpreter. Note: no return object interp.exec(code); 


The built-in compile function requires three arguments: the PyString of code, the file name (used in error messages), and the mode. The mode may be exec, eval, or single if it is just a single statement. Because the compile statement in the example above creates a code object with the exec mode, you can pass that object to the embedded interpreter's exec method. The embedded interpreter's exec method works with code objects as well as strings. The interpreter's eval method, however, works only with strings. This is because it is easy enough to use the built-in eval to execute a compiled code object. To use the built-in eval with a compiled code object, call the __builtin__.eval method, and include the interpreter's namespace as the second argument. The following demonstrates this:

import org.python.core.*; String s = "1 and 0 or 10"; PyCode code = __builtin__.compile(s, "<>", "eval"); // Fetch the interpreter's local namespace PyCode locals = interp.getLocals(); // use __builtin__.eval with interp's locals PyObject o = __builtin__.eval(code, locals); 


The preceding lines are often shortened to the following:

import org.python.core.*; PyCode code = __builtin__.compile("1 and 0 or 10", "<>", "eval"); PyObject o = __builtin__.eval(code, interp.getLocals()); 


Handling Exceptions in the Interpreter

What happens if something goes wrong in the interpreter? The answer is that the interpreter raises the exception org.python.core.PyException. If Jython's open function failed to open a file, the Java code would not get an IOError exception as Jython does, but would instead get a PyException. The following shows a try/except statement in Jython and what it looks like if you try to move the exception handling into Java code surrounding the embedded interpreter:

# in Jython try: f = open("SomeNonExistingFile") except IOError: e, i, tb = sys.exc_info() print e, "\n", tb.dumpStack() // in Java try {
 interp.exec("f = open('SomeNonExistingFile')"); } catch (PyException e) {
 e.printStackTrace(); } 


The PyException stack trace would include the information about the actual Jython exception, but it would not aid in discriminating the type of Jython exception in the catch clause. Even if you use a Java object within the interpreter that raises a Java exception, the Jython wrapping around it turns the exception into a PyException. Trapping a specific Jython exception in a Java catch clause is not possible; therefore, you must a use a try/except statements within the Jython interpreter object, avoid Jython exceptions by negotiating errors in surrounding Java code, or you must discern the actual exception type within Java's catch block by using Py.matchException. Suppose you have a command loop and need to catch only an IOException within it. Just catching the PyException within Java doesn't allow that speci-ficity, so you could instead use a try/except within the interpreter like so:

interp.exec("try:\n" + " _file = open(" + filename + ")\n" + "except:\n" + " print 'File not found. Try again.'); 


Alternately, you could negotiate error trapping within the Java code and later use objects, or pass objects into the interpreter, that have been verified. The following example assumes again that there is a command loop in which someone enters a file name, additionally, the FileNotFoundException or Jython's IOError needs to be avoided so that the command loop continues. This example first tests the file before opening it in the interpreter. This example assumes that a readable file is safe to open from within the interpreter:

File file = new File(filename); if (file.canRead()!=true) {
 System.out.println("File not found. Try again."); break; } // File was found and is readable- safe to open. interp.exec("file = open(" + filename + ")"); 


The org.python.core.Py class contains the function matchException and Jython's exception objects; and using the Py.matchException function to compare a PyException to specific Jython exceptions allows you to discern specific Jython exceptions within a catch clause's associated block of code. The Py.matchException function takes a PyException as the first argument, a PyObject that is a Jython exception (such as Py.IOError) as the second argument, and returns a Boolean value indicating if the PyException is of the specific Jython exception type. Suppose you wrap an interpreter's exec statement in a try/catch statement like so:

try {
 interp.exec("f = open('SomeNonExistingFile')"); } catch (PyException e) {
 //fill in later } 


Then, you can add tests for specific Jython exception like this:

try {
 interp.exec("f = open('SomeNonExistingFile')"); } catch (PyException e) {
 if (Py.matchException(e, Py.AttributeError)) {
 //handle Jython's AttributeError here } else if (Py.matchException(e, Py.IOError)) {
 //handle Jython's IOError here } } 


The set and get Methods

The PythonInterpreter's get and set methods respectively return or set objects in the interpreter's local namespace. It's worth mentioning, for the sake of foreshadowing the getLocals and setLocals methods, that this namespace is a PyStringMap object identified as locals within the interpreter. The bi-directional mobility that objects have between Jython and Java with the get and set methods reintroduces type conversions of data objects. If you set a java.lang.Integer object in the interpreter, does it remain a java.lang.Integer ? The short answer is no, but the reason for the answer lies in the following sections. The purpose of the following sections is to show how to set objects in the interpreter, how to get objects from the interpreter, and to clarify what happens to the type of such objects when they traverse the interpreter's boundary.

set

The PythonInterpreter class has two set methods that allow interpreter instances to bind an identifier (name) within the interpreter to an object originating outside the interpreter. The signatures for these two methods are as follows:

public void set(String name, Object value) public void set(String name, PyObject value) 


Both methods accept a java.lang.String object for the name (first parameter), but their second parameter differs. The second parameter is the actual object bound to the specified name. If this object is a Java Object (the first of the methods listed previously), the interpreter converts that object to an appropriate Jython type. If this object is a PyObject, then the interpreter places it directly in the namespace dictionary without modification. The following shows how to set some simple objects in the embedded interpreter with the set method:

interp.set("S", "String literals becomes a PyStrings in the interp"); interp.set("V", java.util.Vector()); //V becomes a PyInstance type 


Strings and Java instances are easy, but what about the primitive types? A Java int is not a java.lang.Object. The following two lines are both errors:

interp.set("I", 1); interp.set("C", 'c'); 


These types require the use of their java.lang counterparts. Revising the preceding two lines to use the java.lang classes looks like this:

interp.set("I", new Integer(1)); interp.set("C", new Character('c')); 


Part of the logic of the set method is that it converts Java objects to appropriate PyObjects. The section on "Java Types" in , "Operators, Types, and Built-In Functions," describes how Jython converts certain types of objects. The example of setting a variable to a string earlier in this section makes note of this type conversion. A string object becomes a PyString when set in the interpreter. A java.lang.Integer becomes a PyInteger, a java.lang.Long becomes a PyLong , and so on, according to Jython's rules for type conversion.

get

The PythonInterpreter's get method retrieves an object from the interpreter. There are two method signatures for the get method, and they are as follows:

public PyObject get(String name) public Object get(String name, Class javaclass) 


The name parameter designates a key in the interpreter's namespace mapping object (locals), and the return value of the get methods is the value associated with that specified key. The first get method requires only the name parameter, and returns a PyObject. The second get method, however, has an additional parameter to specify the class of the object returned. The automatic type conversion of Java objects in the interpreter's set method is not reciprocal in the get method. To retrieve a Java object instead of a PyObject with the get method, you must specify the Java class you wish returned and cast it to the desired object. To retrieve a java.lang.String object with the get method, you would use this:

interp.set("myStringObject", "A string"); String s = (String)interp.get("myStringObject", String.class); 


The return value is an object, so it must be cast if you are assigning the results to a less generic type. Also, remember that the second parameter must be a class; a string describing the class is insufficient. You can use Classname.class as was done previously, or Objectname.getClass():

interp.set("myStringObject", "A string"); String s = (String)interp.get("myStringObject", String.class); 


Listing 9.8 demonstrates both the set and get methods. What the listing does is create some Java objects, uses set to place them in the interpreter, and then uses get to retrieve the objects back in Java. The object's class is printed at each step—in Java, in Jython, and back in Java, so that it produces a table detailing what automatic type conversions the interpreter applied. Without any command-line arguments, the TypeTest class uses the generic, single-argument get method. When the user specifies the command-line option symmetrical, the TypeTest class applies the two-parameter get method to convert each object back to its original class.

Listing 9.8 Type Converter
// file: TypeTest.java package org.python.demo; import org.python.demo.Embedding; import org.python.core.*; import java.util.Vector; public class TypeTest extends Embedding {
 private boolean simple = true; public TypeTest( ) { ; } public static void main(String[] args)) {
 TypeTest tt = new TypeTest(); tt.initInterpreter(args); tt.test(); } public void test() {
 String codeString; Object[] jTypes = {"A string", new Short("1"), new Integer(3), new Long(10), new Float(3.14), new Double(299792.458), new Boolean(true), new int[] {{1,2,3,4,5}, new Vector(), new PyInteger(1), new Character('c')}; interp.exec("print 'In Java'.ljust(20), " + "'In Jython'.ljust(20), " + "'Back in Java'"); interp.exec("print '------'.ljust(20), " + "'---------'.ljust(20), " + "'------------'"); // get first command-line argument- argv[0] PyObject argv = Py.getSystemState().argv; if (argv.__len__() > 0) {
 String option = argv.__getitem__(0).toString(); if (option.compareTo("symmetrical")==0) simple = false; } for ( int i=0; i < jTypes.length; i++ ) {
 showConversion(jTypes[i]); } } public void showConversion(Object o) {
 interp.set("testObject", o); interp.set("o", o.getClass().toString()); String newClass = null; if (simple) {
 newClass = interp.get("testObject").getClass().toString(); } else {
 newClass = interp.get("testObject", o.getClass()).getClass().toString(); } interp.set("n", newClass); // n for newJavaClass interp.exec("pyClass = str(testObject.__class__) \n" + "print o[o.rfind('.') + 1:].ljust(20), " + "pyClass[pyClass.rfind('.') + 1:].ljust(20), " + "n[n.rfind('.') + 1:]"); } } 


Compile the TypeTest class in Listing 9.8 with the following command:

javac -classpath c:\path\to\jython.jar;. org\python\demo\TypeTest.java 


Running the TypeTest class without a command-line argument produces the following:

dos>java -classpath -c:\jython-2.1\jython.jar";. or g.python.demo.TypeTest In Java In Jython Back in Java ------- --------- -----------
String PyString PyString Short PyInteger PyInteger Integer PyInteger PyInteger Long PyLong PyLong Float PyFloat PyFloat Double PyFloat PyFloat Boolean PyInteger PyInteger class [I PyArray PyArray Vector Vector PyJavaInstance PyInteger PyInteger PyInteger Character PyString PyString 


You can see from the results that the interpreter's automatic conversion is one way. Adding the symmetrical option to the command will show what happens when you try to convert each type back to its original Java class.

C:\WINDOWS\Desktop\ch9examples>java -classpath "c:\jython-2.1\jython.jar";. org.python.demo.TypeTest symmetrical In Java In Jython Back in Java ------- ---------- -----------
String PyString String Short PyInteger Short Integer PyInteger Integer Long PyLong Long Float PyFloat Float Double PyFloat Double Boolean PyInteger Boolean class [I PyArray class [I Vector Vector Vector PyInteger PyInteger PyInteger Character PyString Character 


There is also another means of converting Py* objects into Java objects, which are used more frequently. This is the __tojava__ method, which appears in the following section.

__tojava__

Instances of Jython classes (PyObjects) have a special method called __tojava__. The __tojava__ method requires a Java class as a parameter, and returns the instance coerced into the requested Java class. If the coercion is not possible, the method returns a Py.NoConversion object. The following lines show converting PyString to a java.lang.String object:

interp.set("A = 'A test string'"); PyObject po = interp.get("A"); String MyString = (String)po.__tojava__(String.class); 


The preceding example assumes that __tojava__ is successful. The __tojava__ method would instead return the Py.NoConversion object if it failed to convert the object into an instance of the String class. The Py.NoConversion value would raise a ClassCastException because of the (String) cast. Handling the exception means either wrapping the cast in a try/except, or testing for the Py.NoConversion object before casting. Listing 9.9 chooses to test for the Py.NoConversion object. The Convert class in this listing is another subclass of the Embedding class. The test() method in this case walks through the getting, converting, and casting of a PyInteger object into a java.lang.Character. The conversion fails in this case; testing for Py.NoConversion before casting as a Character prevents ClassCastException.

Listing 9.9 Testing for Py.NoConversion with __tojava__
// file: Convert.java package org.python.demo; import org.python.demo.Embedding; import org.python.core.*; public class Convert extends Embedding {
 public Convert() { ; } public static void main(String[] args)) {
 Convert c = new Convert(); c.initInterpreter(args); c.test(); } public void test() {
 // Set an identifier in the interpreter interp.exec("test = 'A'"); // Get object out of interpreter as PyObject PyObject retrievedObject = interp.get("test"); // Convert PyObject to instance of desired Class Object myObject = retrievedObject.__tojava__(Character.class); // See if conversion failed- meaning Py.NoConversion returned if (myObject == Py.NoConversion) {
 System.out.println("Unable to convert."); } else {
 Character myChar = (Character)myObject; System.out.println("The Character is: " + myChar); } } } 


Converting user-defined, Jython classes into Java objects with the __tojava__ method allows classes written in Jython to be used where certain Java classes are required. Consider an app that uses the Java class A, and that you require a great number of unique versions of this class. You could write these classes in Jython as subclasses of A, and convert them to objects of class A when required by certain method signatures. This may allow rapid development of these required classes. Listings 9.10, 9.11, and 9.12 form a trio of files that employ such a process. The Report class in Listing 9.10 is meant to simulate a report generation tool. Admittedly the pairing down of the report generation portion to avoid distracting from the __tojava__ focus leaves a lot to the imagination, but the pattern is an effective venue for Jython scripts. The Report class loads a Jython script specified on the command line. Listing 9.12 contains a Jython script that handles the details of a specific report. This allows a multitude of reports formats to exist without having to recompile the Report class. The trick to using scripts this way is to have them inherit from a Java class. Listing 9.11 is a simple abstract ReportSpec class that serves as an appropriate superclass for the Jython script in Listing 9.12.

Listing 9.10 Using __tojava__ for Report Generator
// file: Report.java package org.python.demo.reports; import org.python.demo.Embedding; import org.python.demo.reports.ReportSpec; import java.io.*; import org.python.core.*; public class Report extends Embedding {
 public static void main(String[] args)) {
 Report rpt = new Report(); rpt.initInterpreter(args); try {
 rpt.generateReport(); } catch (Exception e) {
 e.printStackTrace(); } } public void generateReport() throws FileNotFoundException, PyException {
 String fileName; ReportSpec rs = null; // Check #1- user supplied command-line arg PySystemState sys = Py.getSystemState(); if (sys.argv.__len__() == 0) {
 System.out.println("Missing filename.\n " + "Usage: 'Report' filename"); return; } else {
 fileName = sys.argv.__getitem__(0).toString(); } // Check #2- Command-line arg is in fact a *.py file if (new File(fileName).isFile() != true) throw new FileNotFoundException(fileName); if (fileName.endsWith(".py") != true) throw new PyException(
 new PyString(fileName + " is not a *.py file")); try {
 rs = getReportSpecInstance(fileName); } catch (InstantiationException e) {
 e.printStackTrace(); } rs.fillTitle(); rs.fillHeadings(); while (rs.fillRow()) {} } protected ReportSpec getReportSpecInstance(String fileName) throws InstantiationException {
 String className; // Exec the file interp.execfile(fileName); // Get the name of the file without path and extension. // This should be the name of the class within the file int start = fileName.lastIndexOf(File.separator); if (start < 0) start = 0; else start++; className = fileName.substring(start, fileName.length() - 3); PyObject reportSpecClass = interp.get(className); if (reportSpecClass == null) throw new InstantiationException(
 "No ReportSpec Class named " + className + "exists in " + fileName); PyObject m_args = (PyObject) new PyInteger(70); PyObject reportSpecInstance = reportSpecClass.__call__(m_args); ReportSpec rs = (ReportSpec)reportSpecInstance.__tojava__(ReportSpec.class); if (rs == Py.NoConversion) throw new InstantiationException(
 "Unable to create a ReportSpec instance from " + className); return rs; } } 


Listing 9.11 A Java Class Jython Report Scripts Subclass
// file: ReportSpec.java package org.python.demo.reports; public abstract class ReportSpec extends Object {
 public abstract String fillTitle(); public abstract void fillHeadings(); public abstract boolean fillRow(); } 


Listing 9.12 The Jython Report Generation Script
# file: ReportTest.py from org.python.demo.reports import ReportSpec class ReportTest(ReportSpec): def __init__(self, reportWidth): # "@sig public ReportTest(int reportWidth)" # plug in data (a database in a real implementation) self.width = reportWidth self.data = [ [1,2,3], [4,5,6], [7,8,9] ] self.pad = reportWidth/(len(self.data[0]) - 1)) def fillTitle(self): print "Test of Report Generator".center(self.width) def fillHeadings(self): """Prints column headings.""" # This would be database metadata in a real implementation for x in ["A", "B", "C"]: print x.ljust(self.pad - 1), print def fillRow(self): if not self.data: return 0 row = self.data.pop() for x in row: print str(x).ljust(self.pad - 1), print return 1 


The abstract Java class that the report scripts must inherit from is the abstract ReportSpec class from Listing 9.11. The Jython script merely needs to meet the protocol specifications of the required superclass, as the ReportTest.py class in Listing 9.12 does. Notice that the class is retrieved from the interpreter, not the instance. It is just as plausible to create the instance in the interpreter and get it; however, in a long-lived process, it may be a bit easier to minimize the interpreter's namespace pollution. Note that the creation of the instance uses the __call__() method on the class object. Revisit , "Advanced Classes," for the importance of the __call__ method for callable Jython objects. Compile the Report class in Listing 9.10 and the ReportSpec class from Listing 9.11 with the following commands:

javac -classpath \path\to\jython.jar;. org\python\demo\reports\Report.java javac -classpath \path\to\jython.jar;. org\python\demo\reports\ReportSpec.java 


Running the Report class with Listing 9.12 as the command-line argument produces the following:

>java -classpath "c:\jython-2.1\jython.jar";. org.python.demo.reports.Report ReportTest.py Test of Report Generator A B C 7 8 9 4 5 6 1 2 3 


The getLocals and setLocals Methods

Locals means the interpreter's local namespace. The set and get methods described above actually bind a key in the locals mapping object, or retrieve a key's associated object. The setLocals and getLocals methods instead allow you to set or retrieve the entire namespace mapping object. There are various reasons for doing so, but the obvious one is maintaining isolated namespaces for multiple scripts. If module A and module B execute sequentially in the interpreter, and both define the same module-global variable, then module B redefined that variable. This will likely have an unfortunate consequence if module A executes again. Fetching and restoring the locals object eliminates this risk of incidental side effect. The getLocals and setLocals methods respectively return or require a dictionary-like PyObject. This is usually an org.python.core.PyStringMap object. The method signatures for the getLocals and setLocals methods are as follows.

public PyObject getLocals() public void setLocals(PyObject d) 


Listing 9.13 is yet another subclass of the earlier Embedding example. The class in this listing sets a test value into a PyStringMap object which it sets in the interpreter. Within the interpreter, that test value is printed to confirm it's there, and then another test value is set in the interpreter. The getLocals method retrieves the locals and prints it to confirm the both identifiers exist in it.

Listing 9.13 Using setLocals and getLocals
// file: LocalsTest.java package org.python.demo; import org.python.demo.Embedding; import org.python.core.*; public class LocalsTest extends Embedding {
 public LocalsTest() { ; } public static void main(String[] args)) {
 LocalsTest L = new LocalsTest(); L.initInterpreter(args); L.test(); } public void test() {
 PyStringMap locals = new PyStringMap(); locals.__setitem__("Test1", new PyString("A test string")); interp.setLocals(locals); interp.exec("print Test1"); interp.exec("Test2 = 'Another teststring'"); PyObject dict = interp.getLocals(); System.out.println(dict); } } Compile with javac -classpath c:\path\to\jython.jar;. org\python\demo\LocalsTest.java 


The output from running Listing 9.13 is as follows:

dos>java -cp -c:\jython\jython.jar";. org.python.demo.LocalsTest A test string {'Test2': 'Another teststring', 'Test1': 'A test string'} 


imp and the Top-Level Script

Many Jython modules make use of the top-level script environment to conditionally run a main section of code. While this description may not ring a bell, the related statement should be extremely familiar:

if __name__ == '__main__': 


A script employs this to run specific code when the script is the main script. The __name__ identifier is automatic in Jython, but when embedding Jython, you must explicitly add this to the interpreter if you require a script to execute the code associated with the __name__ == '__main__' condition. Listing 9.14 is another subclass of the Embedding class, which executes a file specified on the command line. This example, however, adds the __main__ so that __name__ == '__main__' is true.

Listing 9.14 Setting __name__ == '__main__'
// file: TopLevelScript.java package org.python.demo; import org.python.demo.Embedding; import java.io.*; import org.python.core.*; public class TopLevelScript extends Embedding {
 public static void main(String[] args)) {
 TopLevelScript tls = new TopLevelScript(); tls.initInterpreter(args); tls.test(); } public void test() {
 PySystemState sys = Py.getSystemState(); if (sys.argv.__len__() == 0) {
 System.out.println("Missing filename.\n " + "Usage: TopLevelScript filename"); return; } String filename = sys.argv.__getitem__(0).toString(); // Set __name__ PyModule mod = imp.addModule("__main__"); interp.setLocals(mod.__dict__); // Using a file name with execfile interp.execfile(filename); } } Compile with javac -classpath \path\to\jython.jar org\python\demo\TopLevelScript.java 


You can use a minimal Jython module like the following to test the __name__ setting:

# file: mainname.py if __name__ == '__main__': print "It worked." 


The command and output from running this is as follows:

dos> java -classpath \path\to\jython.jar;. ort.python.demo.TopLevelScript mainname.py It worked. 


The same effect could be achieved by creating a PyStringMap with the __name__ key set to __main__:

// Set __name__ PyStringMap dict = new PyStringMap(); dict.__setitem__("__name__", new PyString("__main__")); interp.setLocals(dict); 


Listing 9.14 instead uses the imp.addModule method because Jython does it this way, and because it introduces the imp class. The imp class implements Jython's import facility. The addModule method used in Listing 9.14 loads a module, and then returns that PyModule object. It also places the module name in the list of imported modules: sys.modules. More details about the imp class appear in the later section "Extending Jython."

Embedding the InteractiveInterpreter

The InteractiveInterpreter is a subclass of PythonInterpreter. The InteractiveInterpreter provides the runcode, runsource, and interrupt methods for a higher level of interaction. The two behaviors that are important in this class are exception trapping and management of statement completeness. If an exception occurs in the runcode or runsource methods, it is trapped within the methods so that it is not fatal, thus allowing interaction to continue. The interpreter does print such exceptions, however, so that the user is aware of what happened. Additionally, the runsource method returns a Boolean value that indicates the completeness of the source string. This helps determine which prompt to print and when to reset the statement buffer. Table 9.1 shows the method signatures and use of each of the InteractiveInterpreter's methods.

Table 9.1. InteractiveInterpreter Methods

Method Signature

Summary

public InteractiveInterpreter()

public InteractiveInterpreter (PyObject locals)

These are the constructors for the InteractiveInterpreter class . The second constructor accepts a PyStringMap object just like the PythonInterpreter's one-argument constructor.

public Boolean

runsource(String source)

public Booleanstring.

runsource(String source, String filename)

The runsource method tries to compile and execute a code It also displays exception information, but does not propagate exceptions. The return values are as follows:

public Boolean

runsource(String source, String filename, String symbol)

Success -> false Exception occurred -> false Incomplete statement -> true The true when incomplete is what allows easy control of the loop gathering user input. The second and third parameters are the same as for the built-in compile method.

public void

runcode(PyObject code)

The runcode method executes a code object and displays any exception that occurred during the execution. Note that the exception is only displayed; it does not propagate.

public void

showexception(PyException exc)

Writes exception information to sys.stderr. This is mostly for internal uses.

public void write(String data)

This writes a string to sys.stderr. This is mostly for internal uses.

public void

interrupt(ThreadState ts)

throws InterruptedException

The interrupt method pauses the code to insert an exception in a thread-friendly way.

The following loop leverages the runsource return value to loop until the user has entered a syntactically complete statement:

while (interp.runsource(codeString)) {
 System.out.print(ps2); codeString += "\n" + terminal.readLine(); } 


Listing 9.15 demonstrates both the special handling of exceptions and the return value from the runsource method. Note that the initialize step is still required. The initialize method is the same method used when embedding a PythonInterpreter because that is the InteractiveInterpreter's superclass.

Listing 9.15 Embedding the InteractiveInterpreter
// file: InteractiveEmbedding.java package org.python.demo; import org.python.demo.Embedding; import org.python.util.InteractiveInterpreter; import java.util.Properties; import java.io.*; public class InteractiveEmbedding extends Embedding {
 protected InteractiveInterpreter interp; public static void main(String[] args)) {
 InteractiveEmbedding ie = new InteractiveEmbedding(); ie.initInterpreter(args); ie.test(); ie.interact(); } public void initInterpreter(String[] argv)) {
 // set Properties if (System.getProperty("python.home") == null) System.setProperty("python.home", "c:\\jython-2.1"); // no postProps, all properties but python.home put in registry file InteractiveInterpreter.initialize(System.getProperties(), null, argv); //instantiate- note that it is AFTER initialize interp = new InteractiveInterpreter(); } public void test() {
 interp.runsource("print "this is a syntax error"); interp.runsource("print 'This is not'"); } public void interact() {
 String ps1 = ">>>"; String ps2 = "..."; BufferedReader terminal = new BufferedReader(
 new InputStreamReader(System.in)); interp.write("Enter \"exit\" to quit."); String codeString = ""; interp.write("\n"); while (true) {
 interp.write(ps1); try {
 codeString = terminal.readLine(); } catch (IOException e) {
 e.printStackTrace(); } if (codeString.compareTo("exit")==0) System.exit(0); while (interp.runsource(codeString)) {
 interp.write(ps2); try {
 codeString += "\n" + terminal.readLine(); } catch (IOException e) {
 e.printStackTrace(); } } } } } 


Listing 9.15 initializes the interpreter, runs a quick test method, then begins an interaction loop. The test method intentionally includes a misquoted string to produce a SyntaxError. This demonstrates how the runsource method reports the error without terminating the interpreter. After the test method, the example continues with a command loop. The command loop prints a prompt, reads input, and then feeds that input to the interpreter's runsource method. The runsource method returns true if the statement is incomplete, and this return value is what allows a succinct inner loop that collects input for compound statements until the statement is complete. This inner loop for compound statements also uses the secondary prompt (...) to indicate the continuation of the statement. Compile Listing 9.15 with a command similar to the following:

javac -classpath \path\to\jython.jar;. org\python\demo\InteractiveEmbedding.java 


Ouput from a sample usage of the InteractiveEmbedding class follows. Remember that the SyntaxError and the This is not statement at the start of the execution are part of the test method:

dos>java -cp \path\to\jython;. org.python.demo.InteractiveEmbedding Traceback (innermost last): (no code object) at line 0 File "<input>", line 2 SyntaxError: Lexical error at line 2, column 0. Encountered: <EOF> after : "" This is not Enter "exit" to quit. >>>print "Hello World!" Hello World! >>>try: ... assert 0, "Assertion error for testing compound statements" ...except AssertionError: ... import sys ... e = sys.exc_info() ... print "%s\n%s\n%s" %% e ... exceptions.AssertionError Assertion error for testing compound statements <traceback object at 2147462> >>>exit 


Embedding an InteractiveConsole

The InteractiveConsole adds one more layer of abstraction to embedding, but this one is specific to the console interaction so common in Jython, such as used in Listing 9.13. Creating console interaction involves using the InteractiveConsole's three methods: interact, raw_input, and push. Table 9.2 summarizes these methods.

Table 9.2. InteractiveConsole Methods

Method

Summary

public InteractiveConsole()

public InteractiveConsole (PyObject locals)

public InteractiveConsole (PyObject locals, String filename)

The InteractiveConsole's three constructors allow for optionally setting the interpreter's local namespace and setting a file name for use in error messages.

public void interact()

public void interact(String banner)

The interact method mimics the Jython interpreter. The optional banner argument is a message printed before the first interaction only.

public boolean push(String line)

The push method pushes a single line that does not end with \n into the interpreter. This method returns true or false just as the InteractiveInterpreter's runsource method does—true again means that additional input is required.

public String

raw_input(PyObject prompt)

The InteractiveConsole's raw_input method is the same as the built-in raw_input method.

Listing 9.16 shows how easy it is to create an embedded console. The initialize step is required as always, but beyond that, the interact method does all the work.

Listing 9.16 Embedding the InteractiveConsole
// file: Console.java package org.python.demo; import org.python.util.InteractiveConsole; import java.util.Properties; import java.io.*; public class Console {
 protected InteractiveConsole interp; public Console() {
 // set Properties if (System.getProperty("python.home") == null) System.setProperty("python.home", "c:\\jython-2.1"); // no postProps, registry values used InteractiveConsole.initialize(System.getProperties(), null, new String[0] )); interp = new InteractiveConsole(); } public static void main(String[] args)) {
 Console con = new Console(); con.startConsole(); } public void startConsole() {
 interp.interact("Welcome to your first embedded console"); } } 


Listing 9.16 initializes the InteractiveConsole, and calls its interact method. The initialization is the same procedure as before, but the interaction loop is greatly simplified with the interact method. Executing Listing 9.16 produces Jython's interactive console, so an example is unnecessary. Note that the InteractiveConsole's push and raw_input methods are usable, but they were not necessary for this listing. These methods are designed for interaction, just like the InteractiveInterpreter's runsource method, and therefore are not ideal to performance critical situations. If your app need not interact with the interpreter, use the PythonInterpreter object and its exec and eval methods.

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 9.17 shows a sample Jython module written in Java. You can see that it is nothing more than a Java class. The parts of interest, however, are the ClassDictInit interface, the related classDictInit method, static modifiers on each method, the __doc__ strings, and the PyException. Although the mymod class is very simple, it demonstrates many aspects of creating Jython modules in Java.

Listing 9.17 A 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]); } } 


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 in Jython, it clarifies one advantage to writing Java modules: 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).

>>> 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 control over attribute names visible within Jython and their implementations. The mymod class in Listing 9.17 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 need not be included in the class and therefore would not appear in Jython anyway. The real usefulness comes when you have a complicated module implemented in multiple classes or the class includes multiple attributes you need to hide from Jython. 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.

__doc__ Strings

You can define a module's doc string by including a static PyString member named __doc__:

public static __doc__ = new PyString("Some documentation"); 


The static methods within a class become module functions in Jython. In Listing 9.17, the static fibTest method behaves like the fibTest function within a Jython module. To add a doc string to this function means adding a static PyString called __doc__fibTest. Listing 9.15 uses this to assign documentation to the fibTest method than can be retrieved from Jython as mymod.fibTest.__doc__.

Exceptions

To raise a Jython exception requires throwing the PyException class with two arguments: the actual Jython exception, and the exception message (what is referred to as the exception argument in Jython). Jython's built-in exceptions are located in the org.python.core.Py class. So, a ValueError is really a Py.ValueError.

raise ValueError, "Invalid Value" 


The preceding Jython statement is shown in Java form as follows.

throw PyException(Py.ValueError, "Invalid Value") 


Listing 9.17 uses the PyException to raise a Jython ValueError exception in the interpreter.

Parameters

Jython functions benefit from a rich parameter scheme that includes ordered parameter, keyword parameters, default values, and wild cards. It is possible to emulate this behavior in Java. The following method allows a Java method to handle positional and keyword arguments:

public PyObject MyFunction(PyObject[] args, String[] kw); 


The args array holds all argument values, whereas the kw array holds only the specified keys. Following is and example call of the previously mentioned MyFunction:

MyFunction(1, 2, D=4, C=9) 


The args and kw arrays would then look like the following:

args = [1, 2, 4, 9] kw = ["D", "C"] 


The class org.python.core.ArgParser eases parsing these parameters into more useful forms. The ArgParser class has four constructors:

public ArgParser(String funcname, PyObject[] args, String[] kws, String p0) public ArgParser(String funcname, PyObject[] args, String[] kws, String p0, String p1) public ArgParser(String funcname, PyObject[] args, String[] kws, String p0, String p1, String p2) public ArgParser(String funcname, PyObject[] args, String[] kws, String[] paramnames) 


Each constructor requires the function's name as the first parameter. The function using ArgsParser should use the parameters PyObject[] args and String[] kw. These two objects become the second and third arguments to the ArgParser constructor. The remaining parameters are the list of arguments expected by a method. These can be separate args if there are three or fewer; otherwise, they are a String[]. Listing 9.17 shows a Java method that implements Jython's argument style with the help of ArgParser. Some example Jython methods and their Java method plus ArgParser counterparts presented as follows to clarify.

Jython Method: def test(A, B, C=2) Java implementation: public static PyObject test(PyObject[] args, String[] kws)) {
 ArgParser ap = new ArgParser("test", args, kws, "A", "B", "C"); } Jython Method: def addContact(name, addr, ph=None) Java implementation: public static PyObject addContact(PyObject[] args, String[] kws)) {
 ArgParser ap = new ArgParser("addContact", args, kws, new String[] {"name", "addr", "ph"}); } 


The parameter values are retrieved from the ArgParser instance with one of its get* methods. The get* methods listed here have either one or two parameters. Those with two parameters retrieve parameters with default values. Note that the position of the argument (pos) starts with position 0, not 1.

public String getString(int pos) public String getString(int pos, String def) public int getInt(int pos) public int getInt(int pos, int def) public PyObject getPyObject(int pos) public PyObject getPyObject(int pos, PyObject def) public PyObject getList(int pos) 


For the Jython method:

def test(A, B, C=2) 


The Java implementation, which allows for the default value, is this:

public static PyObject test(PyObject[] args,, String[] kws)) {
 ArgParser ap = new ArgParser("test", args, kws, "A", "B", "C"); int A = ap.getInt(0); // or... // String A = ap.getString(0); // PyObject A = ap.getPyObject(0); // PyTuple A = (PyTuple)ap.getList(0); String B = ap.getString(1); // or... // int B = ap.getInt(1); // PyObject B = ap.getPyObject(1); // PyObject B = ap.getList(1); // here's the two argument version to allow for defaults int C = ap.getInt(2, 2);// first 2=position, second 2=default value } 


Importing Jython Modules in Java

Importing a Jython module in Java usually uses the __builtin__.__import__ function. The __import__ function has four signatures:

public static PyObject __import__(String name) public static PyObject __import__(String name, PyObject globals) public static PyObject __import__(String name, PyObject globals, PyObject locals) public static PyObject __import__(String name, PyObject globals, PyObject locals,PyObject fromlist) 


Importing the random module from within Java would be as follows:

PyObject module = __builtin__.__import__(random); 


Working with PyObjects

Calling Jython classes in Java, and writing Jython modules in Java requires extensive use of Jython's special methods. Special methods are those methods that begin and end with two underscores and are described in . Jython's flexible operation creates special means of calling PyObjects from within Java. Jython's dynamic operation means that the finding, getting, setting, and calling of attributes all happens through methods that provide opportunity to customize or extend each step. covered the special methods for getting, setting, and calling, but there are additional methods for finding. These methods are __findattr__ and __finditem__. The __findattr__ method returns object attributes, and the __finditem__ method returns sequence or dictionary values designated by a key. Usage of these methods create the following signatures:

public PyObject __finditem__(PyObject key) public PyObject __finditem__(int index) public PyObject __finditem__(String key) public PyObject __findattr__(String name) public PyObject __findattr__(PyObject name) 


The __finditem__ and __findattr__ methods that accept a String object as a parameter both require that the string object be interned. String literals are automatically interned, but otherwise, this string must be explicitly interned. If you import the random module and intend to use the randint method, you cannot just call its directory:

// This doesn't work PyObject random = __builtin__.__import__("random"); random.randint(new PyInteger(10), new PyInteger(100)); 


Instead, you should use the __findattr__ method combined with the call method:

PyObject random = __builtin__.__import__("random"); random.__findattr__("randint").__call__(new PyInteger(10), new PyInteger(20)); 


The shortcut method invoke exists to help in calling a method on a PyObject from Java. Specifically, the correct and generic way to call methods on all kinds of Jython mapping objects (such as PyDictionary and PyStringMap) is with the invoke method. Calling the invoke method on a PyObject's method is the equivalent of calling the following:

myPyObject.__getattr__(name).__call__(args, keywords) 


Following are the different method signatures of the invoke method:

public PyObject invoke(String name) public PyObject invoke(String name, PyObject arg1) public PyObject invoke(String name, PyObject arg1, PyObject arg2) public PyObject invoke(String name, PyObject[] args)) public PyObject invoke(String name, PyObject[] args,, String[] keywords)) 


The different signatures respectively allow for differing sets of arguments passed to the invoked method, but what does not differ is that the first argument is a string representing the name of the PyObject's method to call. This name parameter must be an interned string. Remember that string literals are automatically interned. The following example shows the creation of a PyDictionary and the retrieval of its keys from within Java using the invoke method. This example uses PyDictionary's empty constructor and subsequently adds values with the __setitem__ special method:

PyDictionary dct = new PyDictionary(); dct.__setitem__(new PyString("G"), new PyInteger(1)); dct.__setitem__(new PyString("D"), new PyInteger(2)); dct.__setitem__(new PyString("A"), new PyInteger(3)); dct.__setitem__(new PyString("E"), new PyInteger(4)); dct.__setitem__(new PyString("B"), new PyInteger(5)); PyObject keys = dct.invoke("keys"); PyObject keys = dict.invoke("keys"); 


The invoke method above uses the keys method of the PyDictionary object to retrieve its keys. Because the method name (Keys) is a string literal in the previous example, it is automatically interned. Looping through the keys returned in the previous example also requires special care. Because a PyObject's __len__ method may return wrong values, the only way to safely loop through Jython sequences in Java is to test for each index value until reaching a non-existing index. If we continue the dictionary keys example from above with a loop through those keys, the proper and generic way to loop through them is implemented as follows:

PyObject key; for (int i = 0; (key = keys.__finditem__(i)) != null; i++) {
 System.out.println("K: " + key + ", V: " + dict.__getitem__(key)); } 


Writing Jython Classes in Java

When a Java class emulates a Jython module, functions within that module are often implemented as static class members. Emulating a Jython class in Java can therefore be implemented as a static inner class; however, the ClassDictInit interface allows more flexibility in this regard. To emulate a Jython class with Java, it is best to subclass the most appropriate Py* class found in the org.python.core package and then implement all the special methods required for the desired type of class. All classes written in Java may subclass PyObject and implement the __findattr__, __setattr__, and __delattr__ methods. A Mapping object would subclass PyDictionary or PyStringMap and implement __finditem__, __setitem__, __delitem__, and the associated mapping methods. The same is true for Jython's other data types.

Adding a Java Class as a Built-In Jython Module

When you write a Jython module in Java, you also have the option to designate this module as a built-in module. The registry key python.modules.builtin allows you to add modules to the list of built-in modules written in Java. The python.modules.builtin property is a comma-separated list of entries. The entries may appear in three forms. Table 9.3 shows each form of a module entry and an explanation of its use.

Table 9.3. Built-In Modules Entry Syntax and Description

Entry Syntax

Description

name

Just the name of the Java class. This assumes that the class is in the org.python.modules package. Adding this entry to the python.modules.builtin list allows you to use "import name" in Jython. If you wish to add the class org.python.modules.jnios, the entry would appear as follows:

python.modules.builtin = jnios

name:class

This form requires a name and fully qualified class, separated by a colon. The name need not be the class name; it is simply the name Jython uses to refer to this class. If the name duplicates a pre-existing name, the pre-existing module is overridden. To add the class com.mycompany.python.agent as the name mycompanyagent use the following:

python.modules.builtin =

mycompanyagent:org.mycompany.python.agent

name:null

This removes the module name from the list of built-in modules. To remove the os module, use the following:

python.modules.builtin = os:null

The python.modules.builtin property is unique in how it is set. Currently setting this property with the -D command-line switch does not add a module to this built-in list. It must be set in the registry file, or in the post-properties (second arg) in the initialize method. To add mymod as a module, compile it, and ensure that it is in the correct directory tree for its package and in the classpath. Then edit the registry file, adding the following:

python.modules.builtin = "mymod:org.python.demo.modules.mymod" 


The name, or portion left of the colon, could be any legal identifier, but the class, or right side of the colon, must be a full package.class string uniquely identifying the appropriate class. Once a Java class is added as a built-in, it is available in Jython with just an import of its simple name. The mymod from the preceding example is available with the following:

>>> import mymod 


This same mechanism for defining a built-in allows you to substitute a module for an existing built-in, or remove a built-in. All of this contributes to the ease of which Jython is extended and customized with Java.

Screenshot CONTENTS Screenshot
Comments