Web Application Development Using web.py and Jython

Given the recent discusion on the Jython-dev list of getting popular CPython applications ported to run on Jython I took a look at whether it was possible to get the increasingly poplar web.py web appplication (anti)framework to work on the Jython platform. It turned out it was relatively easy to get it running, particularly with the release of Jython 2.2 on 24/08/07.

Potentially this provides a rapid and pythonic way to develop web applications, whilst providing all the advantages that a Jython/Java platform gives, and a possible alternate hosting solution for web.py based solutions. The web.py/Jython combination can either be run using either web.py's builtin web server as a quick way to develop web applications without needing the typical Java web stack, or using it's wsgi component in combination with modjy and a Java application server, in this example Tomcat.

The software used:

jython 2.2, web.py 0.22, tomcat 5.5.20, java 1.5.0_07

Some minor modifications to web.py are required as it is written for CPython 2.4 and above. The patch updates the database module so it uses zxJDBC and adds support for Oracle, but omits pooled connection support, although it should be possible to use a Java library for this if required. web.py uses flup for session functionality, I haven't looked at whether this will work on Jython as yet.

1) Patch web.py

Download web.py and copy the source directory (web) from the web.py distribution into your Jython installations Lib directory. The following changes are then required to support database connectivity on Jython 2.2:

####################

db.py:

20a21,25
> import sys
> sys.add_package('com.ziclix.python.sql')
> sys.add_package('oracle.jdbc.driver')
> sys.add_package('org.gjt.mm.mysql')
> 
250c255
< def connect(dbn, **keywords):
---
> def connect(**keywords):
257a263
>     dbn = keywords['dbn']
259,265c265
<         try: 
<             import psycopg2 as db
<         except ImportError: 
<             try: 
<                 import psycopg as db
<             except ImportError: 
<                 import pgdb as db
---
>         from com.ziclix.python.sql import zxJDBC as db
273c273
<         import MySQLdb as db
---
>         from com.ziclix.python.sql import zxJDBC as db
275c275,285
<             keywords['passwd'] = keywords['pw']
---
>             keywords['password'] = keywords['pw']
>             del keywords['pw']
>         if 'db' in keywords:
>             keywords['database'] = keywords['db']
>             del keywords['db']
>         db.paramstyle = 'qmark' # it's both, like psycopg
> 
>     elif dbn == "oracle":
>         from com.ziclix.python.sql import zxJDBC as db
>         if 'pw' in keywords:
>             keywords['password'] = keywords['pw']
277c287,290
<         db.paramstyle = 'pyformat' # it's both, like psycopg
---
>         if 'db' in keywords:
>             keywords['database'] = keywords['db']
>             del keywords['db']
>         db.paramstyle = 'numeric'
325c338,339
<                 web.ctx.db = db.connect(**keywords)
---
>                 web.ctx.db = db.connect(keywords.get('database'),keywords.get('user'), keywords.get('password'), keywords.get('driver'))
616a631,633
>     elif web.ctx.db_name == "oracle":
>         web.ctx.db_execute(db_cursor, sql_query)
>         sql_query = SQLQuery("SELECT "+seqname+".currval from dual")




####################

form.py:

22c22,23
<         o = copy.deepcopy(self)
---
>         #not sure about this ... throws error with deepcopy().
>         o = copy.copy(self)

2) Using web.py builtin web server:

Once patched you should be able to run a web.py application as follows:

webapp.py:

import web
from web import form

urls = ('/','index','/myselect','myselect')


class index:
  def GET(self):
    print "Home"

class myselect:
  def GET(self):
    tst = web.select('user',what='*',where="nid=4")
    for ts in tst:
      print ts[('name')]



web.config.db_parameters = dict(dbn='mysql',user='me',pw='you',db='jdbc:mysql://localhost/db',driver='org.gjt.mm.mysql.Driver')

if __name__ == "__main__":web.run(urls, globals())

To run:sh> jython webapp.py 80

3) Using wsgi, modjy and tomcat.

Follow the instructions at http://www.xhaus.com/modjy to install and configure modjy in your Tomcat installation, ensure you modify the web.xml python.home attribute to point correctly at your local Jython installation, and create the test web application (modjy_webapp) as per instructions. I had to put the jython files in the modjy distribution in my local Jython installs Lib directory and modify modjy_impl.py as there is a bug, as per http://henkenotes.blogspot.com/search/label/jython. Thanks to Henke Eriksson for this.

Modify your web.py application as below and place it in the root of the modjy_webapp context (webapps/modjy_webapp/).

import web

urls = ('/','index','/myselect','myselect')


class index:
  def GET(self):
    print "Home"

class myselect:
  def GET(self):
    tst = web.select('user',what='*',where="nid=4")
    for ts in tst:
      print ts[('name')]


web.config.db_parameters = dict(dbn='mysql',user='me',pw='you',db='jdbc:mysql://localhost/db',driver='org.gjt.mm.mysql.Driver')

###Replace orig main line with wsgi handler:

handler = web.wsgifunc(web.webpyfunc(urls, globals()))

You should now be able to access your web.py application using Tomcat, e.g. http://localhost:8080/modjy_webapp/myselect. I've also noticed a performance improvement when using the first full release of Jython 2.2.

Cheers Colin