PyCon India 2011
September 16-18, 2011
A tool to convert:
def square(x):
return x * x
to:
function square(x) {
return x * x;
}
Here is a sample from Open Library project.
<ul class="booklinks" id="links-display">
<!-- JAVASCRIPT Template -->
<li id="links-template" style="display: none;" class="repeat-item">
<div class="linkRemove"><a ...>[x]</a></div>
<a href="{{url}}">{{title}}</a>
<span class="link wrap">{{url}}</span>
...
</li>
<!-- PYTHON/web.py template -->
$for index, link in enumerate(work.links):
<li class="repeat-item">
<div class="linkRemove"><a ...>[x]</a></div>
<a href="$link.url">$link.title</a>
<span class="link wrap">$link.url</span>
...
</li>
</ul>
Here is a more complex example that renders html both on server and client:
http://openlibrary.org/subjects/python_(computer_program_language)
Template:
$jsdef hello(name):
Hello, $name!
$hello("world")
generates the following HTML:
<script type="text/javascript">
function hello(name){
var self = [], loop;
self.push("Hello, "); self.push(websafe(name)); self.push("!\n");
self.push("\n");
return self.join("");
}
</script>
Hello, world!
This won't work!
$jsdef hello(name):
Hello, $name.upper()!
$hello("world")
It will generate:
<script type="text/javascript">
function hello(name){
var self = [], loop;
self.push("Hello, "); self.push(websafe(name.upper())); self.push("!\n");
self.push("\n");
return self.join("");
}
</script>
Hello, world!
Workaround:
$code:
def upper(s): return s.upper()
<script type="text/javascript">
function upper(s) { return s.toUpperCase(); }
</script>
$jsdef hello(name):
Hello, $upper(name)!
$hello("world")
The functionality of upper
has to implemented separately in Python and Javascript.
UGLY!
Started working on a new project.
I Found Pyjamas.
Pyjamas is a Rich Internet Application (RIA) Development Platform for both Web and Desktop.
It contains a Python-to-Javascript compiler, an AJAX framework and a Widget Set API. Pyjamas started life as a Python port of Google Web Toolkit, the Java-to-Javascript compiler.
Acronym for Abstract Syntax Tree.
Example:
z = x * x + y * y
= | +---+---+ | | z + | +-----+-----+ | | * * | | +---+---+ +---+---+ | | | | x x y y
>>> import compiler >>> compiler.parse('x = 1 + 2') Module(None, Stmt( [Assign( [AssName('x', 'OP_ASSIGN')], Add((Const(1), Const(2))))]))
As tree:
Assign
+-----------+---------+
| |
AssName Add
+----+-----+ +----+-----+
| | | |
'x' 'OP_ASSIGN' Const Const
| |
1 2
I wrote a small script to explore Python AST.
$ ./scripts/compile.py 'y = x + 1'
Module(None, Stmt([Assign([AssName('y', 'OP_ASSIGN')], Add((Name('x'), Const(1))))]))
Take node.children[0].children[0]
.
$ ./scripts/compile.py 'y = x + 1' 0 0
Assign([AssName('y', 'OP_ASSIGN')], Add((Name('x'), Const(1))))
See what methods Assign
class has.
$ ./scripts/compile.py 'y = x + 1' 0 0 -e 'dir(node)'
['__doc__', '__init__', '__iter__', '__module__', '__repr__',
'asList', 'expr', 'getChildNodes', 'getChildren', 'lineno', 'nodes']
Lets see what is expr
.
$ ./scripts/compile.py 'y = x + 1' 0 0 -e 'node.expr'
Add((Name('x'), Const(1)))
class Visitor:
def visit(self, node):
"""Dispatches the call to visit_$nodetype method."""
nodetype = node.__class__.__name__
f = getattr(self, "visit_" + nodetype, self.generic_visit)
return f(node)
def generic_visit(self, node):
pass
def visit_Return(self, node):
return "return %s;" % self.visit(node.value)
def visit_While(self, node):
condition, code, else_part = node.asList()
return "while (%s) { %s } " % (self.visit(condition), self.visit(code))
...
NodeVisitor
class in ast
module, which provides the functionality of visit
method.Slightly improved version.
class Visitor(ast.NodeVisitor):
def __init__(self):
self.indent = 0
self.buf = None
def translate(self, node):
self.buf = StringIO()
self.visit(node)
return self.buf.getvalue()
def write(self, line, indent=False):
if indent:
self.buf.write(" " * self.indent)
self.buf.write(line)
return self
def write_block(self, node):
self.indent += 1
self.visit(node)
self.indent -= 1
...
class Visitor(ast.NodeVisitor):
...
def visit_Add(self, node):
self.visit(node.left)
self.write(" + ")
self.visit(node.right)
def visit_If(self, node):
test, code = node.tests[0]
self.write("if (", indent=True)
self.visit(test)
self.write(") {\n")
self.write_block(code)
self.write("}", indent=True)
In Python variables are considered as local if undeclared.
def square(x):
y = x * x # y is local
return y
In javascript they are considered globals.
function square(x) {
y = x * x; // y is global.
return y;
}
Correct translation:
Python:
ncalls = 0
def square(x):
global ncalls # declare ncalls as global
ncalls += 1
y = x * x
return y
Javascript:
ncalls = 0;
function square(x) {
var y; // declare y as local
ncalls += 1;
y = x * x;
return y;
}
Python:
def uppercase(s):
return s.upper()
Javascript:
// this doesn't work
function uppercase(s) {
return s.upper();
}
// correct!
function uppercase(s) {
return py.getattr(s, "upper")();
}
Do we really need to support all __foo__
magic?
def f():
"""dummy function"""
return 1
x = f.__name__
y = f.__doc__
Readability counts!
Python:
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
Javascript generated by Pyjamas:
$m['fib'] = function(n) {
var $or1,$or2,$add3,$add4,$sub3,$sub2,$sub1,$sub4;
if ($p['bool'](($p['bool']($or1=$p['op_eq'](n, 0))?$or1:$p['op_eq'](n, 1)))) {
return 1;
}
else {
return $p['__op_add']($add3=$m['fib']($p['__op_sub']($sub1=n,$sub2=1)),
$add4=$m['fib']($p['__op_sub']($sub3=n,$sub4=2)));
}
return null;
};
There was an interesting talk on AST at US PyCon 2011.
What would you do with an ast? - Matthew J Desmarais - video
The slides sources of this talk are available at:
Table of Contents | t |
---|---|
Exposé | ESC |
Full screen slides | e |
Presenter View | p |
Source Files | s |
Slide Numbers | n |
Toggle screen blanking | b |
Show/hide slide context | c |
Notes | 2 |
Help | h |