|
From: <bc...@wo...> - 2001-02-19 10:25:27
|
[brian]
>I have implemented a few extension modules in Java to be used in the Jython
>runtime. I have a good feel for most of the requirements of doing such
>except for the area of exceptions. I have seen a couple different ways to
>create and raise exceptions and I wanted to know if there was a standard, or
>least best practices approach.
>
>I've noticed that Py.java and Finn's cPickle module have written the
>exception class hierarchy in Python (as opposed to Java) and then used
>jpythonc to produce the classes so they would be available in Java. I've
>never felt comfortable checking in generated source.
I didn't feel good about it either. It was a hack needed to get
Jython-2.0 finished. At the time I had spend way too much time trying to
get jython exceptions right, and I needed something that just plain
worked.
>On the other hand, some modules, mine included, have used a static PyString
>and use PyException's constructor to make the exception. This seems weak to
>me as I'm not really subclassing the exception, just giving it a type. I'd
>rather have a more OO feel by subclassing the exception, but I haven't seen
>this done anywhere except for PySyntaxError.
>
>I'm opened to any ideas. I am partial to subclassing PyException as this
>seems the most correct, but since so few examples exist of such being done
>I'd like to find out why? I think the Py.makeException() methods would be
>useful but they don't allow the specification of a superclass.
>
>Anyways, sorry to ramble, but I've been confused about exceptions in Jython
>for some time.
So have I. In errata-06 I coded all the standard exceptions as java
classes. The base Exception then became:
public static class Exception extends PyObject {
public static String __doc__ =
"Base class for all standard Python exceptions.";
public PyObject[] args;
public Exception(PyObject[] args) {
this.args = args;
}
public PyString __str__() {
switch (args.length) {
case 0:
return Py.newString("");
case 1:
return args[0].__str__();
default:
return (new PyTuple(args)).__str__();
}
}
public PyObject __getitem__(int i) {
return args[i];
}
public String toString() {
return __str__().toString();
}
}
which is a straightforward conversion from the python exceptions.py
module. Making application specific subclasses from java is equally
straightforward. The cPickle.java source still contains examples of this
as comments.
However this had some drawbacks because exceptions.Exception is then a
python type:
1. types can not control how the class is printed, only how the
instances are printed. As a result the exception name was written
as "org.python.core.NameError".
2. we can not make multiple inheritance of types.
Both these drawbacks are avoided when the exceptions.Exception is a
python class. Bases on this experience from errata-06 I decided it was
better when exceptions.Exception is a python class.
The drawbacks of this decision is the total lack of an java API with
which to create python classes. I'm working on a way to make python
classes from within java a little easier. The details are not finalized
yet, but the general mechanism is that the PyClass is created with:
buildClass(dict, "EnvironmentError", "StandardError",
"EnvironmentError",
"Base class for I/O related errors.");
where the arguments are
- module dict
- classname
- superclassname
- name of class code method
- doc string
The "name of class code method" must be a java method which create and
populate the class dict:
public static PyObject EnvironmentError() {
PyObject dict = passCode(null);
dict.__setitem__("__init__",
getJavaFunc("EnvironmentError__init__"));
dict.__setitem__("__str__",
getJavaFunc("EnvironmentError__str__"));
return dict;
}
and the class functions are java method that implements the desired
functionally:
public static void EnvironmentError__init__(PyObject[] arg) {
ArgParser ap = new ArgParser("search", arg, null,
"self", "args");
PyObject self = ap.getPyObject(0);
PyObject args = ap.getList(1);
self.__setattr__("args", args);
self.__setattr__("errno", Py.None);
self.__setattr__("strerror", Py.None);
self.__setattr__("filename", Py.None);
if (args.__len__() == 3) {
// open() errors give third argument which is the filename.
// BUT, so common in-place unpacking doesn't break, e.g.:
//
// except IOError, (errno, strerror):
//
// we hack args so that it only contains two items. This
// also means we need our own __str__() which prints out the
// filename when it was supplied.
PyObject[] tmp = Py.unpackSequence(args, 3);
self.__setattr__("errno", tmp[0]);
self.__setattr__("strerror", tmp[1]);
self.__setattr__("filename", tmp[2]);
self.__setattr__("args",
args.__getslice__(Py.Zero, Py.newInteger(2
}
if (args.__len__() == 2) {
// common case: PyErr_SetFromErrno()
PyObject[] tmp = Py.unpackSequence(args, 2);
self.__setattr__("errno", tmp[0]);
self.__setattr__("strerror", tmp[1]);
}
}
Again this java code is a 1-to-1 match with the python code from
exceptions.py. We can then avoid having generated code in the codebase.
This kind of API support is about the same that CPython have for this
task.
Still, I agree that it is a lot of complicated code when all a module
writer want to do is:
class MyExc(Exception):
def __str__(self):
return ...
regards,
finn
|