Differences between revisions 6 and 13 (spanning 7 versions)
Revision 6 as of 2006-12-31 22:38:08
Size: 10504
Editor: 181
Comment:
Revision 13 as of 2008-11-15 09:15:57
Size: 5796
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
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 and most of the basic functionality working: db connectivity, form.py and template.py. 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. 
Line 7: Line 7:
The software used in my examples are: The software used:
Line 9: Line 9:
jython 2.2.a1,
web.py 0.2,
jython 2.2,
web.py 0.22,
Line 12: Line 12:
java 1.4.2 java 1.5.0_07
Line 14: Line 14:
Some minor modifications to web.py are required as it is written for CPython 2.4 and above, and due to some missing Jython functionality, which I document below, note the way I have implemented the missing Jython dict() functionality could be improved... 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. 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.
Line 18: Line 18:
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 get it to run on Jython: 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:
Line 26: Line 26:
5a6,7
> from __future__ import generators
>
19a22,27
> #needed to import external packages in jython, also need to be on classpath
20a21,25
Line 36: Line 32:
24c32
< from utils import storage, iters, iterbetter
---
> from utils import storage, iters, iterbetter, fdict
162d169
< >>> sqlify(None)
234c241
250c255
Line 45: Line 35:
> def connect(keywords):
241a249
> def connect(**keywords):
257a263
Line 48: Line 38:
244c252 259,265c265
< try:
Line 50: Line 41:
< except ImportError:
< try:
< import psycopg as db
< except ImportError:
< import pgdb as db
Line 51: Line 47:
>     from com.ziclix.python.sql import zxJDBC as db
247c255
<         import psycopg as db
> from com.ziclix.python.sql import zxJDBC as db
273c273
< import MySQLdb as db
Line 55: Line 51:
>         from com.ziclix.python.sql import zxJDBC as db
253,254c261,263
< keywords['database'] = keywords['db']
< del keywords['db']
> from com.ziclix.python.sql import zxJDBC as db
275c275,285
<     keywords['passwd'] = keywords['pw']
Line 60: Line 55:
> keywords['password'] = keywords['pw']
> del keywords['pw']
Line 63: Line 60:
257c266
< import MySQLdb as db
---
> from com.ziclix.python.sql import zxJDBC as db
259c268
< keywords['passwd'] = keywords['pw']
---
> keywords['password'] = keywords['pw']
261c270,284
< db.paramstyle = 'pyformat' # it's both, like psycopg
---
> if 'db' in keywords:
> keywords['database'] = keywords['db']
> del keywords['db']
> #db.paramstyle = 'pyformat' # it's both, like psycopg
Line 84: Line 66:
> del keywords['pw']
> if 'db' in keywords:
> keywords['database'] = keywords['db']
> del keywords['db']
> db.paramstyle = 'numeric'
265c288
< import sqlite3 as db
---
> from com.ziclix.python.sql import zxJDBC as db
273,275c296,298
< web.config._hasPooling = False
< keywords['database'] = keywords['db']
< del keywords['db']
277c287,290
< db.paramstyle = 'pyformat' # it's both, like psycopg
Line 101: Line 72:
278c301
< import kinterbasdb as db
---
> from com.ziclix.python.sql import zxJDBC as db
280c303
< keywords['passwd'] = keywords['pw']
---
> keywords['password'] = keywords['pw']
282,283c305,307
< keywords['database'] = keywords['db']
< del keywords['db']
---
> if 'db' in keywords:
> keywords['database'] = keywords['db']
> del keywords['db']
294,301c318,319
< if isinstance(web.ctx.db, dict):
< keywords = web.ctx.db
< if web.config._hasPooling:
< if 'db' not in globals():
< globals()['db'] = PooledDB(dbapi=db, **keywords)
< web.ctx.db = globals()['db'].connection()
< else:
> db.paramstyle = 'numeric'
325c338,339
Line 126: Line 76:
> keywords = web.ctx.db
> web.ctx.db = db.connect(keywords.get('database'),keywords.get('user'), keywords.get('password'), keywords.get('driver'))
302a321
>
373c392
< yield storage(dict(zip(names, row)))
---
> yield storage(fdict(zip(names, row)))
378c397
< out.list = lambda: [storage(dict(zip(names, x))) \
---
> out.list = lambda: [storage(fdict(zip(names, x))) \
545a565,567
> web.ctx.db = db.connect(keywords.get('database'),keywords.get('user'), keywords.get('password'), keywords.get('driver'))
616a631,633
Line 140: Line 79:
>   web.ctx.db_execute(db_cursor, sql_query) >   web.ctx.db_execute(db_cursor, sql_query)
Line 142: Line 81:
624a647
>
Line 146: Line 83:
####################

debugerror.py:

16a17
> from utils import fdict
20c21,27
< whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1])
---
> #whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1])
> #sep attribute doesn't seem to exist in jython, hence:
> if os.name == 'nt':
> whereami = '\\'.join(whereami.split('\\')[:-1])
> else:
> whereami = '/'.join(whereami.split('/')[:-1])
>
201c208
< $:dicttable(dict(newctx))
---
> $:dicttable(fdict(newctx))
Line 175: Line 92:
> #not sure about this ... template.py throws error with deepcopy(). > #not sure about this ... throws error with deepcopy().
Line 178: Line 95:
####################

