WSGI and PyPy sandbox (English)

Here in Lithuania we have small but very enthusiastic python community and recently we have had discussion in our mailing list how we could improve Python learning situation here in Lithuania. One part of discussion was about giving people possibility to try playing around with python without installing it. I had remembered that something similar was proposed by PyPy but had not managed to remember how it was named. My friend pointed that it is named PyPy sandbox. I guess my domain name has higher priority in my head :-)

So I have decided to play around with it a little bit. With some help from pypy-dev mailing list I have managed to create working solution in several days: http://py.sandbox.lt. Here is example printing "Hello, world!".

Here is instructions from my head what you must do to implement something similar:

  1. Get pypy code (you must use trunk):

    svn co http://codespeak.net/svn/pypy/trunk pypy-trunk
    
  2. Translate pypy sandbox. Please note that you need at least 1GB RAM because otherwise it will take a lot of time (or will fail if you don't have swap):

    sudo aptitude install python-dev
    sudo aptitude install python-ctypes
    sudo aptitude install libffi-dev
    cd pypy-trunk/pypy/translator/goal
    ./translate.py --sandbox targetpypystandalone.py
    

    The result of this will be pypy-c file. You can test it with this command:

    /pypy-trunk/pypy/translator/sandbox/pypy_interact.py /some/path/pypy-c
    
  3. The last step is to write web application. I have found similar application written in Django in PyPy blog entry about PyPy Wroclaw sprint. However I personally prefer WSGI. So I have written similar application (some explaining comments in the code):

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Add pypy-trunk path to system path
    import sys
    sys.path.append('/home/dalius/projects/pypy-trunk')
    from pypy.translator.sandbox import pypy_interact
    from cStringIO import StringIO
    # We use webob here because it makes WSGI programming easier
    from webob import Request
    import urllib
    # pypy-c location in your system
    SANDBOX_BIN = '/home/dalius/projects/pypy-trunk/pypy/translator/sandbox/pypy-c'
    # Code templates
    page_template = """
        <style type="text/css">
            div {max-width:700px; border:1px solid black;
            font-family:monospace; margin:0 0 10px 0; padding:5px;}
            div h5 {margin:0 0 5px 0; padding:0; background:#eee;}
            form {max-width:700px;}
            textarea, input {display:block;}
            textarea {width:700px; height:400px;}
            input.code {width: 100%%;}
        </style>
        <div><h5>Code</h5>
        <form method="POST">
            <textarea name="codepad">%s</textarea>
            <input type="submit" value="Run Code"/>
        </form>
        </div>
        """
    page_output_template = """
        <div>
            <h5>Code address</h5>
            <input class="code" type="text" value="http://py.sandbox.lt/?%s"/>
        </div>
        <div>
            <h5>Output</h5>
            <pre>%s</pre>
        </div>
    """
    def escape_html(html):
        return html.replace('&', '&amp').replace('<', '&lt').replace('>', '&gt;')
    class PyPyApp:
        def __init__(self):
            pass
        def __call__(self, environ, start_response):
            """
            """
            req = Request(environ)
            if 'codepad' in req.params:
                code = req.params['codepad']
                code_output = StringIO()
                # heapsize - amount of memory for pypy process. In this
                # case it is 1MB. Please note that this argument must be
                # first.
                sandproc = pypy_interact.PyPySandboxedProc(SANDBOX_BIN,
                        ['--heapsize', str(1024*1024), '-c', code])
                # Limit process in time. Protection from:
                # while True: pass
                sandproc.settimeout(10)
                try:
                    sandproc.interact(stdout=code_output, stderr=code_output)
                finally:
                    sandproc.kill()
                page = (page_template + page_output_template) % \
                        (escape_html(code),
                                urllib.urlencode((('codepad', code),)),
                                escape_html(code_output.getvalue()))
            else:
                page = page_template % ('', )
            start_response('200 OK',
                    [('Content-type', 'text/html; charset=UTF-8')])
            return [page]
    if __name__ == '__main__':
        from paste.httpserver import serve
        serve(PyPyApp(), host='0.0.0.0', port=8080)
    

You can install paste and try this application. However in real world there are more solutions. I personally use Apache with mod_wsgi module. There are some hints regarding PyPy and mod_wsgi:

And yet another advice: your server running Apache might not have resources to translate pypy-c sandbox. Please remember that you can use virtualisation (e.g. VirtualBox) in this case. Install OS similar to your server and translate pypy-c in it.

Some practical examples:

Resources: