javaclass
Current release: javaclass 0.2
See also: javaclass Mercurial repository
Rationale
The javaclass collection of packages and utilities (also known as
ClassFile) provides a means of importing Java classes and packages directly
into Python, without the need for a Java virtual machine, so that the classes
may be instantiated, accessed, run and manipulated just like Python classes,
and that the resulting objects and methods can be accessed and manipulated
just like Python objects and methods. It should be possible to run compiled
Java programs with the Python interpreter and not notice that it isn't the
Java virtual machine being used - given sufficient library support for the
program concerned, of course.
Quick Examples
It can be quicker to see what this is about by seeing some examples.
The Not Very Convincing Example
You can run Java classes by finding one with a main method and executing
it. Here's a comparison of a freshly prepared Java class being run in Python
and in a Java virtual machine respectively:
cd tests/
javac Value.java
runclass.py Value
v.getValue() correct: 123
v.getValue() correct: 456
v.isPositive() correct: 1
v.isPositive() correct: 0
v.compare(-790) correct: -1
v.compare(-788) correct: 1
v.compare(-789) correct: 0
v.getValue() == v2.getValue() correct: 0
v2.add(-123) correct: 0
v2.getValue() correct: 255
java Value
v.getValue() correct: 123
v.getValue() correct: 456
v.isPositive() correct: true
v.isPositive() correct: false
v.compare(-790) correct: -1
v.compare(-788) correct: 1
v.compare(-789) correct: 0
v.getValue() == v2.getValue() correct: false
v2.add(-123) correct: 0
v2.getValue() correct: 255
The Slightly More Credible Example
It can be more interesting to get into Python's interactive mode and then
start playing around with Java classes:
Python 2.2.2 (#2, Jan 21 2005, 16:16:57)
[GCC 2.96 20000731 (Red Hat Linux 7.3 2.96-112)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
import javaclass.classhook
from __this__ import Value
dir()
['Value', '__builtins__', '__doc__', '__name__',
'javaclass']
dir(Value)
['__class__', '__delattr__', '__dict__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__init______I_', '__module__',
'__new__', '__reduce__', '__repr__', '__setattr__', '__str__', '__weakref__',
'add', 'add____I_', 'compare', 'compare____I_', 'getClass', 'getClass___',
'getValue', 'getValue___', 'isPositive', 'isPositive___', 'main',
'main___java__lang__String_array_', 'newValue', 'newValue___', 'setValue',
'setValue____I_']
v = Value(20050121)
v.getValue()
20050121
v.setValue(20050401)
v.getValue()
20050401
Copyright and Licence
The javaclass software is distributed under the terms of the GNU Lesser
General Public Licence (LGPL). See the file COPYING.txt
in the
docs
directory within the source code distribution.
Getting Started
See the README.txt
file in the distribution directory.
Motivation
Pick one of the following:
- The need/desire to access Java libraries from Python without firing up
Java virtual machines or switching to Jython (and thereby losing
convenient access to various CPython libraries).
- Mixing languages available for the Java runtime with Python.
- Static typing for the Python environment, albeit achieved by writing
Java or other appropriate languages.
- Having an open source environment from top to bottom to run Java
bytecode on.
- Experimentation around import hooks, bytecode generation; observation
of different runtime and type systems interacting.
- Making Python libraries available to Java programs - Tkinter for Java,
anyone?!
Limitations
It isn't all great, however. Here are some reasons why this might not do
what you want it to:
- It runs on the Python runtime which does not have the security,
threading and just-in-time compiler features that people enjoy about Java
runtimes, so if what you like to do is to run one big servlet container
with lots of classes and threads from different people floating around,
this isn't going to protect them from doing all sorts of things to each
other and to your system. However, you may take the unfashionable view
that the operating system is supposed to do that kind of thing.
- It works by generating Python bytecode from the Java bytecode in class
files (and .jar archives). Generally, anyone who is anyone in the Python
pantheon has largely recommended against doing anything with the
bytecode, despite noble efforts to make exciting things happen by
transforming it, optimising it, and so on. (Instead, there's been more
emphasis on lots of runtime baggage for things which could be done by
analysis of the code with modified bytecode being produced as a result,
and let's not get started on some of the syntax enhancements!)
Consequently, stability might be an issue for some configurations,
especially since CPython doesn't fail particularly graciously with badly
behaved bytecode.
- Some of the translation from Java to Python bytecode takes a few
liberties. For example, Java exceptions are handled in ways reminiscent
of a 1980s microcomputer assembly language, whereas Python bytecode has
higher level constructs for exceptions; this translation can probably be
done incorrectly, triggering some kind of bytecode misbehaviour, and we
all know what happens then.
- At the Python level, not all things seem totally right. For example,
Java bytecode instructions are used to instantiate and then initialise
exceptions just like other objects, and while Python can support this
with new-style objects, Python won't let you use new-style objects as
exceptions. Consequently, when Java exceptions appear in Python programs,
they will be wrapped in old-style exceptions and have to be handled
specially.
- In order to support method dispatch properly, special names are used
for the translated methods which include the type information found in
the bytecode; things like
main___java__lang__String_array_
and setValue____I_
appear when you look inside classes and
objects. When implementing libraries in Python for Java programs, such
method naming conventions have to be used because the original code is
very specific about which method is being invoked, and for specialised
versions of __init__
it becomes necessary to do a
setattr
to add such methods into classes because of various
"name mangling" measures in the Python compilation process. Now, many
people might start advocating decorators at this point, but not everyone
is running the very latest stuff from python.org, and decorators won't
help you target a specific specialised method anyway.
- Special dispatcher methods are often generated for the benefit of
Python access to Java classes, even though such methods are not strictly
necessary for the Java classes to work amongst themselves. Such methods
are only generated when many methods of the same name reside in a given
class, since where Java distinguishes between them on the basis of the
signatures, Python permits only one method of a given name and needs
additional logic to dispatch to the actual method implementations on the
basis of the types of the incoming values. The implementation of the
dispatcher method is naive and does not try to order the type checks and
dispatches according to the specificity of the parameter types; thus, a
more reliable but more verbose way of ensuring that the correct method in
such cases is called from a Python program may be to use the special long
method name (eg.
setValue____I_
).
- Imported and translated bytecode is not written out or cached. This
means that a fair amount of work happens every time you need to import
Java classes, although the generation of .pyc files could be introduced
provided that it captured the slightly different import semantics of
Java; for example, you can import classes belonging to a package from
several places on the PYTHONPATH, and this is something that generally
isn't allowed/supported with the classic Python module import
mechanisms.
- Most Java programs need standard Java library (also known as the Java
API) classes; indeed, with
java.lang.String
and seemingly
java.lang.StringBuffer
required just for the usage of
strings, and with the classic main
method having
String[]
in its signature, some kind of library
implementation is obviously necessary. Whilst importing everything from
the .jar files bundled with Sun's JRE might be tempting, there may be a
certain amount of native calling going on in secret which would defeat
this approach, and anyone seriously interested in running Java code in
Python should really want to steer clear of such proprietary dependencies
anyway. It could be possible to wrap and make use of GNU Classpath or
some other open source Java API implementation, but the cleanest (but not
necessarily the least time-consuming) approach is surely to implement the
standard API classes in Python, and a package called java
is
included which contains some fairly quick and dirty implementations to
get things working.
Suggestions for Python Improvements
- Make the bytecode interpreter more robust when encountering badly
behaved bytecode, consider bytecode verification and other exciting
features.
- Allow new-style objects as exceptions. Actually, just do something
about the whole old-style vs. new-style class thing!
- Allow possible optimisation for things like statically predetermined
method calls. The debate about static typing intersects slightly with the
need to define methods with parameters of particular types, but
supporting Java-style method "coexistence" (or multimethods, or whatever
the proper name is) would presumably involve doing lots of things to the
language that were once thought to be highly un-Pythonic. Besides, the
need to interoperate with statically typed languages shouldn't dictate
the design of Python, especially if better type systems could be adopted
instead, leaving the static typing glue for strictly special
occasions.
- Not that threaded Java programs have been run on Python yet, but with
Python's current threading architecture I'd expect some
disappointment.