Jython Course Outline
Author: | Dave Kuhlman |
---|---|
Address: | dkuhlman@rexx.com http://www.rexx.com/~dkuhlman |
Revision: | 1.1a |
Date: | Nov 12, 2007 |
Copyright: | Copyright (c) 2006 Dave Kuhlman. All Rights Reserved. This software is subject to the provisions of the MIT License http://www.opensource.org/licenses/mit-license.php. |
Abstract
This document provides an outline of an introductory course on programming in Jython and connecting Jython to Java
Contents
- 1 How-to Write Jython Code
- 2 Installing and Running Jython
- 3 Integrating Java into Jython/Python
- 4 Integrating Java into Jython/Python
- 5 Integrating Jython/Python into Java
- 5.1 Calling Jython from Java
- 5.2 Embedding Jython into Java
- 5.2.1 Why embedded Jython
- 5.2.2 Embedding Jython into Java is simple
- 5.2.3 Passing values into a Jython script
- 5.2.4 Initializing the interpreter
- 5.2.5 Retrieving values from a Jython script
- 5.2.6 There are also a few complexities
- 5.2.7 Exposing transparent objects
- 5.2.8 Exposing opaque objects
- 5.2.9 Type conversion
- 5.2.10 Using a custom class loader
- 5.2.11 Embedding a Jython console
- 6 Additional Exercises
- 7 References and Sources
1 How-to Write Jython Code
Jython is Python. That's one of its big advantages: you get two for the price of one. If your learn Python, then you have also learned Jython, and vice versa. If you already know Python, then you know Jython.
But, if you do not know Python or Jython, then here are good training aids:
2 Installing and Running Jython
2.1 Install Jython
You will need Java installed, of course. And, since you are likely to want to use Jython class libraries from Jython, it is also likely that you will want the Java SDK. Important: If more than one version of Java is installed on your machine, make sure that when you install Jython using the version of Java for which the SDK is installed and the version of Java that you will be using when you run Jython.
Download the Jython installation jar file -- You can file the Jython distribution here: Jython downloads -- http://jython.org/Project/download.html.
Install Jython -- Follow the instructions at: Jython installation -- http://jython.org/Project/installation.html:
$ java -jar jython_installer-2.2.jar
Command line history -- On MS Windows, command line history for the Jython interactive interpreter comes built-in. On Linux, to get command line history, command line editing, and readline support, follow the instructions here: ReadlineSetup -- http://wiki.python.org/jython/ReadlineSetup.
2.2 Configuration
There are several places to configure Jython.
2.2.2 Jython configuration files
For explanation of configuration options and values, see:
- The comments in the (default) registry file.
- The Jython Registry -- http://www.jython.org/docs/registry.html.
2.2.3 Checking configuration values
From within the Jython interactive interpreter or from within your Jython application, you can display the values of configuration properties.
To get the system properties as a dictionary-like object, do:
>>> from java.lang import System >>> props = System.getProperties()
Of particular interest are the following:
- props['java.class.path'] -- Location of the Jython jar file.
- props['java.library.path'] -- Locations of Java class libraries.
Other properties are in sys.registry:
>>> import sys >>> r = sys.registry >>> for k in r: ... print k, r[k]
Here is a script that you may find useful when interactively inspecting system properties:
>>> from java.lang import System >>> props = System.getProperties() >>> names = [] >>> for name in props.keys(): ... names.append(name) ... >>> names.sort() # now you can list the keys in alpha order >>> for val in props['java.class.path'].split(':'): ... print val ... /home/dkuhlman/a1/Python/Jython/Tmp1/Jython-2.1/jython.jar /usr/share/jython/jython.jar
2.2.4 Classpath and python path
Jython can pick up Java class files from locations on either the Jython/Python path (see sys.path) or the Java classpath. Set these with the following:
The Python/Jython path can be set in your registry file. See registry variable python.path.
Or, at runtime, you could do:
>>> import sys >>> sys.path.append('/path/to/module')
But, you must do the above before trying to import the module.
Set the classpath by setting the CLASSPATH environment variable. Note that (on my Linux machine, at least) the CLASSPATH environment variable is picked up and added to the Java -classpath flag.
A few rules about CLASSPATH and python.path:
- sys.path in the registry file -- Add here to enable importing from Java classes (.java), Java class libraries (.jar), and Jython/Python (.py).
- CLASSPATH -- Add paths to this environment variable in order to enable importing from Java classes (.java) and Java class libraries (.jar), but not Jython/Python (.py).
2.3 Running Jython
The Jython interactive, command-line interpreter: jython.
Jython IDEs (interactive development environments) -- There is a Jython plug-in for Eclipse. See: http://pydev.sourceforge.net/.
Exercises -- Start the Jython interpreter. Then do each of the following:
- Print "hello".
- Define an empty class.
- Import a Python/Jython file containing a class definition. Create an instance of that class.
- Import a module from the standard Python/Jython library, for example, re or os.path. Use a method from that module.
- Import a Java class, for example, java.util.Vector. Create and use an instance of that class.
Running Jython scripts:
From the command line, run a script with jython. For example:
$ jython myscript.py
For help, run:
$ jython --help
For debugging, use something similar to the following:
import pdb pdb.run('main()')
Or:
import pdb pdb.set_trace()
For example:
def main(): util101() if __name__ == '__main__': import pdb; pdb.set_trace() main()
To "set a breakpoint" in your code so that it will drop into debugger, either (1) use the b command at the pdb prompt or (2) add the following to your code at the location where you wish to drop into the debugger:
import pdb; pdb.set_trace()
For more information on the Python debugger, see The Python Debugger in the Python standard documentation, or while in the debugger, type help.
To make a script both "run-able" and "import-able", use the following idiom:
if __name__ == '__main__': main()
Don't forget to include a doc string at the top of your module for documentation.
Exercise -- Create a small Jython script:
- Include a class in your script that creates an instance of java.util.Vector.
- Make the script both "run-able" and "import-able".
- From the Jython interpreter, import the script and create an instance of the class.
- From the command line, use jython to run the script.
- Add pdb debugging to your script. Run the script again from the command line. Step through several lines of code.
2.4 Installing Jython/Python packages
Some Jython packages will be distributed as a Java jar file. If that is the case, add the jar file to your classpath.
If the package is distributed as a standard Python package with a setup.py installer file and if there are no C/C++ files in the package, then you might try something like the following:
$ python setup.py install --prefix /path/to/install/directory
And, then put that install directory on your classpath.
3 Integrating Java into Jython/Python
3.1 Calling existing Java code
In order to call Java code from Jython do the following:
- Import the Java module.
- Use the Java module to create an instance/object.
- Call functions and objects in it.
It works the way you would hope and expect it to. Here is an example:
>>> from java.util import Vector >>> v = Vector() >>> dir(v) ['__init__', 'add', 'addAll', 'addElement', 'capacity', 'class', 'clear', 'clone', 'contains', 'containsAll', 'copyInto', 'elementAt', 'elements', 'empty', 'ensureCapacity', 'equals', 'firstElement', 'get', 'getClass', 'hashCode', 'indexOf', 'insertElementAt', 'isEmpty', 'iterator', 'lastElement', 'lastIndexOf', 'listIterator', 'notify', 'notifyAll', 'remove', 'removeAll', 'removeAllElements', 'removeElement', 'removeElementAt', 'retainAll', 'set', 'setElementAt', 'setSize', 'size', 'subList', 'toArray', 'toString', 'trimToSize', 'wait'] >>> >>> v.add('aaa') 1 >>> v.add('bbb') 1 >>> for val in v: ... print val ... aaa bbb
In some cases you will need to pass Java objects to Java methods.
Special treatment for some overloaded Java methods -- Explicitly create and pass Jython objects. For more on this, see: Overloaded Java Method Signatures -- http://www.jython.org/Project/userguide.html#overloaded-java-method-signatures.
Often you can use Python/Jython style and idioms to process Java objects. For example: the Jython for statement can be applied to Java collection objects.
Exercise -- Use the class java.util.Hashtable to create a dictionary with several keys and values, then print out the keys and their values. Solution:
>>> from java.util import Hashtable >>> impl_language = Hashtable() >>> impl_language.put('jython', 'java') >>> impl_language.put('python', 'c') >>> for key in impl_language.keys(): ... print '%s is implemented in %s' % (key, impl_language[key]) ... python is implemented in c jython is implemented in java
Which, when connected to my trivial, little database, prints out the following:
Name Description Rating ==== =========== ====== tomato red and tasty 8 peach sweet and succulent 8 tangerine sweet but tart 7
Resources:
- More information on zxJDBC: http://jython.org/Project/userguide.html#database-connectivity-in-jython
- The JDBC driver for PostgreSQL: http://jdbc.postgresql.org/
- The JDBC driver for MySQL: http://www.mysql.com/products/connector/j/
- Python Tabular Databases SIG: Status -- http://www.python.org/community/sigs/current/db-sig/status/
- The Python Database Topic Guide -- http://www.python.org/doc/topics/database/
- Import a Java class, for example, java.util.Vector. Create and use an instance of that class.
Running Jython scripts:
From the command line, run a script with jython. For example:
$ jython myscript.py
For help, run:
$ jython --help
For debugging, use something similar to the following:
import pdb pdb.run('main()')
Or:
import pdb pdb.set_trace()
For example:
def main(): util101() if __name__ == '__main__': import pdb; pdb.set_trace() main()
To "set a breakpoint" in your code so that it will drop into debugger, either (1) use the b command at the pdb prompt or (2) add the following to your code at the location where you wish to drop into the debugger:
import pdb; pdb.set_trace()
For more information on the Python debugger, see The Python Debugger in the Python standard documentation, or while in the debugger, type help.
To make a script both "run-able" and "import-able", use the following idiom:
if __name__ == '__main__': main()
Don't forget to include a doc string at the top of your module for documentation.
Exercise -- Create a small Jython script:
- Include a class in your script that creates an instance of java.util.Vector.
- Make the script both "run-able" and "import-able".
- From the Jython interpreter, import the script and create an instance of the class.
- From the command line, use jython to run the script.
- Add pdb debugging to your script. Run the script again from the command line. Step through several lines of code.
Some Jython packages will be distributed as a Java jar file. If that is the case, add the jar file to your classpath.
If the package is distributed as a standard Python package with a setup.py installer file and if there are no C/C++ files in the package, then you might try something like the following:
$ python setup.py install --prefix /path/to/install/directory
And, then put that install directory on your classpath.
4 Integrating Java into Jython/Python
4.1 Calling existing Java code
In order to call Java code from Jython do the following:
- Import the Java module.
- Use the Java module to create an instance/object.
- Call functions and objects in it.
It works the way you would hope and expect it to. Here is an example:
>>> from java.util import Vector >>> v = Vector() >>> dir(v) ['__init__', 'add', 'addAll', 'addElement', 'capacity', 'class', 'clear', 'clone', 'contains', 'containsAll', 'copyInto', 'elementAt', 'elements', 'empty', 'ensureCapacity', 'equals', 'firstElement', 'get', 'getClass', 'hashCode', 'indexOf', 'insertElementAt', 'isEmpty', 'iterator', 'lastElement', 'lastIndexOf', 'listIterator', 'notify', 'notifyAll', 'remove', 'removeAll', 'removeAllElements', 'removeElement', 'removeElementAt', 'retainAll', 'set', 'setElementAt', 'setSize', 'size', 'subList', 'toArray', 'toString', 'trimToSize', 'wait'] >>> >>> v.add('aaa') 1 >>> v.add('bbb') 1 >>> for val in v: ... print val ... aaa bbb
In some cases you will need to pass Java objects to Java methods.
Special treatment for some overloaded Java methods -- Explicitly create and pass Jython objects. For more on this, see: Overloaded Java Method Signatures -- http://www.jython.org/Project/userguide.html#overloaded-java-method-signatures.
Often you can use Python/Jython style and idioms to process Java objects. For example: the Jython for statement can be applied to Java collection objects.
Exercise -- Use the class java.util.Hashtable to create a dictionary with several keys and values, then print out the keys and their values. Solution:
>>> from java.util import Hashtable >>> impl_language = Hashtable() >>> impl_language.put('jython', 'java') >>> impl_language.put('python', 'c') >>> for key in impl_language.keys(): ... print '%s is implemented in %s' % (key, impl_language[key]) ... python is implemented in c jython is implemented in java
4.2 Extending a Java class in Jython
You can import and then extend (sub-class) a Java class.
Example -- This sample extends the Java filestream class by adding a method that converts all characters to upper case::
import sys from java.io import FileOutputStream class UppercaseFileOutputStream(FileOutputStream): def write_upper(self, text): text = text.upper() self.write(text) def test(outfilename): fos = UppercaseFileOutputStream(outfilename) for idx in range(10): fos.write_upper('This is line # %d\n' % idx) fos.close() infile = open(outfilename, 'r') for line in infile: line = line.rstrip() print 'Line: %s' % line def main(): args = sys.argv[1:] if len(args) != 1: print 'usage: extend_fileoutputstream.py <infilename>' sys.exit(1) test(args[0]) if __name__ == '__main__': main()
4.3 Emulating Jython classes in Java
You can make a Java class "act like" one of the built-in Jython classes. In order to do so, you would implement one or more of Jython's special methods. You can find descriptions of the special methods in the "Python Reference Manual": 3.4 Special method names -- http://docs.python.org/ref/specialnames.html.
Example: This module implements a class that acts like a sequence in certain ways, specifically (1) it responds to the len() operator by returning a length; (2) it supports an append method; and (3) it supports the use of the [] operator to get a value:
import java.util.Vector; // Implement selected part of the Jython container interface. public class CustomContainer { private Vector data; public CustomContainer() { data = new Vector(); } // Implement the len() operator. public int __len__() { return data.size(); } // Implement the append() method. public int append(String item) { data.add(item); return 1; } // Implement the [] operator. public String __getitem__(int index) { return (String)data.elementAt(index); } }
And here is an example of the use of this custom container class:
$ jython Jython 2.2.1rc1 on java1.4.2_10 Type "copyright", "credits" or "license" for more information. >>> >>> import CustomContainer as cc >>> container = cc() >>> container.append('item number one') 1 >>> container.append('item number two') 1 >>> container.append('item number three') 1 >>> len(container) 3 >>> for index in range(len(container)): ... print container[index] ... item number one item number two item number three
Notes:
- A more powerful solution, depending on your needs, would be for CustomContainer to inherit from java.util.Vector. See section Emulating Jython Dictionaries, Sequences, Etc. for an example.
4.4 Preparing Java code to be called from Jython
Another view: Java is the extension language for Jython.
No special work is required. Jython can call normal Java classes.
Need to pay attention to data types, for example, on the Jython side. Use an explicit cast, for example, float(5).
For additional help, see:
- Overview of Jython Documentation
- The Jython API with frames or without frames.
You can also customize a Java class to make it more "Jythonic".
4.4.1 Adding doc strings to a Java class
This first, simple example adds doc strings:
// Showme.java import org.python.core.*; public class ShowMe { public static PyString __doc__ = new PyString("Simple Jython extension #1"); public String name; public ShowMe(String newName) { name = newName; } public static PyString __doc__set_name = new PyString( "Set the name attribute"); public void set_name(String newName) { name = newName; } public static PyString __doc__get_name = new PyString( "Get the name attribute"); public String get_name() { return name; } public static PyString __doc__Show = new PyString( "Show the name attribute"); public void Show() { System.out.println("My name is \"" + name + "\"."); } }
Notes:
- Doc strings for the class and methods are defined with public static Strings. You can, alternatively, use PyString.
- For more complex control over doc strings (for example, in a Java files that contains multiple classes) your class can implement the ClassDictInit interface and implement the classDictInit method. See "Jython for Java Programmers", pp. 276 ff.
4.4.2 Working with Jython arguments
The ArgParser class helps us handle Jython keyword arguments. If helps us support the analog of Jython's *args and **kwargs in Java methods.
How to do it -- An overview:
Define your Java method with the following prototype:
public PyObject foo(PyObject[] args, String[] keywords);
- Parse the arguments with class ArgParser.
- Access individual arguments with ArgParser methods getInt(), getString(), getList(), and getPyObject().
- Since both args and keywords are arrays, check the number of arguments actually passed with args.length and keywords.length.
For more information, see: org.python.core Class ArgParser.
Exercise -- (1) Write a Java class containing a method that prints all its arguments and all the keyword arguments passed to it. (2) Then call that method from Jython.
Solution:
// DemoArgs.java import org.python.core.*; public class DemoArgs { public static PyString __doc__ = new PyString("Demonstrate the use of complex arguments."); public String name; public String value; public DemoArgs(String newName, String newValue) { name = newName; value = newValue; } public static PyString __doc__set_name = new PyString( "Set the name attribute"); public void set_name(PyObject[] args, String[] kwargs) { System.out.println("length(args): " + String.valueOf(args.length) + " length(kwargs): " + String.valueOf(kwargs.length) ); ArgParser ap = new ArgParser("set_name", args, kwargs, new String[] {"name", "value"}); String newName = ap.getString(0, ""); String newValue = ap.getString(1, "<empty>"); if (newName.compareTo("") != 0) { name = newName; } value = newValue; } public static PyString __doc__get_name = new PyString( "Get the name attribute"); public String get_name() { return name; } public static PyString __doc__get_value = new PyString( "Get the value attribute"); public String get_value() { return value; } public static PyString __doc__Show = new PyString( "Show the name and value attributes"); public void Show() { System.out.println("My name is \"" + name + "\" and my value is \"" + value + "\"."); } }
Compile the above file with javac or some other Java compiler. To do so, you will need to add jython.jar to your CLASSPATH.
Notes:
- Use class ArgParser to capture the arguments.
- Use ArgParser methods getInt, getString, getPyObject, and getList to retrieve arguments.
- Notice that in method get_name, we print the length of the args and kwargs. This demonstrates that you can check the length of these arrays and can throw an exception if, for example, too few arguments are passed.
Also see the Jython FAQ: 5.3 Supporting *args and **kw in Java methods -- http://jython.org/Project/userfaq.html#supporting-args-and-kw-in-java-methods.
4.4.3 Sub-classing a Java class
Notice that, in Jython, we can extend a class written in Java:
import DemoArgs class Fancy(DemoArgs): def __init__(self, name, value): DemoArgs.__init__(self, name, value) def ShowFancy(self): print "I'm fancy and my name is %s and my value is %s" % \ (self.name, self.value) def test(): f = Fancy('dave', 'funny') f.ShowFancy() f.set_name('daniel', 'cute') f.ShowFancy() test()
When you run the above, you should see something like the following:
$ jython tmp.py I'm fancy and my name is dave and my value is funny length(args): 2 length(kwargs): 0 I'm fancy and my name is daniel and my value is cute
4.4.4 Emulating Jython Dictionaries, Sequences, Etc.
Extend class org.python.core.PyObject and its sub-classes. See: org.python.core Class PyObject.
Implement the following methods:
__getitem__() __finditem() __setitem__() __delitem__() ...
getitem() vs. finditem():
- If the index is not found or out of range, finditem() returns null, whereas __getitem() should throw an exception.
- The Jython API documentation says to override finditem() and not getitem(). See: org.python.core Class PyObject.
See 3.3.5 Emulating container types -- http://docs.python.org/ref/sequence-types.html in the Python Reference Manual for more information on customizing dictionaries and sequences.
Exercise -- (1) Write a Java class that emulates or imitates a Jython dictionary. (2) In addition, each access method should print a message. (3) Test your Java class from Jython by creating an instance of it, then setting and retrieving a key-value pair.
4.4.4.1 Solution #1 -- Emulating a dictionary
This solution is for educational purposes only (see Solution #2 -- Extending PyDictionary):
// TestDict.java import org.python.core.*; import java.util.*; public class TestDict { public Hashtable data; public TestDict() { data = new Hashtable(); } public void __setitem__(String key, String value) { data.put(key, value); System.out.println("Added key \"" + key + "\" value: \"" + value + "\""); } public String __getitem__(String key) { if (data.containsKey(key)) { String value = (String)data.get(key); System.out.println("Found key \"" + key + "\" value: \"" + value + "\""); return value; } else { throw new PyException(Py.KeyError, "The key does not exit."); } } public boolean __contains__(String key) { if (data.containsKey(key)) { System.out.println("Found key \"" + key + "\""); return true; } else { System.out.println("Did not find key \"" + key + "\""); return false; } } }
Notes:
The above class implements a limited part of the Jython dictionary protocol, in particular __setitem__, __getitem__, and __contains__.
This above solution also illustrates how to throw ("raise" in Jython terms) an exception from Java that can be caught in Jython. Here is an example of catching that exception on the Jython side:
>>> try: ... x = b['xyz'] ... except KeyError, e: ... print '*** error: %s' % e ... *** error: The key does not exit.
The Jython FAQ recommends that your Jython class extends PyObject. (see 5. Extending Jython -- http://jython.org/Project/userfaq.html#extending-jython) I've found that it is not strictly necessary to extend PyObect in your Java class (the one that emulates a Jython built-in). But, if you do, you will need to follow the signature of the methods that implement operators (for example __setitem__, __getitem__, etc) exactly. To learn those signatures, see the API documentation in the Doc/ directory under your Jython installation.
Here is an example that uses the above solution:
# test_TestDict.py import TestDict def test(): d = TestDict() d['aa'] = 'AAAA' d['bb'] = 'BBBB' d['cc'] = 'CCCC' print d.data if 'bb' in d: print 'present' test()
And, here is the result of running this test:
$ jython test_TestDict.py Added key "aa" value: "AAAA" Added key "bb" value: "BBBB" Added key "cc" value: "CCCC" {aa=AAAA, bb=BBBB, cc=CCCC} Found key "bb" present
4.4.4.2 Solution #2 -- Extending PyDictionary
This solution shows how you most likely would start if you wanted to extend the dictionary type or implement a custom dictionary type:
// TestDictSub.java import org.python.core.*; import java.util.*; public class TestDictSub extends PyDictionary { public void __setitem__(PyObject key, PyObject value) { super.__setitem__(key, value); System.out.println("Added key \"" + key + "\" value: \"" + value + "\""); } public PyObject __getitem__(PyObject key) { if (super.has_key(key)) { PyObject value = super.__getitem__(key); System.out.println("Found key \"" + key + "\" value: \"" + value + "\""); return value; } else { throw new PyException(Py.KeyError, "The key does not exit."); } } }
Notes:
- This class inherits the methods in the PyDictionary class. It overrides several of those methods, specifically __setitem__ and __getitem__.
- The Java class could also extend the dictionary type by implementing additional, new methods.
Also see the Jython FAQ: 5.1 Java classes that emulate Jython Dictionaries and Sequences -- http://jython.org/Project/userfaq.html#java-classes-that-emulate-jython-dictionaries-and-sequences.
4.4.5 Emulating Jython object attribute access
We can implement and override object attribute access in a Java class:
Extend class org.python.core.PyObject and its sub-classes.
Implement the following methods:
__findattr__() __setattr__() __delattr__()
__findattr__() is called only if an attribute is not found in an object.
Exercise -- (1) Write a Java class class that supports access to attributes. (2) In addition, each access method should print a message. (3) Test your Java class from Jython by creating an instance of it, then setting and getting an attribute.
Solution:
// TestDictSub.java import org.python.core.*; import java.util.*; public class TestDictAttr extends PyDictionary { public PyObject __findattr__(String key) { PyString objkey = new PyString(key); if (super.has_key(objkey)) { PyObject value = super.__getitem__(objkey); System.out.println("Found attr \"" + key + "\" value: \"" + value + "\""); return value; } else { throw new PyException(Py.KeyError, "The attr does not exist."); } } }
Notes:
Test this solution with the following:
$ jython Jython 2.2a1 on java1.4.2 (JIT: null) Type "copyright", "credits" or "license" for more information. >>> >>> import TestDictAttr >>> a = TestDictAttr() >>> print a.dave Traceback (innermost last): File "<console>", line 1, in ? KeyError: The attr does not exit. >>> a['dave'] = 'some little value' >>> print a.dave Found attr "dave" value: "some little value" some little value
Arguments to __findattr__ and __finditem__ must be interned strings. Literal strings are automatically interned. For other strings, use intern(s).
Also see the Jython FAQ: 5.2 Emulating Jython object attribute access with a Java class -- http://jython.org/Project/userfaq.html#emulating-jython-object-attribute-access-with-a-java-class.
4.5 Extending a built-in Jython class in Java
In Java, you can inherit from an extend the built-in Jython classes.
Some of the classes that you can consider extending are:
- PyDictionary
- PyInteger
- PyList
- PyString
- PyStringMap
- PyTuple
An example that extends the PyDictionary class is in section Solution #2 -- Extending PyDictionary.
5 Integrating Jython/Python into Java
5.1 Calling Jython from Java
5.1.1 Run Jython code on an interpreter embedded in Java
jythonc is currently unsupported and is deprecated, although it might reappear in some future version of Jython. So, using jythonc to compile your Jython code to Java for use in your Java code may not appeal to you. An embedded Jython interpreter may be a solution.
Overview -- Here is the general approach:
- Create a Jython interpreter object.
- Insert (set) values in your embedded interpreter, if needed.
Use that interpreter to either:
- Run several lines of code that import and use your Jython module, or
- Run a small wrapper script that imports and uses your Jython modules.
- Retrieve values from your embedded interpreter, if necessary.
Each of these topics has been covered above.
Disadvantages of this approach:
- It's a little clumsy. Requires a small amount of Java code.
Advantages of this approach:
- jythonc is not required. jythonc is deprecated and is not planned for Jython 2.3.
- No need for a separate compile step.
- No need to re-compile your script each time it is modified.
5.1.2 How to run Jython code on an interpreter embedded in Java
Resources -- For instructions on how to call Jython code from Java, see:
- Accessing Jython from Java Without Using jythonc http://wiki.python.org/jython/JythonMonthly/Articles/September2006/1
- Simple and Efficient Jython Object Factories http://wiki.python.org/jython/JythonMonthly/Articles/October2006/3 -- Note that the following examples were developed from this document.
Description -- In order to implement this approach, here is what you will do:
- Implement one or more Jython/Python modules containing one or more classes. Or, perhaps this code already exists.
- Create a Java interface for each Jython class that you want to expose to Java. The interface should describe each method that you wish to expose to Java.
- Create a single Java "factory" class:
- The constructor (1) creates a Jython interpreter; (2) runs a script that imports the Jython modules containing the Jython classes; (3) retrieves and saves the Jython classes from the intrepreter.
- Implement one "createX" method for each Jython/Python class/type to be used from Java.
- Implement the Java code that uses the Jython classes. This Java
code will typically do the following:
- Create a factory object.
- Call the "createX" methods to create Java objects that give access to the Jython classes.
- Call the Jython methods through these Jython "proxies".
5.1.2.1 Example -- a possible organization on disk
|-- Employee.py # The Jython classes |-- jyinterface | |-- Main.java # The client code | |-- factories | | |-- EmployeeFactory.java # The factory | `-- interfaces | |-- DependentType.java # A Java interface | `-- EmployeeType.java # A Java interface
Notes:
- The Jython classes (in this case, Employee.py) can go anywhere on your Jython/Python path. See python.path in your registry file: The Jython Registry -- http://jython.org/Project/userguide.html#the-jython-registry.
- Layout for the Java code follows normal Java rules for packages.
5.1.2.2 Example -- the Jython classes
These are the Jython classes that we wish to expose to and call from Java:
# Employee.py from jyinterface.interfaces import EmployeeType from jyinterface.interfaces import DependentType class Employee(EmployeeType): def __init__(self, first, last, id): self.first = first self.last = last self.id = id deps = self.create_dependents() self.deps = deps def create_dependents(self): d1 = Dependent('Sally', 'Serious', 11) d2 = Dependent('Larry', 'Lighthearted', 12) return [d1, d2] def getEmployeeFirst(self): return self.first def getEmployeeLast(self): return self.last def getEmployeeId(self): return self.id def getDependents(self): return self.deps def addDependent(self, dependent): self.deps.append(dependent) class Dependent(DependentType): def __init__(self, first, last, id): self.first = first self.last = last self.id = id def getDependentFirst(self): return '<<%s>>' % self.first def getDependentLast(self): return '<<%s>>' % self.last def getDependentId(self): return self.id * 4
5.1.2.3 Example -- A Java interface class
This (jyinterface/interfaces/EmployeeType.java) describes the interface to our Java (client) code:
// EmployeeType.java package jyinterface.interfaces; import org.python.core.PyList; import jyinterface.interfaces.DependentType; public interface EmployeeType { public String getEmployeeFirst(); public String getEmployeeLast(); public String getEmployeeId(); public PyList getDependents(); public void addDependent(DependentType dependent); }
5.1.2.4 Example -- another interface class
This (jyinterface/interfaces/DependentType.java) is another interface. It describes and helps to expose another Jython class.
// DependentType.java package jyinterface.interfaces; public interface DependentType { public String getDependentFirst(); public String getDependentLast(); public Integer getDependentId(); }
Notes:
- We only describe the portions of the interface that we wish to expose to Java. It is likely that this will be a proper subset of the methods in our Jython class.
5.1.2.5 Example -- the factory class
This (jyinterface/factories/EmployeeFactory.java) is the factory that creates (proxy) instances of our Jython classes. These instances are Java instances with an underlying Jython implementation:
// EmployeeFactory.java package jyinterface.factory; import jyinterface.interfaces.EmployeeType; import jyinterface.interfaces.DependentType; import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyInteger; import org.python.util.PythonInterpreter; public class EmployeeFactory { public EmployeeFactory() { String cmd = "from Employee import Employee\nfrom Employee import Dependent"; PythonInterpreter interpreter = new PythonInterpreter(); //interpreter.exec("from Employee import Employee"); //interpreter.exec("from Employee import Dependent"); interpreter.exec(cmd); jyEmployeeClass = interpreter.get("Employee"); jyDependentClass = interpreter.get("Dependent"); } public EmployeeType createEmployee(String first, String last, String id) { PyObject employeeObj = jyEmployeeClass.__call__( new PyString(first), new PyString(last), new PyString(id)); return (EmployeeType)employeeObj.__tojava__(EmployeeType.class); } public DependentType createDependent(String first, String last, int id) { PyObject dependentObj = jyDependentClass.__call__( new PyString(first), new PyString(last), new PyInteger(id)); return (DependentType)dependentObj.__tojava__(DependentType.class); } private PyObject jyEmployeeClass; private PyObject jyDependentClass; }
Notes:
- The Jython interpreter is created only once, when the factory object is created.
- The Java classes that are proxies for the Jython classes exposed to Java (jyEmployeeClass amd jyDependentClass) are also only created once.
- Each "creater" method (createEmployee and createDependent) uses the __call__ method to create an instance. This is the same as using obj = Employee(), for example, in Jython. Then the result is converted to a Java object and returned.
5.1.2.6 Example -- the Java consumer code
This (jyinterface/Main.java) is the Java code that uses our Jython classes:
// Main.java package jyinterface; import jyinterface.factories.EmployeeFactory; import jyinterface.interfaces.EmployeeType; import jyinterface.interfaces.DependentType; import org.python.core.PyObject; import org.python.core.PyList; public class Main { private static void printEmployee(EmployeeType employee) { System.out.println("Name: " + employee.getEmployeeFirst() + " " + employee.getEmployeeLast()); System.out.println("Id: " + employee.getEmployeeId()); PyList deplist = employee.getDependents(); int count = deplist.__len__(); System.out.println("count: " + Integer.toString(count)); for (int idx = 0; idx < count; idx++) { PyObject obj = deplist.__getitem__(idx); DependentType dep = (DependentType)obj.__tojava__(DependentType.class); printDependent(dep); } } private static void printDependent(DependentType dependent) { System.out.println("Dep Name: " + dependent.getDependentFirst() + " " + dependent.getDependentLast()); System.out.println("Dep Id: " + dependent.getDependentId().toString()); } public static void main(String[] args) { EmployeeFactory factory = new EmployeeFactory(); EmployeeType emp = factory.createEmployee("Josh", "Juneau", "1"); printEmployee(emp); printEmployee(factory.createEmployee("Charlie", "Groves", "2")); System.out.println("------------------"); DependentType dependent = factory.createDependent( "Dave", "Kuhlman", 4); printDependent(dependent); System.out.println("------------------"); emp.addDependent(dependent); printEmployee(emp); } }
Notes:
This client code creates a factory object, then uses that factory object to create several Java objects that are proxies for the Jython Employee and Dependent objects.
Then the client code calls several of the methods that are implemented in the Jython code and exposed to Java through the Java interfaces.
On the Java side, we apply operators to a list object (and other Jython built-in types) by calling their methods directly, for example:
- len() -- obj.__len__()
- obj[idx] -- obj.__getitem__(idx)
For descriptions of these "special" methods, see the following section in the Python language reference: 3.4 Special method names -- http://docs.python.org/ref/specialnames.html.
On the Java side, we use a Jython object by
Retrieving it as a generic PyObject.
Converting it to a Java object with:
obj.__tojava__(XXType.class)
where XXType is the Java interface that describes the Jython object.
Casting the object to the appropriate Java type.
Calling its "Java" methods.
We can pass a Jython object back to Jython from Java. Notice the call to method addDependent().
5.1.3 Additional support for running Jython code on an interpreter embedded in Java
In what follows, we provide and describe scripts that assist in using the above strategy.
- generate_interfaces.py -- Does some of the work of generating the Java interface files and the Java factory. You will still have to edit the generated files (for example, to fill in type information). But, it may save you a good deal of typing. You can find it here: Jython/Java interface generator -- http://www.rexx.com/~dkuhlman/JythonInterfaceGenerator-1.0a.tar.gz.
5.2 Embedding Jython into Java
5.2.1 Why embedded Jython
There are several reasons and purposes for embedding the Jython interpreter into your Java application:
- You want to offer your users the flexibility and power of customizing and extending your application. An embedded Jython interpreter enables them to run Jython scripts in your application and to communicate with it.
- You have existing Jython code, and you want to use that code in your Java application. Note that this is a capability that might have been handled by using the jythonc compiler to compile your Jython script into Java code, but jythonc is deprecated. For an example of this technique and information on how to do it, see section Calling Jython from Java.
5.2.2 Embedding Jython into Java is simple
Embedding the Jython interpreter can be as simple as this:
// File: SimpleEmbedded.java import org.python.util.PythonInterpreter; import org.python.core.*; import java.io.*; public class SimpleEmbedded { public static void main(String[]args) throws PyException, IOException { BufferedReader terminal; PythonInterpreter interp; terminal = new BufferedReader(new InputStreamReader(System.in)); System.out.println ("Hello"); interp = new PythonInterpreter(); interp.exec("import sys"); interp.exec("print sys"); interp.set("a", new PyInteger(42)); interp.exec("print a"); interp.exec("x = 2+2"); PyObject x = interp.get("x"); System.out.println("x: " + x); PyObject localvars = interp.getLocals(); interp.set("localvars", localvars); String codeString = ""; String prompt = ">> "; while (true) { System.out.print (prompt); try { codeString = terminal.readLine(); if (codeString.compareTo("exit") == 0) { System.exit(0); break; } interp.exec(codeString); } catch (IOException e) { e.printStackTrace(); } } System.out.println("Goodbye"); } }
5.2.3 Passing values into a Jython script
Notice the call interp.set("a", new PyInteger (42)); in the above example.
You can also retrieve the dictionary object representing the namespace for the interpreter, then retrieve objects from that dictionary. Example:
PyDictionary namespace = interp.getLocals(); PyObject obj = namespace.__getitem__("x"); Integer x = obj.__tojava__(Integer.class);
5.2.4 Initializing the interpreter
You can also create a Jython interpreter with an initial global namespace:
// File: TestInterp01.java import org.python.util.PythonInterpreter; import org.python.core.*; import java.io.*; public class TestInterp01 { public static void main(String[]args) throws PyException, IOException { PythonInterpreter interp; PyString key; PyInteger value; System.out.println ("Hello"); PyDictionary table = new PyDictionary(); key = new PyString("aaa"); value = new PyInteger(111); table.__setitem__(key, value); key = new PyString("bbb"); value = new PyInteger(222); table.__setitem__(key, value); interp = new PythonInterpreter(table); interp.exec("print 'aaa:', aaa"); interp.exec("print 'bbb:', bbb"); } }
5.2.4.1 An interpreter with an initial system state
And, you can create an interpreter with an initial system state:
// File: TestInterp02.java import org.python.util.PythonInterpreter; import org.python.core.*; import java.io.*; public class TestInterp02 { public static void main(String[]args) throws PyException, IOException { PythonInterpreter interp; PyString key; PyInteger value; System.out.println ("Hello"); PyDictionary table = new PyDictionary(); key = new PyString("aaa"); value = new PyInteger(111); table.__setitem__(key, value); key = new PyString("bbb"); value = new PyInteger(222); table.__setitem__(key, value); PySystemState state = new PySystemState(); interp = new PythonInterpreter(table, state); interp.exec("print 'aaa:', aaa"); interp.exec("print 'bbb:', bbb"); } }
5.2.4.2 A system state and a custom class loader
This is of interest because the system state can be used to provide a custom class loader:
PyDictionary table = new PyDictionary(); RestrictedClassLoader classLoader = new RestrictedClassLoader(); PySystemState state = new PySystemState(); state.setClassLoader(classLoader); PythonInterpreter interp = new PythonInterpreter(table, state);
And here is a sample class loader. This one merely restricts the classes that can be loaded to a small set:
// RestrictedClassLoader.java import java.util.Vector; import java.util.Enumeration; import java.lang.ClassNotFoundException; class RestrictedClassLoader extends ClassLoader { Vector goodnames; public RestrictedClassLoader () { goodnames = new Vector(); goodnames.add("Goodclass1"); goodnames.add("Goodclass2"); goodnames.add("Goodclass3"); goodnames.add("Goodclass4"); } public Class findClass(String name) throws ClassNotFoundException { Enumeration items = goodnames.elements(); while (items.hasMoreElements()) { String item = (String)items.nextElement(); if (item.compareTo(name) == 0) { return super.findClass(name); } } throw new ClassNotFoundException("class loading restricted. " + name + " not allowed."); } }
5.2.5 Retrieving values from a Jython script
Notice the call PyObject x = interp.get("x"); in the above example.
You can also retrieve the dictionary object representing the namespace for the interpreter, then add and modify the names and their values in that dictionary. Example:
PyDictionary namespace = interp.getLocals(); PyInteger value = new PyInteger(144); namespace.__setitem__("x", value);
5.2.6 There are also a few complexities
You will want to selectively expose capabilities from your Java application to scripts run by/on the embedded Jython interpreter.
You will want to protect your application from malicious or erroneous scripts.
Here are a few suggestions:
- Describe your possible classes of users (those who will write scripts) with respect to (1) trusted vs. untrusted and (2) error tolerant vs. non-tolerant.
- For users who are trusted and error tolerant, provide transparent objects from your application.
- For users who are trusted and not error tolerant, provide opaque objects, i.e. wrappers for real objects from your application.
- For users who are not trusted, implement a security policy, or do not expose a scripting interface at all.
5.2.7 Exposing transparent objects
Java application objects and values can be passed through to scripts executed or evaluated by the embedded interpreter.
Some mechanisms for passing objects:
- set and get -- Use these to set or retrieve values in the local namespace for the scripts that your embedded interpreter will run or has run.
- setLocals and getLocals -- Using these methods, you can pass or retrieve the entire namespace. If you are inserting values to be used (or shared) by scripts, you may want to retrieve and, possibly, copy the initial namespace. Remember that is a Jython dictionary, so modifying it without copying may affect other scripts running in the same interpreter.
5.2.8 Exposing opaque objects
This is similar to the strategy for transparent objects, except that you must implement wrapper classes, then provide instances of these classes instead of instances of transparent objects.
5.2.9 Type conversion
Mostly, Jython takes care of this for you.
However, at times it may help to know what conversions are performed.
And, you can also perform explicit conversions.
5.2.10 Using a custom class loader
You can control access to Java classes with a custom class loader. There is an example in section A system state and a custom class loader.
5.2.11 Embedding a Jython console
This example shows how to embed a Jython interactive console into a Java program:
import org.python.util.InteractiveConsole; import java.util.Properties; import java.io.*; public class interactive_console1 { protected InteractiveConsole interp; public interactive_console1() { if (System.getProperty("python.home") == null) { System.setProperty("python.home", ""); } InteractiveConsole.initialize(System.getProperties(), null, new String[0]); interp = new InteractiveConsole(); } public static void main(String[] args) { interactive_console1 con = new interactive_console1(); con.startConsole(); } public void startConsole() { interp.interact("Hello from console."); } }
Notes:
attrs = node.getAttributes() count = attrs.getLength() for idx in range(count):
attr = attrs.item(idx) print ' %s: %s' % (
attr.getNodeName(), attr.getNodeValue(), )node = node.getNextSibling()
- def show_names(node):
"""Show the value of the name element for each person element. """ node = node.getFirstChild() while node:
- if (node.getNodeType() == node.ELEMENT_NODE and
- node.getTagName() == 'person'):
- show_person_name(node)
node = node.getNextSibling()
- def show_person_name(node):
node = node.getFirstChild() while node:
- if (node.getNodeType() == node.ELEMENT_NODE and
- node.getTagName() == 'name'):
- show_text('name: ', node)
node = node.getNextSibling()
- def show_text(msg, node):
"""Show a message and the value of a text node. """ node = node.getFirstChild() while node:
- if node.getNodeType() == node.TEXT_NODE:
- print ' %s %s' % (msg, node.getNodeValue(), )
node = node.getNextSibling()
- def usage():
- print 'Usage: jython test_xerces.py <infilename>' sys.exit(-1)
- def main():
args = sys.argv[1:] if len(args) != 1:
usage()test(args[0])
- if __name__ == '__main__':
- main()
Notes:
- Except for the parser set-up (in function test), this example is almost the same as the JAXP example. For the most part, it uses the same API.
Resources:
- Download dom4j from: dom4j: the flexible XML framework for Java -- http://www.dom4j.org/
- Add the dom4j jar file to your CLASSPATH.
- In order to get some features to work, I also had to install the jaxen XPath engine. Add that to your CLASSPATH, too. See: jaxen: universal Java XPath engine -- http://jaxen.codehaus.org/.
There are examples at:
- dom4j - Quick Start Guide -- http://www.dom4j.org/guide.html
- dom4j cookbook -- http://www.dom4j.org/cookbook.html
Example 1a -- This example parses an XML document (file), then walks the dom4j DOM element tree and prints out information on each node:
import sys from org.dom4j.io import SAXReader def show_indent(level): return ' ' * level def show_node(node, level): """Display one node in the DOM tree. """ if node.getNodeType() == node.ELEMENT_NODE: name = node.getName() print '%sElement node: %s' % (show_indent(level), name, ) attrs = node.attributes() for attr in attrs: aName = attr.getQualifiedName() aValue = attr.getValue() print ' %sAttr -- %s: %s' % (show_indent(level), aName, aValue,) if node.text: print ' %sText: "%s"' % (show_indent(level), node.text,) def show_tree(node, level): show_node(node, level) level1 = level + 1 if node.getNodeType() == node.ELEMENT_NODE: children = node.elements() for child in children: show_tree(child, level1) def test(infilename): print 'Version: %s' % (sys.version, ) reader = SAXReader() doc = reader.read(infilename) root = doc.getRootElement() show_tree(root, 0) def main(): args = sys.argv[1:] if len(args) != 1: print 'usage: test_dom4j_2.py in_xml_file' sys.exit(1) test(args[0]) if __name__ == '__main__': #import pdb; pdb.set_trace() main()
Notes:
- We use node.elements() to get all children that are elements, but not, for example, text nodes.
Example 1b -- This example also parses an XML document (file), then walks the dom4j DOM element tree and prints out information on each node. However, at each node it iterates over all the content nodes, including text nodes:
import sys from org.dom4j.io import SAXReader def show_indent(level): return ' ' * level def show_node(node, level): """Display one node in the DOM tree. """ if node.getNodeType() == node.ELEMENT_NODE: name = node.getName() print '%sElement node: %s' % (show_indent(level), name, ) attrs = node.attributes() for attr in attrs: aName = attr.getQualifiedName() aValue = attr.getValue() print ' %sAttr -- %s: %s' % (show_indent(level), aName, aValue,) elif node.getNodeType() == node.TEXT_NODE: print '%sText node: "%s"' % (show_indent(level), node.text, ) def show_tree(node, level): show_node(node, level) level1 = level + 1 if node.getNodeType() == node.ELEMENT_NODE: children = node.content() for child in children: show_tree(child, level1) def test(infilename): print 'Version: %s' % (sys.version, ) reader = SAXReader() doc = reader.read(infilename) root = doc.getRootElement() show_tree(root, 0) def main(): args = sys.argv[1:] if len(args) != 1: print 'usage: test_dom4j_2.py in_xml_file' sys.exit(1) test(args[0]) if __name__ == '__main__': #import pdb; pdb.set_trace() main()
Notes:
We use node.content() to get all child nodes, including text nodes.
Use tests such as the following to determine whether a node is an element node or a text node:
if node.getNodeType() == node.ELEMENT_NODE: o o o elif node.getNodeType() == node.TEXT_NODE: o o o
Example 2 -- This example creates an dom4j document object and adds element objects to it:
import sys import org.dom4j.DocumentHelper import org.dom4j.io.OutputFormat from org.dom4j.io import XMLWriter from org.dom4j.io import OutputFormat def test(): # 1. # Create a new document and add a few elements to it. doc = org.dom4j.DocumentHelper.createDocument() root = doc.addElement('specs') e1 = root.addElement('class1') e1.addAttribute('name', 'dave') e1.addAttribute('category', 'medium') e1.addText('some simple content') e1 = root.addElement('class2') e1.addAttribute('name', 'mona') e1.addAttribute('category', 'good') e1.addText('more content') # 2. # Print a text representation of the DOM tree. text = doc.asXML() print text print '\n', '-' * 40, '\n' # 3. # Print a formatted (pretty-print) representation. format = OutputFormat.createPrettyPrint() writer = XMLWriter(sys.stdout, format) writer.write(doc) print '\n', '-' * 40, '\n' print 'root:', root, '\n' # 4. # Iterate over the children of the root. # Print child and parent names, etc. itr = root.elementIterator() for idx, child in enumerate(itr): print idx, child parent = child.getParent() print 'parent:', parent.getName() print 'child:', child.getName() print 'text: "%s"' % child.getText() print test()
Notes -- What this example does:
- Creates a new document and adds a few elements to it.
- Produces and prints a text representation of the DOM tree.
- Prints a formatted (pretty-print) representation.
- Iterates over the children of the root. Prints child and parent names, etc.
Resources:
XMLBeans provides the ability to generate Java bindings for an XML document type from an XML Schema. Roughly speaking, XMLBeans generates a Java class for each element type defined in an XML Schema.
When used with Jython, those Java bindings become quite Jythonic.
You can read about XMLBeans here:
- Welcome to XMLBeans -- http://xmlbeans.apache.org/index.html.
- XMLBeans at Wikipedia -- http://en.wikipedia.org/wiki/XMLBeans.
You can find XMLBeans at: Welcome to XMLBeans -- http://xmlbeans.apache.org/index.html.
To install XMLBeans, follow the instructions at: Installing XMLBeans -- http://xmlbeans.apache.org/documentation/conInstallGuide.html.
After unrolling the binary distribution, I added the following to my CLASSPATH:
- $XMLBEANS_HOME/lib/xbean.jar
- $XMLBEANS_HOME/lib/jsr173_1.0_api.jar
And, I added the following to my PATH:
- $XMLBEANS_HOME/bin
where XMLBEANS_HOME is the directory in which XMLBeans is installed.
This example is copied from XMLBeans at Wikipedia -- http://en.wikipedia.org/wiki/XMLBeans and adapted to Jython.
The XML Schema:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema targetNamespace="http://www.openuri.org/domain/country/v1" xmlns:tns="http://www.openuri.org/domain/country/v1" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0"> <xs:element name="Country" type="tns:Country"/> <xs:complexType name="Country"> <xs:sequence> <xs:element name="Name" type="xs:string"/> <xs:element name="Population" type="xs:int"/> <xs:element name="Iso" type="tns:Iso"/> </xs:sequence> </xs:complexType> <xs:complexType name="Iso"> <xs:annotation><xs:documentation>ISO 3166</xs:documentation></xs:annotation> <xs:sequence> <xs:element name="Alpha2" type="tns:IsoAlpha2"/> <xs:element name="Alpha3" type="tns:IsoAlpha3"/> <xs:element name="CountryCode" type="tns:IsoCountryCode"/> </xs:sequence> </xs:complexType> <xs:simpleType name="IsoCountryCode"> <xs:restriction base="xs:int"> <xs:totalDigits value="3"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="IsoAlpha2"> <xs:restriction base="xs:string"> <xs:pattern value="[A-Z]{2}"/> <xs:whiteSpace value="collapse"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="IsoAlpha3"> <xs:restriction base="xs:string"> <xs:pattern value="[A-Z]{3}"/> <xs:whiteSpace value="collapse"/> </xs:restriction> </xs:simpleType> </xs:schema>
Supposing that you name the above schema country.xsd, you can compile this with XMLBeans using something like the following:
$ scomp -out country.jar country.xsd
Here is a bit of Java code, copied from Wikipedia, that uses the generated Java classes:
import org.openuri.domain.country.v1.Country; import org.openuri.domain.country.v1.Iso; public class CountrySample { public static void main(String[] args) { Country country = Country. Factory.newInstance(); country.setName("Denmark"); country.setPopulation(5450661); // from wikipedia :-) // print out country XMLBean as XML System.out.println(country.xmlText()); // check if document is valid - will print "Document is invalid" // because required Iso child element in not in the object System.out.println ("Document is " + (country.validate() ? "valid" : "invalid")); // add child with complex type Iso to make the document valid Iso iso = country.addNewIso(); iso.setAlpha2("DK"); iso.setAlpha3("DNK"); iso.setCountryCode(208); // print out country XMLBean as XML System.out.println(country.xmlText()); // check if document is valid - will print "Document is valid" System.out.println ("Document is " + (country.validate() ? "valid" : "invalid")); } }
After translation to Jython, here is the equivalent code. Note that it uses the same generated Java classes as the above Java code:
from org.openuri.domain.country.v1 import Country from org.openuri.domain.country.v1 import Iso from org.apache.xmlbeans import XmlOptions class CountrySample(object): def main(self, args): country = Country. Factory.newInstance() country.setName("Denmark") country.setPopulation(5450661); # from wikipedia :-) # Print out country XMLBean as XML. print country.xmlText() # Print out country XMLBean as XML with indentation. opts = XmlOptions() opts.setSavePrettyPrint() opts.setSavePrettyPrintIndent(4) print country.xmlText(opts) # Check if document is valid - will print "Document is invalid" # because required Iso child element in not in the object. if country.validate(): condition = "valid" else: condition = "invalid" print "Document is", condition # Add child with complex type Iso to make the document valid. iso = country.addNewIso(); iso.setAlpha2("DK") iso.setAlpha3("DNK") iso.setCountryCode(208) # Print out country XMLBean as XML. print country.xmlText(opts) # Check if document is valid - will print "Document is valid". if country.validate(): condition = "valid" else: condition = "invalid" print "Document is", condition def test(): country_sample = CountrySample() country_sample.main([]) test()
Add the generated jar file to your CLASSPATH. In Linux, I can do that with the following:
$ export CLASSPATH=$CLASSPATH:./country.jar
Now run it:
$ jython test_xml.py
And, that prints out:
<xml-fragment><v1:Name xmlns:v1="http://www.openuri.org/domain/country/v1">Denmark</v1:Name><v1:Population xmlns:v1="http://www.openuri.org/domain/country/v1">5450661</v1:Population></xml-fragment> <xml-fragment> <v1:Name xmlns:v1="http://www.openuri.org/domain/country/v1">Denmark</v1:Name> <v1:Population xmlns:v1="http://www.openuri.org/domain/country/v1">5450661</v1:Population> </xml-fragment> Document is invalid <xml-fragment> <v1:Name xmlns:v1="http://www.openuri.org/domain/country/v1">Denmark</v1:Name> <v1:Population xmlns:v1="http://www.openuri.org/domain/country/v1">5450661</v1:Population> <v1:Iso xmlns:v1="http://www.openuri.org/domain/country/v1"> <v1:Alpha2>DK</v1:Alpha2> <v1:Alpha3>DNK</v1:Alpha3> <v1:CountryCode>208</v1:CountryCode> </v1:Iso> </xml-fragment> Document is valid
JDBC is Java classes. It is, therefore, usable from Jython.
You will need JDBC driver/adapters for your database.
But, JDBC is not very Pythonic.
zxJDBC is Pythonic. zxJDBC implements the Python DB API on top of JDBC. For more on the Python DB API, see SIG on Tabular Databases in Python and Python Database API Specification v2.0.
If zxJDBC is not already in your installed version of Jython, then you can:
- Downloading the source from http://sourceforge.net/projects/zxjdbc.
- Creating a directory (e.g. zxJDBC), then un-rolling it.
- Add zxJDBC/lib/zxJDBC.jar to your CLASSPATH
You can get documentation on zxJDBC by:
- Downloading the source from http://sourceforge.net/projects/zxjdbc.
- Creating a directory (e.g. zxJDBC), then un-rolling it.
- Pointing your browser at zxJDBC/doc/index.html.
Example -- The following example opens a connection to a PostgreSQL database, then prints out the rows in a table in that database. In order to make this example work, I put the following jar files on my CLASSPATH:
- zxJDBC.jar -- Not needed for Jython 2.2, and possibly not needed for the version of Jython 2.1 on your machine. JDBC support has been folded into Jython 2.1 and Jython 2.2a.
- postgresql-8.1-407.jdbc3.jar -- You will need a suitable driver for your database and version.
Here is the example implementation:
""" For this test, add the JDBC driver to your CLASSPATH. For example, in my case I added: postgresql-8.2-506.jdbc4.jar """ from com.ziclix.python.sql import zxJDBC def test(): d, u, p, v = ( "jdbc:postgresql://thrush:5432/test", # ... host, port, database "postgres", # user name "mypassword", # pass word "org.postgresql.Driver", # driver ) db = zxJDBC.connect(d, u, p, v, CHARSET='iso_1') cur = db.cursor() cur.execute('select * from plant_db') rows = cur.fetchall() s1 = '%s %s %s' % ( 'Name'.ljust(12), 'Description'.ljust(24), 'Rating'.ljust(10), ) print s1 s1 = '%s %s %s' % ( '===='.ljust(12), '==========='.ljust(24), '======'.ljust(10), ) print s1 for row in rows: rating = str(row[2]) print '%s %s %s' % ( row[0].ljust(12), row[1].ljust(24), rating.ljust(10), ) cur.close() db.close() if __name__ == '__main__': test()
Which, when connected to my trivial, little database, prints out the following:
Name Description Rating ==== =========== ====== tomato red and tasty 8 peach sweet and succulent 8 tangerine sweet but tart 7
Resources:
- More information on zxJDBC: http://jython.org/Project/userguide.html#database-connectivity-in-jython
- The JDBC driver for PostgreSQL: http://jdbc.postgresql.org/
- The JDBC driver for MySQL: http://www.mysql.com/products/connector/j/
- Python Tabular Databases SIG: Status -- http://www.python.org/community/sigs/current/db-sig/status/
- The Python Database Topic Guide -- http://www.python.org/doc/topics/database/
6 Additional Exercises
[To be added.]
7 References and Sources
Introductory articles:
- An Introduction to Jython: An introductory article on Jython
- alt.lang.jre: Get to know Jython: An introduction to Jython that includes a summary of Jython features.
- Use Jython to Exercise Java APIs Without Compiling: Another introduction with an emphasis on the use of Java classes.
- Charming Jython: Yet another introductory article.
- Scripting Languages For Java: A comparison of scripting languages for Java.
Thanks to David Goodger for the following list or references. His "Code Like a Pythonista: Idiomatic Python" (http://python.net/~goodger/projects/pycon/2007/idiomatic/) is worth a careful reading:
- "Python Objects", Fredrik Lundh, http://www.effbot.org/zone/python-objects.htm
- "How to think like a Pythonista", Mark Hammond, http://python.net/crew/mwh/hacks/objectthink.html
- "Python main() functions", Guido van Rossum, http://www.artima.com/weblogs/viewpost.jsp?thread=4829
- "Python Idioms and Efficiency", http://jaynes.colorado.edu/PythonIdioms.html
- "Python track: python idioms", http://www.cs.caltech.edu/courses/cs11/material/python/misc/python_idioms.html
- "Be Pythonic", Shalabh Chaturvedi, http://shalabh.infogami.com/Be_Pythonic2
- "Python Is Not Java", Phillip J. Eby, http://dirtsimple.org/2004/12/python-is-not-java.html
- "What is Pythonic?", Martijn Faassen, http://faassen.n--tree.net/blog/view/weblog/2005/08/06/0
- "Sorting Mini-HOWTO", Andrew Dalke, http://wiki.python.org/moin/HowTo/Sorting
- "Python Idioms", http://www.gungfu.de/facts/wiki/Main/PythonIdioms
- "Python FAQs", http://www.python.org/doc/faq/