WSGI ir PyPy Sandbox

Python e-konferencijoje (žiūrime „Python bendruomenė“) užvirė diskusija apie Python mokymą, informaciją lietuvių kalba ir pan. Ir viena iš diskusijos temų buvo apie galimybę žmonėms išbandyti python'ą jo neinstaliavus. Miglotai prisiminiau, kad kažką panašaus gali pasiūlyti PyPy (fantastiškas projektas, bet čia jau atskira didelė tema). Ačiū Vytautui, kuris atgamino mano atmintį ir pasakė, kad tai vadinasi pypy sandbox. Na bent jau aišku kodėl mano galvoje tai nesurado vietos (žiūrime mano domeno pavadinimą). Taigi pabandžiau panagrinėti ar labai sunku tai padaryti ir pasirodo tai nėra itin sunku. Reikėjo tik šiek tiek pasikonsultuoti ir per kelias dienas turėjau veikiantį rezultatą: http://py.sandbox.lt. Pvz.: programėlė atspausdinanti "Labas, pasauli!".

Taigi, ką reikia daryti, jei norite kažko panašaus:

  1. Gaukite pypy kodą (naudoju trunk'ą, nes taip rekomendavo):

    svn co http://codespeak.net/svn/pypy/trunk pypy-trunk
  2. Sutransliuokite pypy sandbox'ą. Jums reikės bent 1GB RAM'o, nes kitaip viskas užtruks ilgai:

    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

    Šios komandos rezultatas yra pypy-c failas, kurį galite patestuoti taip:

    /pypy-trunk/pypy/translator/sandbox/pypy_interact.py /some/path/pypy-c
  3. Paskutinis žingsnis yra parašyti web'inę aplikaciją. Panašią web aplikaciją parašytą Django radau PyPy Vroclavo (?!) sprinto metu, bet aš asmeniškai labai mėgstu WSGI. Taigi parašiau WSGI aplikaciją kuri daro tą patį. Paaiškinantys komentarai kode:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # Pridedame pypy-trunk į path'ą, kad galėtūmem importuoti
    # reikalingus pypy modulius.
    import sys
    sys.path.append('/home/dalius/projects/pypy-trunk')

    from pypy.translator.sandbox import pypy_interact
    from cStringIO import StringIO

    # Naudojame webob, nes tai stipriai palengviną mūsų gyvenimą.
    from webob import Request
    import urllib

    # pypy-c lokacija jūsų kompiuteryje
    SANDBOX_BIN = '/home/dalius/projects/pypy-trunk/pypy/translator/sandbox/pypy-c'

    # Kodo šablonai
    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 - nurodome kiek atminties skiriame pypy. Šiuo
    # atveju vieną megabaitą. Atkreipkite dėmesį į tai,
    # kad šis argumentas turi būti pirmas.
    sandproc = pypy_interact.PyPySandboxedProc(SANDBOX_BIN,
    ['--heapsize', str(1024*1024), '-c', code])
    # Nurodome kiek laiko gali trukti procesas. Apsaugo nuo:
    # 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)

Suinstaliavę paste šią aplikaciją galite paleisti tiesiog taip ir pabandyti, bet realiame pasaulyje pasirinkimų yra tikrai daugiau. Aš naudoju Apache ir mod_wsgi modulį. Kol PyPy problema nesutaisyta jums teks pasinaudoti šiuo mažu fix'u, kad mod_wsgi veiktų. Kita mod_wsgi problema yra tokia, kad kai pypy sandbox proceso galimas laikas pasibaigs jis nebus tinkamai terminuojamas. Sprendimas yra naudoti mod_wsgi 2.X branch'ą demono rėžime (daugiau info čia: http://groups.google.com/group/modwsgi/browse_thread/thread/aae8d2d675ebcf9).

Dar vienas patarimas: jūsų serverio resursų gali neužtekti sutransliuoti pypy-c. Prisiminkite, kad yra tokie dalykai kaip virtualizacija. Suinstaliuokite analogišką sistemą jūsų serveriui ir sutransliuokite pypy-c ten.

Ir dar keletas praktinio pritaikymo pavyzdžių:

Šaltiniai: