Saturday, December 30, 2006

Handling Method Arguments in Jython

I have been away from Jython for a while over the Christmas period but now I am back and eager to make some progress porting the csv module. So where was I? Oh yes, last time I managed to get into a position where test_csv.py ran successfully (success meaning that the test script executed - not that the tests passed!), so now I can start trying to get a test to pass.

I have decided to tackle test_reader_arg_valid1() test first simply because it's the first test in test_csv.py:


def test_reader_arg_valid1(self):
self.assertRaises(TypeError, csv.reader)
self.assertRaises(TypeError, csv.reader, None)
self.assertRaises(AttributeError, csv.reader, [], bad_attr = 0)
self.assertRaises(csv.Error, csv.reader, [], 'foo')
class BadClass:
def __init__(self):
raise IOError
self.assertRaises(IOError, csv.reader, [], BadClass)
self.assertRaises(TypeError, csv.reader, [], None)
class BadDialect:
bad_attr = 0
self.assertRaises(AttributeError, csv.reader, [], BadDialect)

As you can see, a series of tests are being performed on the csv.reader() method so I need to concentrate on implementing just enough of it to get the test to pass.

From the python documentation, csv.reader() is defined as follows:

reader(csvfile[, dialect='excel'[, fmtparam]])

csvfile is the only required argument and it can be any object that supports the iterator protocol. Next, the dialect name can be specified as an optional parameter or omitted (in which case, the dialect will default to excel). The other optional fmtparam keyword arguments can be given to override individual formatting parameters in the current dialect.

So csv.reader() has it all - mandatory, optional and keyword arguments. I am going to need to figure out how this works in Jython to pass the test_reader_arg_valid1() test.

In Jython three types of method are supported:

  • StandardCall: Mandatory, Positional arguments. (i.e. void method(PyObject arg1, PyObject arg2) {} )

  • PyArgsCall: List of optional, positional arguments. (i.e. void method(PyObject[] args))

  • PyArgsKeywordsCall: List of optional, positional or keyword arguments. (i.e. void method(PyObject[] args, String[] keywords))


As csv.reader() must support keyword arguments, it must be defined as follows:

public static void reader(PyObject[] args, String[] keywords) {
}

The parameter list must be specified exactly like this (except for the identifier names which can differ) because Jython uses reflection to make a method of type PyArgsKeywordsCall only if it has exactly one PyObject array as the first argument and one String array as the second argument. If you add another argument to the beginning of the parameter list, then the method will automatically be of StandardCall type and won't support keyword arguments.

I can use the handy helper class, ArgParser to parse the arguments and extract the relevant values. First, I need to create an instance of ArgParser as follows:

public static PyObject reader(PyObject[] args, String[] keywords) {

ArgParser ap = new ArgParser(
"reader",
args,
keywords,
new String[] {
"csvfile", "dialect", "delimiter",
"doublequote", "escapechar", "lineterminator",
"quotechar", "quoting", "skipinitialspace"
});
//..
}

args and keywords are passed into ArgParser along with a list of the names of each argument that the method supports. Then it is possible to simply pick out the value of a parameter by invoking a getXXX() method specifying the position of the argument. So to get the value of "quotechar", you'd ask for the value at position 6 as follows:

String quotechar = ap.getString(6, "'");

Simple, eh? It is equally as easy to support optional arguments. For example, the dialect argument would be extracted as follows:

String dialect = ap.getString(1, "excel");

Here, if dialect is not specified then it will default to "excel".

Now that I have learned how to support positional, optional and keyword arguments in Jython I can focus on type checking the arguments and throwing the appropriate exceptions in order to pass test_reader_arg_valid1().

1 comment:

Lars said...

Great to see that this module has been worked on. I too would like to be able to use csv in jython.
Any progress lately, or is this a dead effort?

Lars