Jython and PyXML

S.Prasanna,
sprasanna199@gmail.com

Here I will discuss my experience with PyXML and Jython. I tried to make the best use of Jython with the latest PyXML module. While working with PyXML is a fairly easy task in Python, making things things to work with the latest PyXML in Jython is bit tricky.

Below is a simple code, which gets the servlets and their mappings from the Web.xml file in Tomcat.

Listing 1: XMLParse.py

  1  #XMLParse.py
  2  from xml.dom import minidom
  3
  4 
#Open the parser
  5  doc = minidom.parse(open("Web.xml" , "r"))
  6
  7  mapping = doc.getElementsByTagName('servlet-mapping')
  8  servlet = doc.getElementsByTagName('servlet')
  9
10  #Get the tag
11  dictservlet = {}
12  dictmapping = {}
13 
#Get all servlet names and associated class
14  for a in servlet:
15    b = a.getElementsByTagName ('servlet-name')
16    c = a.getElementsByTagName ('servlet-class')
17    for k in b:
18      for h in c:
19        dictservlet [k.firstChild.data] = h.firstChild.data
20
21  #Get all servlet names and associated URL patterns
22  for a in mapping:
23    b = a.getElementsByTagName('servlet-name')
24    c = a.getElementsByTagName ('url-pattern')
25    for k in b:
26      for h in c:
27        dictmapping [h.firstChild.data] = k.firstChild.data
28
29  print "\n\n The dynamic servlet mappings are "
30  print "\n\nServlets are "
31  for i in dictservlet.keys(): print i ," = ", dictservlet[i]
32  print "\n\nServlet mappings are "
33  for i in dictmapping.keys(): print i ," = ", dictmapping[i]

When executing this code in jython (without PyXML), an exception was reported as shown below.

C:\>Jython XMLParse.py

Traceback (innermost last):
File "XMLParse.py", line 4, in ?
File "C:\jython-2.1\Lib\xml\dom\minidom.py", line 908, in parse
File "C:\jython-2.1\Lib\xml\dom\minidom.py", line 900, in _doparse
File "C:\jython-2.1\Lib\xml\dom\pulldom.py", line 251, in getEvent
AttributeError: feed

The exception thrown is due to the module pulldom.py. In this case the pulldom.py module from the latest stable Python version (2.3) is replaced in the Jython library module and then executed. (i.e replace the < PYTHON_HOME>/lib/xml/dom/pulldom.py in <JYTHON_HOME>/lib/xml/dom/pulldom.py) . This solved the problem and the above code worked.

The other thing which I experimented is to make the latest PyXML module to work with Jython in executing the above code. To do this, download the PyXML source and from the PyXML source copy the contents of the xml directory and replace it with that of the Jython 2.1 xml module.

C:\>jython xmlparse.py

Traceback (innermost last):
File "xmlparse.py", line 4, in ?
File "C:\jython-2.1\Lib\xml\dom\minidom.py", line 1907, in parse
ImportError: cannot import name expatbuilder

Since expat parser is not available in jython, the line, which throws the exception in minidom.py, the code in that line in minidom.py should be changed.

def parse(file, parser=None, bufsize=None):
    """Parse a file into a DOM by filename or file object."""
    if parser is None and not bufsize:
        from xml.dom import expatbuilder
        return expatbuilder.parse(file)
    else:
         from xml.dom import pulldom
        return _do_pulldom_parse(pulldom.parse, (file,),
            {'parser': parser, 'bufsize': bufsize})

def parseString(string,parser=None):
    """Parse a file into a DOM from a string."""
    if parser is None:
        from xml.dom import expatbuilder
        return expatbuilder.parseString(string)
    else:
        from xml.dom import pulldom
        return _do_pulldom_parse(pulldom.parseString, (string,),
                                  {'parser': parser})

Change it to

def parse(file, parser=None, bufsize=None):
    """Parse a file into a DOM by filename or file object."""
    if parser is None and not bufsize:
        from xml.dom import pulldom
        return _do_pulldom_parse(pulldom.parse, (file,),
            {'parser': parser, 'bufsize': bufsize})

def parseString(string, parser=None):
    """Parse a file into a DOM from a string."""
    if parser is None:
        from xml.dom import pulldom
        return _do_pulldom_parse(pulldom.parseString, (string,),
                                   {'parser': parser})

After making this modification, the subsequent exception thrown was

C:\>jython xmlparse.py

Traceback (innermost last):
File "xmlparse.py", line 4, in ?
File "C:\jython-2.1\Lib\xml\dom\minidom.py", line 1908, in parse
File "C:\jython-2.1\Lib\xml\dom\minidom.py", line 1898, in _do_pulldom_parse
File "C:\jython-2.1\Lib\xml\dom\pulldom.py", line 338, in parse
File "C:\jython-2.1\Lib\xml\sax\sax2exts.py", line 37, in make_parser
File "C:\jython-2.1\Lib\xml\sax\saxexts.py", line 69, in make_parser
File "C:\jython-2.1\Lib\xml\sax\saxexts.py", line 37, in _create_parser
File "C:\jython-2.1\Lib\xml\sax\drivers2\drv_xmlproc.py", line 9, in ?
ImportError: cannot import name xmlproc

After a careful analysis of the PyXML module and the python module it uses, it was found that the cause of the exception was due to the absence of some python (2.3) modules in jython.

Now I used the python 2.3 modules

Urlib2.py
Dis.py
Inspect.py
Opcode.py

In the Jython library and finally the code executed in Jython!

Note: While replacing the inspect.py module from Python 2.3 to Jython, make sure that you replace the // operator in inspect.py module since jython 2.1 does not support // operator. Instead modify that operator in inspect.py as follows.

start = lineno - 1 - context//2

to

start = line - 1 - int(math.floor(context/2))

In short, many python libraries can be made to work with Jython with little tweaking, to take the full advantage of Java Implementation of python.