template.py:

8c8
< from utils import storage, group
---
> from utils import storage, group, fdict
585c585
< kw = dict([(x, self.h(y)) for (x, y) in i[KWARGS]])
---
> kw = fdict([(x, self.h(y)) for (x, y) in i[KWARGS]])

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

utils.py:

5a6,7
> from __future__ import generators
>
145,148c147,150
< iters.__doc__ = """
< A list of iterable items (like lists, but not strings). Includes whichever
< of lists, tuples, sets, and Sets are available in this version of Python.
< """
---
> #iters.__doc__ = """
> #A list of iterable items (like lists, but not strings). Includes whichever
> #of lists, tuples, sets, and Sets are available in this version of Python.
> #"""
355c357
< return dict([(value, key) for (key, value) in mapping.iteritems()])
---
> return fdict([(value, key) for (key, value) in mapping.iteritems()])
528c530,531
< return ''.join(c for c in str(string).split('.')[0] if c.isdigit())
---
> return ''.join([c for c in str(string).split('.')[0] if c.isdigit()])
711c714,715
< for (key, value) in locals.iteritems():
---
> #for (key, value) in locals.iteritems():
> for (key, value) in locals.items():
759a764,774
> def fdict(seq):
> result = {}
> for key,value in seq:
> result[key] = value
> return result
>
> def dbdict(**seq):
> result = {}
> for key,value in seq.items():
> result[key] = value
> return result

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

webapi.py:

5a6,7
> from __future__ import generators
>
20c22
< from utils import storage, storify, threadeddict, dictadd, intget, lstrips
---
> from utils import storage, storify, threadeddict, dictadd, intget, lstrips, fdict
93c95
< def dictify(fs): return dict([(k, fs[k]) for k in fs.keys()])
---
> def dictify(fs): return fdict([(k, fs[k]) for k in fs.keys()])
252c254,255
< db.connect(**config.db_parameters)
---
> db.connect(config.db_parameters)
Line 283: Line 125:
#notice using dbdict() rather than dict() for db connections
Line 285: Line 126:
web.config.db_parameters = web.utils.dbdict(dbn='mysql',user='me',pw='you',db='jdbc:mysql://localhost/db',driver='org.gjt.mm.mysql.Driver') web.config.db_parameters = dict(dbn='mysql',user='me',pw='you',db='jdbc:mysql://localhost/db',driver='org.gjt.mm.mysql.Driver')
Line 306: Line 147:
from web import form
Line 322: Line 162:
web.config.db_parameters = web.utils.dbdict(dbn='mysql',user='me',pw='you',db='jdbc:mysql://localhost/db',driver='org.gjt.mm.mysql.Driver') web.config.db_parameters = dict(dbn='mysql',user='me',pw='you',db='jdbc:mysql://localhost/db',driver='org.gjt.mm.mysql.Driver')
Line 330: Line 170:
You should now be able to access your web.py application using Tomcat, e.g. http://localhost:8080/modjy_webapp/myselect. 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.

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

JythonMonthly/Articles/January2007/1 (last edited 2008-11-15 09:15:57 by localhost)