Maciej Fijalkowski, fijal@genesilico.pl
This tutorial explains how to use PyPy's JavaScript backend. The reader should have some previous knowledge of writing RPython programs.
The JavaScript backend lets you write RPython source code which gets translated into JavaScript and executed in a browser. The resulting JavaScript code should not depend on a browser (this is a backend responsibility) and should integrate as much as possible with a web server.
You can use a JavaScript backend in many different ways. One of those is to simply run jscompile from the 'bin' directory with a module name and a list of functions, this makes that the listed functions get compiled. Another way is to use the rpython2javascript function from js.main to generate your javascript on-the-fly (e.g. when serving a web page).
Here is a simple example using MochiKit's logDebug call to show a message:
from pypy.translator.js.modules import mochikit def simple_example(): mochikit.createLoggingPane(True) mochikit.logDebug("Hello")
Save this to a python file, e.g. simpledemo.py. Then use jscompile to create a javascript version:
$ python pypy/bin/jscompile.py simpledemo simple_example
Note that you specify the module as a python module which must be on the python path.
When you run this you should see some information on the compilation process scrolling past. The JavaScript is written to a file which looks something like /tmp/usession-1/some_strange_function_which_will_never_be_called.js.
You can call the compilation process programatically using rpython2javascript:
from pypy.translator.js.main import rpython2javascript import simpledemo js_src = rpython2javascript(simpledemo, ["simple_example"]) print js_src
The idea is simple: Normal python method calls get rendered as communication over XMLHttpRequest without too much user intervention. To achieve this, you must tell PyPy that these particular method calls need to be rendered in this way.
To achieve this, first you must subclass BasicExternal from bltregistry and provide an instance of it to RPython code (for instance as global variable). Then you need to set class instance's _render_xmlhttp to True, to tell the JS backend to render it using xmlhttp communication. Because the web server code does not need to be rpython, you have to supply some way of telling PyPy what types can be returned out of it. This is done using the decorator @callback(retval = <used type>) from bltregistry. For example you may provide:
from pypy.translator.js.lib.support import callback @callback(retval = {str:str}) def some_fun(self, some_arg = 3): ....
The decorator tells the compiler that this function will return mapping from string to string and will take integer as an argument. You can simply specify the arguments using keyword arguments with an example, or pass an args dictionary to the described decorator.
Then you must supply a method which returns JSON data in the server itself (for example, this is done automatically by TurboGears using the @expose(format='json') decorator).
To create a simple javascript method which pings a server you need two parts, a server side python class which knows how to talk via XMLHttpRequest to a client side call.
On the server side:
from pypy.rpython.ootypesystem.bltregistry import BasicExternal from pypy.translator.js.lib.support import callback class PingHandler(BasicExternal): _render_xmlhttp = True @callback(retval={str:str}) def ping(self, ping_str="aa"): return dict(response="PONG: %s" % ping_str) ping_handler = PingHandler()
This uses the BasicExternal class and the described decorator to let PyPy know how to deal with the input and output of the methods. Now you can use an instance of this class to pass to other parts of the RPython code.
On the client you call the ping_handler.ping method with a callback:
from pypy.translator.js.modules import mochikit from somewhere import ping_handler def callback(response): mochikit.logDebug("Got response: " + response["response"]) def ping(): ping_handler.ping("PING", callback)
You compile this to JavaScript using jscompile or rpython2javascript and the ping method. The resulting javascript is passed to a web browser, while there needs to be a server which knows how to deal with a HTTP request to /ping (the name is derived from the name of the method decorated with described). The server then calls the ping_handler.ping method with the data from the call and returns a JSON dictionary.
There is nothing special in this case. The JS backend can work with virtually any web framework. In some of the examples TurboGears is used, but just for simplifying generation of the JSON data. You can use simplejson to generate a JSON response from any framework. The django ping example shows how to do this in Django.
There is bub'n'bros client working in javascript available here (No working copy on-line right now, sorry) and a simple example of JS a python console. There is also a simple django ping example.