Jython 2.5.2 has been released, get it while it's hot!
The new SQLAlchemy 0.6 is out with official support for Jython and Python 3, among various other features.
Last year the Django community adopted the Django Pony as their unofficial mascot. Since then, I keep hearing a complaint about Pylons: that it lacks a pony.
As of the 0.9.7 release, Pylons now supports Jython 2.5. The new snakefight tool can create a WAR file from a Pylons app via its bdist_war distutils command.
Using bdist_war is simple: just follow these instructions on the Pylons official docs: Pylons on Jython.
The WAR file contains everything you'll need: the Jython runtime and all the eggs your app requires. For Paste style apps, it can automatically generate a deployment descriptor (web.xml) to load the application via modjy (which is now included with Jython as of the beta2 release).
This makes deployment to a J2EE App server incredibly easy, even easier than deploying your app behind Apache or the like.
I'll be giving a talk on Pylons on Jython at PyCon '09 in a couple weeks.
After a lengthy release cycle (5 betas, 6 release candidates) the new Pylons 0.9.7 release is finished!
This release is huge:
o Optional SQLAlchemy and Genshi or Jinja2 integration for new projects, out of the box
o Support for Jython 2.5 (and support for Google App Engine with appengine-monkey, and even some success on PyPy)
o New extensive documentation, as well as the Pylons book (which is also available online)
o Now uses the awesome WebOb library
o New PylonsHQ website, powered by CouchDB
and a whole slew of new features and fixes. See the official release announcement for more details.
Sébastien Boisgérault's jython-elementtree project was incorporated into the recent Jython 2.5b1 release.
It doesn't implement any actual elementtree code, but instead provides a partial implementation of the pyexpat module (which the pure Python elementtree is built upon) in a clever 600 lines of Python code.
This provides full support for elementtree but also improves compatibility with code using pyexpat. For example, the standard lib's xmlrpclib module takes advantage of pyexpat for faster XML parsing if the module is provided. Jython may never fully support every pyexpat feature, but this is a great step towards supporting libraries like Genshi, among others.
Unfortunately jython-elementtree requires the latest Xerces: an additional 1mb jar. Xerces also seems to have also introduced some classpath issues, which is just about as cliche as you can get in Java.
The 2.5b1 release also includes an overhaul of Java Integration. It's now based on new style classes, which is very important going forward.
Debugging the Jython compiler (or most compilers for that matter) isn't the most straightforward process, so here I'll describe the typical steps I go through to figure it out.
The Problem
I've found a bug while trying out the SymPy project:
(jython)pjenvey@golgo13:~/src/python/sympy$ jython -c "import sympy"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "sympy/__init__.py", line 18, in <module>
from polys import *
File "sympy/polys/__init__.py", line 4, in <module>
from polynomial import Poly, PolynomialError, SymbolsError, \
java.lang.VerifyError: (class: sympy/polys/polynomial$py, method: __mul__$52 signature: (Lorg/python/core/PyFrame;)Lorg/python/core/PyObject;) Accessing value from uninitialized register 9
q[sum([ x*y for x, y in zip(monom, degs) ])] = coeff
a[[b for b, c in d]] = e
Compiled from "bad2.py"Tangent
public class bad2$py extends org.python.core.PyFunctionTable implements org.python.core.PyRunnable{
static final bad2$py self;
static final org.python.core.PyString _0;
static final org.python.core.PyCode f$0;
public org.python.core.PyObject f$0(org.python.core.PyFrame);
Code:
0: aload_1
1: ldc #4; //String __file__
3: getstatic #10; //Field _0:Lorg/python/core/PyString;
6: invokevirtual #16; //Method org/python/core/PyFrame.setglobal:(Ljava/lang/String;Lorg/python/core/PyObject;)V
9: aload_1
10: iconst_1
11: invokevirtual #20; //Method org/python/core/PyFrame.setline:(I)V
14: aload_1
15: ldc #22; //String e
17: invokevirtual #26; //Method org/python/core/PyFrame.getname:(Ljava/lang/String;)Lorg/python/core/PyObject;
20: astore_2
21: aload_1
22: ldc #28; //String a
24: invokevirtual #26; //Method org/python/core/PyFrame.getname:(Ljava/lang/String;)Lorg/python/core/PyObject;
27: new #30; //class org/python/core/PyList
30: dup
31: invokespecial #34; //Method org/python/core/PyList."":()V
34: dup
35: ldc #36; //String append
37: invokevirtual #41; //Method org/python/core/PyObject.__getattr__:(Ljava/lang/String;)Lorg/python/core/PyObject;
40: astore_3
41: aload_1
42: ldc #43; //String _[1_3]
44: aload_3
45: invokevirtual #46; //Method org/python/core/PyFrame.setlocal:(Ljava/lang/String;Lorg/python/core/PyObject;)V
48: aconst_null
49: astore_3
50: aload_1
51: iconst_1
52: invokevirtual #20; //Method org/python/core/PyFrame.setline:(I)V
55: aload_1
56: ldc #48; //String d
58: invokevirtual #26; //Method org/python/core/PyFrame.getname:(Ljava/lang/String;)Lorg/python/core/PyObject;
61: invokevirtual #52; //Method org/python/core/PyObject.__iter__:()Lorg/python/core/PyObject;
64: astore_3
65: goto 131
68: aload 4
70: iconst_2
71: invokestatic #58; //Method org/python/core/Py.unpackSequence:(Lorg/python/core/PyObject;I)[Lorg/python/core/PyObject;
74: astore 5
76: aload 5
78: iconst_0
79: aaload
80: astore 6
82: aload_1
83: ldc #60; //String b
85: aload 6
87: invokevirtual #46; //Method org/python/core/PyFrame.setlocal:(Ljava/lang/String;Lorg/python/core/PyObject;)V
90: aconst_null
91: astore 6
93: aload 5
95: iconst_1
96: aaload
97: astore 6
99: aload_1
100: ldc #62; //String c
102: aload 6
104: invokevirtual #46; //Method org/python/core/PyFrame.setlocal:(Ljava/lang/String;Lorg/python/core/PyObject;)V
107: aconst_null
108: astore 6
110: aload_1
111: iconst_1
112: invokevirtual #20; //Method org/python/core/PyFrame.setline:(I)V
115: aload_1
116: ldc #43; //String _[1_3]
118: invokevirtual #26; //Method org/python/core/PyFrame.getname:(Ljava/lang/String;)Lorg/python/core/PyObject;
121: aload_1
122: ldc #60; //String b
124: invokevirtual #26; //Method org/python/core/PyFrame.getname:(Ljava/lang/String;)Lorg/python/core/PyObject;
127: invokevirtual #66; //Method org/python/core/PyObject.__call__:(Lorg/python/core/PyObject;)Lorg/python/core/PyObject;
130: pop
131: aload_1
132: iconst_1
133: invokevirtual #20; //Method org/python/core/PyFrame.setline:(I)V
136: aload_3
137: invokevirtual #69; //Method org/python/core/PyObject.__iternext__:()Lorg/python/core/PyObject;
140: astore 4
142: aload 4
144: ifnonnull 68
147: aload_1
148: iconst_1
149: invokevirtual #20; //Method org/python/core/PyFrame.setline:(I)V
152: aload_1
153: ldc #43; //String _[1_3]
155: invokevirtual #73; //Method org/python/core/PyFrame.dellocal:(Ljava/lang/String;)V
158: aload 6
160: invokevirtual #77; //Method org/python/core/PyObject.__setitem__:(Lorg/python/core/PyObject;Lorg/python/core/PyObject;)V
163: aconst_null
164: astore_2
165: aload_1
166: iconst_m1
167: putfield #81; //Field org/python/core/PyFrame.f_lasti:I
170: getstatic #85; //Field org/python/core/Py.None:Lorg/python/core/PyObject;
173: areturn
}
public PyObject f$0(PyFrame pyframe)
{
pyframe.setglobal("__file__", _0);
pyframe.setline(1);
PyObject pyobject = pyframe.getname("e");
PyList pylist = new PyList();
PyObject pyobject1 = pylist.__getattr__("append");
pyframe.setlocal("_[1_3]", pyobject1);
pyobject1 = null;
pyframe.setline(1);o
pyobject1 = pyframe.getname("d").__iter__();
PyObject pyobject3;
do
{
pyframe.setline(1);
pyobject2 = pyobject1.__iternext__();
if(pyobject2 != null)
{
PyObject apyobject[] = Py.unpackSequence(pyobject2, 2);
pyobject3 = apyobject[0];
pyframe.setlocal("b", pyobject3);
pyobject3 = null;
pyobject3 = apyobject[1];
pyframe.setlocal("c", pyobject3);
pyobject3 = null;
pyframe.setline(1);
pyframe.getname("_[1_3]").__call__(pyframe.getname("b"));
} else
{
break;
}
} while(true);
pyframe.setline(1);
pyframe.dellocal("_[1_3]");
pyframe.getname("a").__setitem__(pylist, pyobject3);
pyobject = null;
pyframe.f_lasti = -1;
return Py.None;
}
(jython)pjenvey@golgo13:~/src/java/jython-trunk-clean3/asm-3.1/lib$ java -cp ~/src/java//jython-trunk-clean3/dist/jython.jar:asm-3.1.jar:asm-tree-3.1.jar:asm-analysis-3.1.jar:asm-util-3.1.jar org.objectweb.asm.util.CheckClassAdapter /tmp/bad2\$py.classThe first column shown is the instruction number, the second the contents of the local variable table, the third the contents of the operand stack, then finally the instruction.
org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 90: Expected an object reference, but found .
at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source)
at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source)
at org.objectweb.asm.util.CheckClassAdapter.main(Unknown Source)
Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Expected an object reference, but found .
at org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(Unknown Source)
at org.objectweb.asm.tree.analysis.Frame.execute(Unknown Source)
... 3 more
f$0(Lorg/python/core/PyFrame;)Lorg/python/core/PyObject;
00000 Lbad2$py; PyFrame . . . . . : : FRAME FULL [] []
00001 Lbad2$py; PyFrame . . . . . : : ALOAD 1
00002 Lbad2$py; PyFrame . . . . . : PyFrame : LDC "__file__"
00003 Lbad2$py; PyFrame . . . . . : PyFrame String : GETSTATIC bad2$py._0 : Lorg/python/core/PyString;
00004 Lbad2$py; PyFrame . . . . . : PyFrame String PyString : INVOKEVIRTUAL org/python/core/PyFrame.setglobal (Ljava/lang/String;Lorg/python/core/PyObject;)V
00005 Lbad2$py; PyFrame . . . . . : : ALOAD 1
00006 Lbad2$py; PyFrame . . . . . : PyFrame : ICONST_1
00007 Lbad2$py; PyFrame . . . . . : PyFrame I : INVOKEVIRTUAL org/python/core/PyFrame.setline (I)V
00008 Lbad2$py; PyFrame . . . . . : : ALOAD 1
00009 Lbad2$py; PyFrame . . . . . : PyFrame : LDC "e"
00010 Lbad2$py; PyFrame . . . . . : PyFrame String : INVOKEVIRTUAL org/python/core/PyFrame.getname (Ljava/lang/String;)Lorg/python/core/PyObject;
00011 Lbad2$py; PyFrame . . . . . : PyObject : ASTORE 2
00012 Lbad2$py; PyFrame PyObject . . . . : : ALOAD 1
00013 Lbad2$py; PyFrame PyObject . . . . : PyFrame : LDC "a"
00014 Lbad2$py; PyFrame PyObject . . . . : PyFrame String : INVOKEVIRTUAL org/python/core/PyFrame.getname (Ljava/lang/String;)Lorg/python/core/PyObject;
00015 Lbad2$py; PyFrame PyObject . . . . : PyObject : NEW org/python/core/PyList
00016 Lbad2$py; PyFrame PyObject . . . . : PyObject PyList : DUP
00017 Lbad2$py; PyFrame PyObject . . . . : PyObject PyList PyList : INVOKESPECIAL org/python/core/PyList.()V
00018 Lbad2$py; PyFrame PyObject . . . . : PyObject PyList : DUP
00019 Lbad2$py; PyFrame PyObject . . . . : PyObject PyList PyList : LDC "append"
00020 Lbad2$py; PyFrame PyObject . . . . : PyObject PyList PyList String : INVOKEVIRTUAL org/python/core/PyObject.__getattr__ (Ljava/lang/String;)Lorg/python/core/PyObject;
00021 Lbad2$py; PyFrame PyObject . . . . : PyObject PyList PyObject : ASTORE 3
00022 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList : ALOAD 1
00023 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList PyFrame : LDC "_[1_3]"
00024 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList PyFrame String : ALOAD 3
00025 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList PyFrame String PyObject : INVOKEVIRTUAL org/python/core/PyFrame.setlocal (Ljava/lang/String;Lorg/python/core/PyObject;)V
00026 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList : ACONST_NULL
00027 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList Lnull; : ASTORE 3
00028 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList : ALOAD 1
00029 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList PyFrame : ICONST_1
00030 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList PyFrame I : INVOKEVIRTUAL org/python/core/PyFrame.setline (I)V
00031 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList : ALOAD 1
00032 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList PyFrame : LDC "d"
00033 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList PyFrame String : INVOKEVIRTUAL org/python/core/PyFrame.getname (Ljava/lang/String;)Lorg/python/core/PyObject;
00034 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList PyObject : INVOKEVIRTUAL org/python/core/PyObject.__iter__ ()Lorg/python/core/PyObject;
00035 Lbad2$py; PyFrame PyObject Lnull; . . . : PyObject PyList PyObject : ASTORE 3
00036 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList : GOTO L0
00037 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList : L1
00038 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList : FRAME FULL [bad2$py org/python/core/PyFrame org/python/core/PyObject org/python/core/PyObject org/python/core/PyObject] [org/python/core/PyObject org/python/core/PyList]
00039 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList : ALOAD 4
00040 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyObject : ICONST_2
00041 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyObject I : INVOKESTATIC org/python/core/Py.unpackSequence (Lorg/python/core/PyObject;I)[Lorg/python/core/PyObject;
00042 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyObject : ASTORE 5
00043 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject . : PyObject PyList : ALOAD 5
00044 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject . : PyObject PyList PyObject : ICONST_0
00045 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject . : PyObject PyList PyObject I : AALOAD
00046 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject . : PyObject PyList PyObject : ASTORE 6
00047 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList : ALOAD 1
00048 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList PyFrame : LDC "b"
00049 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList PyFrame String : ALOAD 6
00050 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList PyFrame String PyObject : INVOKEVIRTUAL org/python/core/PyFrame.setlocal (Ljava/lang/String;Lorg/python/core/PyObject;)V
00051 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList : ACONST_NULL
00052 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList Lnull; : ASTORE 6
00053 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList : ALOAD 5
00054 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject : ICONST_1
00055 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject I : AALOAD
00056 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject : ASTORE 6
00057 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList : ALOAD 1
00058 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList PyFrame : LDC "c"
00059 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList PyFrame String : ALOAD 6
00060 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList PyFrame String PyObject : INVOKEVIRTUAL org/python/core/PyFrame.setlocal (Ljava/lang/String;Lorg/python/core/PyObject;)V
00061 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList : ACONST_NULL
00062 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject PyObject : PyObject PyList Lnull; : ASTORE 6
00063 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList : ALOAD 1
00064 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyFrame : ICONST_1
00065 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyFrame I : INVOKEVIRTUAL org/python/core/PyFrame.setline (I)V
00066 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList : ALOAD 1
00067 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyFrame : LDC "_[1_3]"
00068 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyFrame String : INVOKEVIRTUAL org/python/core/PyFrame.getname (Ljava/lang/String;)Lorg/python/core/PyObject;
00069 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject : ALOAD 1
00070 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject PyFrame : LDC "b"
00071 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject PyFrame String : INVOKEVIRTUAL org/python/core/PyFrame.getname (Ljava/lang/String;)Lorg/python/core/PyObject;
00072 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject PyObject : INVOKEVIRTUAL org/python/core/PyObject.__call__ (Lorg/python/core/PyObject;)Lorg/python/core/PyObject;
00073 Lbad2$py; PyFrame PyObject PyObject PyObject PyObject Lnull; : PyObject PyList PyObject : POP
00074 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList : L0
00075 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList : FRAME FULL [bad2$py org/python/core/PyFrame org/python/core/PyObject org/python/core/PyObject] [org/python/core/PyObject org/python/core/PyList]
00076 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList : ALOAD 1
00077 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList PyFrame : ICONST_1
00078 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList PyFrame I : INVOKEVIRTUAL org/python/core/PyFrame.setline (I)V
00079 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList : ALOAD 3
00080 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList PyObject : INVOKEVIRTUAL org/python/core/PyObject.__iternext__ ()Lorg/python/core/PyObject;
00081 Lbad2$py; PyFrame PyObject PyObject . . . : PyObject PyList PyObject : ASTORE 4
00082 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList : ALOAD 4
00083 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyObject : IFNONNULL L1
00084 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList : ALOAD 1
00085 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyFrame : ICONST_1
00086 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyFrame I : INVOKEVIRTUAL org/python/core/PyFrame.setline (I)V
00087 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList : ALOAD 1
00088 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyFrame : LDC "_[1_3]"
00089 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList PyFrame String : INVOKEVIRTUAL org/python/core/PyFrame.dellocal (Ljava/lang/String;)V
00090 Lbad2$py; PyFrame PyObject PyObject PyObject . . : PyObject PyList : ALOAD 6
00091 ? : INVOKEVIRTUAL org/python/core/PyObject.__setitem__ (Lorg/python/core/PyObject;Lorg/python/core/PyObject;)V
00092 ? : ACONST_NULL
00093 ? : ASTORE 2
00094 ? : ALOAD 1
00095 ? : ICONST_M1
00096 ? : PUTFIELD org/python/core/PyFrame.f_lasti : I
00097 ? : GETSTATIC org/python/core/Py.None : Lorg/python/core/PyObject;
00098 ? : ARETURN
@Override
public Object visitSubscript(Subscript node) throws Exception {
if (node.slice instanceof Slice) {
return Slice(node, (Slice) node.slice);
}
expr_contextType ctx = node.ctx;
if (node.ctx == expr_contextType.AugStore && augmode == expr_contextType.Store) {
restoreAugTmps(node, 2);
ctx = expr_contextType.Store;
} else {
visit(node.value);
visit(node.slice);
if (node.ctx == expr_contextType.AugStore && augmode == expr_contextType.Load) {
saveAugTmps(node, 2);
ctx = expr_contextType.Load;
}
}
switch (ctx) {
case Del:
code.invokevirtual("org/python/core/PyObject", "__delitem__", "(" + $pyObj + ")V");
return null;
case Load:
code.invokevirtual("org/python/core/PyObject", "__getitem__", "(" + $pyObj + ")" + $pyObj);
return null;
Store:
code.aload(temporary);
code.invokevirtual("org/python/core/PyObject", "__setitem__", "(" + $pyObj + $pyObj + ")V");
return null;
}
return null;
}
item assignment (visitSubscript):Our astview.py utility can show you the real AST nitty gritty:
value being e (visit(node.value) in visitSubscript),
key being (visit(node.slice) in visitSubscript) ->
list comp ->
tuple unpack
(jython)pjenvey@golgo13:~/src/java/jython-trunk-clean3$ jython ast/astview.py ~/src/python/sympy/bad2.py
('Module',
('body',
('Assign (1,0)',
('targets',
('Subscript (1,0)',
('value', ('Name (1,0)', ('id', 'a'), ('ctx', ('Load',)))),
('slice',
('Index',
('value',
('ListComp (1,3)',
('elt', ('Name (1,3)', ('id', 'b'), ('ctx', ('Load',)))),
('generators',
('comprehension',
('target',
('Tuple (1,5)',
('elts',
('Name (1,9)', ('id', 'b'), ('ctx', ('Store',))),
('Name (1,12)', ('id', 'c'), ('ctx', ('Store',)))),
('ctx', ('Store',)))),
('iter',
('Name (1,17)', ('id', 'd'), ('ctx', ('Load',)))),
('ifs',))))))),
('ctx', ('Store',)))),
('value', ('Name (1,23)', ('id', 'e'), ('ctx', ('Load',)))))))
00009 Lbad2$py; PyFrame . . . . . : PyFrame : LDC "e"(Index 2 being the PyObject after PyFrame in the last line)
00010 Lbad2$py; PyFrame . . . . . : PyFrame String : INVOKEVIRTUAL org/python/core/PyFrame.getname (Ljava/lang/String;)Lorg/python/core/PyObject;
00011 Lbad2$py; PyFrame . . . . . : PyObject : ASTORE 2
00012 Lbad2$py; PyFrame PyObject . . . . : : ALOAD 1
Index: src/org/python/compiler/CodeCompiler.java
===================================================================
--- src/org/python/compiler/CodeCompiler.java (revision 5379)
+++ src/org/python/compiler/CodeCompiler.java (working copy)
@@ -1604,6 +1604,7 @@
return Slice(node, (Slice) node.slice);
}
+ int value = temporary;
expr_contextType ctx = node.ctx;
if (node.ctx == expr_contextType.AugStore && augmode == expr_contextType.Store) {
restoreAugTmps(node, 2);
@@ -1627,7 +1628,7 @@
return null;
case Param:
case Store:
- code.aload(temporary);
+ code.aload(value);
code.invokevirtual("org/python/core/PyObject", "__setitem__", "(" + $pyObj + $pyObj + ")V");
return null;
}
I attended a couple days of JavaOne last week and luckily avoided the norovirus outbreak. However I did notice something else spreading through Moscone Center; interest in languages on the JVM. For example:
Occasionally on the Pylons IRC Channel or mailing list someone will reference the game Starcraft, where "Pylons" are structures that act as a power source. Sometimes the game vocally instructs you to construct additional Pylons.
You could say that last month we've constructed a couple additional Pylons, and by that I mean made Pylons run on different environments:
o Pylons on Jython trunk!
o Pylons on Google App Engine via Ian Bicking's appengine-monkey and mako trunk
With the development versions of both Pylons and its dependencies (and the Mako jython branch) I'm now able to create the flicksearch tutorial app from the Pylons Official Docs on Jython.
The Mako jython branch uses Python 2.5's _ast module, which Jython now supports thanks to Frank Wierzbicki's recent work. Mako's also using Armin Ronacher's handy ast module that'll hopefully find its way into the stdlib. Frank and I are still working on smoothing out a couple of our _ast's rough edges, hence the separate Mako jython branch, but Mako is in good shape with 95% of its 214 tests passing (with most of those failures due to the lack of PEP 263 source code encodings, which we'll get to supporting on Jython soon).
According to the Pylons on Jython buildbot, only a few of the Pylons dependency's tests fully pass -- but most of the failing tests are due to unicode issues (again, lack of PEP 263), CPython dict ordering assumed in doctests, and a few other issues that aren't important enough to cover here. Pylons own test failures are only due to unicode issues and lack of support for the Kid and Cheetah templating engines, which Pylons is only testing for the sake of testing alternative templating engines anyway.
The biggest problem with Pylons on Jython so far is running it in development mode with paster serve --reload, which hot reloads your WSGI app whenever a change to one of its files is made. The paster reloader is working but the reloading process is very slow on Jython. Reloading is done by spawning 2 processes; when a file changes, the child process serving your app exits and the parent creates it anew (last time I looked Django and CherryPy's reloaders also work this way). Since CPython startup time, including loading of the entire WSGI app, is pretty quick, this is the safest and probably easiest way to accomplish a reload.
Unfortunately Jython suffers from a lenghty startup time. It takes about 1.5 seconds best case scenario just to get the >>> prompt in Jython on my 2.33ghz MacBook Pro. It takes almost 10 seconds to get paster serve --reload to fully load a simple Pylons app; that's the parent process startup time plus the child process startup time plus the app load time.
I've been experimenting with using Nailgun to improve startup time, but it actually isn't helping Pylons' startup time as much as I thought it might. Jython must have a startup time bottleneck or two that we need to find here.
Nailgun also poses other problems, such as when System.exit()'ing resources like sockets and files are left open unless they're explicitly cleaned up. Worse yet, threads are left running -- and Pylons' paster serve by default loads 10 listener threads.
If we can't drastically improve the startup time issue in Jython sooner rather than later, we can probably come up with a reloader for Jython for the time being that doesn't require a full restart. These can be tricky and in general are avoided, though.
Here's a couple screenshots of Pylons on Jython, showing off a handy feature of dynamic languages on the JVM; just having the ability to play around with Java in an interactive interpreter:
o Pylons-dev WebError on Jython
o Pylons paster shell on Jython
Pylons is also making progress towards an additional release, 0.9.7, which will utilize the work done by the PyCon sprinters (particularly on WebHelpers), WebOb, WebError (and maybe WebTest) and will also include optional support for a basic SQLAlchemy configuration for new projects. As well as Alpha Jython support.
A couple weeks ago setuptools added support for Jython trunk. This was made possible with a number of additions and fixes to Jython for setuptools, including distutils and file descriptor support.
A working setuptools is the first big step in getting Pylons working on Jython. Django on Jython has had some recent success; luckily for them Django doesn't rely on/use setuptools at all. So Leo Soto, Jim Baker and the rest of the Django on Jython folks were able to skip directly to kicking around the Django code on Jython.
Now that we have setuptools, the next step is to go through each of Pylons dependencies and ensure their tests pass. Pylons as well as some of its dependencies use the nose test runner. Nose is almost fully working on Jython (after I submit a few upstream patches to nose).
Other dependencies (such as Paste and Beaker) use py.test for their test runners, but there's been some talk of moving some of these over to nose -- which would actually be a good thing right now for Jython. Though technically nose could run through these projects' tests as they already are, they rely on some py.test functionality, such as py.test.raises.
Not to bad mouth py.test -- it has some nice features, like distributed testing -- but I attempted get it working on Jython during the TurboGears 2 sprint a few weeks ago and it wasn't a small task. Part of the problem is the fact that py.test is part of the larger py package. py isn't just a test runner, it also includes other packages that are intertwined with test portion -- all of which need to work before you're able to use just py.test.