Source

yt_ajax / bottle / ytrepl_extpanel.py

Full commit
from bottle import request, response, route, run, server_names, debug

import cgi
import codeop
import inspect
import os
localDir = os.path.dirname(__file__)
import re
import json

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

import sys
import traceback

import numpy as na

import base64
import uuid

from ext_widgets import YTExtWebWidget

class HTTPREPL(object):
    
    def __init__(self, locals=None):
        self.locals = {}
        if locals:
            self.locals.update(locals)
        self.buffer = []
        self._widgets = {}
        for cls in YTExtWebWidget.__subclasses__():
            self.locals['add_%s' % cls._widget_name] = cls.create_object_creator(
                    self._widgets, self.locals)
    
    def push(self, line):
        """Push 'line' and return exec results (None if more input needed)."""
        if line == "help":
            return "Type help(object) for help about object."
        if line == "help()":
            return "You cannot call help() without an argument."
        
        self.buffer.append(line)
        source = "\n".join(self.buffer)
        
        try:
            code = codeop.compile_command(source, "<HTTP input>", 'single')
        except (OverflowError, SyntaxError, ValueError):
            self.buffer = []
            return traceback.format_exc()
        
        if code is None:
            # More lines needed.
            return None
        
        self.buffer = []
        return self.execute(code)
    
    def execute(self, code):
        """Execute the given code in self.locals and return any stdout/sterr."""
        out = StringIO()
        oldout = sys.stdout
        olderr = sys.stderr
        sys.stdout = sys.stderr = out
        try:
            try:
                exec code in self.locals
            except:
                result = traceback.format_exc()
            else:
                result = out.getvalue()
        finally:
            sys.stdout = oldout
            sys.stderr = olderr
        out.close()
        return result
    
    def dir(self, line):
        """Examine a partial line and provide attr list of final expr."""
        line = re.split(r"\s", line)[-1].strip()
        # Support lines like "thing.attr" as "thing.", because the browser
        # may not finish calculating the partial line until after the user
        # has clicked on a few more keys.
        line = ".".join(line.split(".")[:-1])
        try:
            result = eval("dir(%s)" % line, {}, self.locals)
        except:
            return []
        return result
    
    def doc(self, line):
        """Examine a partial line and provide sig+doc of final expr."""
        line = re.split(r"\s", line)[-1].strip()
        # Support lines like "func(text" as "func(", because the browser
        # may not finish calculating the partial line until after the user
        # has clicked on a few more keys.
        line = "(".join(line.split("(")[:-1])
        try:
            result = eval(line, {}, self.locals)
            try:
                if isinstance(result, type):
                    func = result.__init__
                else:
                    func = result
                args, varargs, varkw, defaults = inspect.getargspec(func)
            except TypeError:
                if callable(result):
                    doc = getattr(result, "__doc__", "") or ""
                    return "%s\n\n%s" % (line, doc)
                return None
        except:
            return None
        
        if args and args[0] == 'self':
            args.pop(0)
        missing = object()
        defaults = defaults or []
        defaults = ([missing] * (len(args) - len(defaults))) + list(defaults)
        arglist = []
        for a, d in zip(args, defaults):
            if d is missing:
                arglist.append(a)
            else:
                arglist.append("%s=%s" % (a, d))
        if varargs:
            arglist.append("*%s" % varargs)
        if varkw:
            arglist.append("**%s" % varkw)
        doc = getattr(result, "__doc__", "") or ""
        return "%s(%s)\n%s" % (line, ", ".join(arglist), doc)
    
route_functions = {}
repl = HTTPREPL(None)

def preroute(future_route, *args, **kwargs):
    def router(func):
        route_functions[future_route] = (args, kwargs, func)
        return func
    return router

@preroute("/")
def index():
    """Return an HTTP-based Read-Eval-Print-Loop terminal."""
    return open(os.path.join(localDir, "ytrepl_extpanel.html")).read()
    
@preroute("/push", method="POST")
def push():
    """Push 'line' and return exec results as a bare response."""
    line = request.POST['line']
    result = cgi.escape(repl.push(line))
    new_values = repl.locals.pop("new_values", "")
    execute = repl.locals.pop("execute", "")
    if result is None:
        # More input lines needed.
        response.status = 204
    tr = dict(success = 'true', text = result, new_values = new_values,
              execute = execute)
    print tr
    return json.dumps(tr)

@preroute("/dir")
def rdir():
    """Push 'line' and return result of eval on the final expr."""
    line = request.GET['line']
    result = repl.dir(line)
    if not result:
        response.status = 204
        return
    return repr(result)

@preroute("/doc")
def doc():
    """Push 'line' and return result of getargspec on the final expr."""
    line = request.GET['line']
    result = repl.doc(line)
    if not result:
        response.status = 204
    return result

# This needs to get moved in to the base class we implement
@preroute("/widget_action", method="POST")
def panner_widgets():
    widget_id = request.POST['widget_id']
    widget = repl._widgets.get(widget_id, None)
    if not isinstance(widget, YTExtWebWidget):
        print widget_id, repl._widgets.keys()
        return ""
    rv = widget.do_action(request.POST)
    return json.dumps( rv ) 

@preroute("/resources/:val", method="GET")
def resources(val):
    if not os.path.exists("resources/%s" % val):
        response.status = 404
        return
    return open("resources/%s" % val).read()

if __name__ == '__main__':
    debug(mode=True)
    token = uuid.uuid1()
    for r in route_functions:
        args, kwargs, f = route_functions[r]
        if r[0] == "/": r = r[1:]
        rp = "/%s/%s" % (token, r)
        print "%s => %s" % (rp, f.func_name)
        route(rp, *args, **kwargs)(f)
    print "Greetings! Your private token is %s ." % token
    print
    print "Please direct your browser to:"
    print
    print "     http://localhost:8080/%s/" % token
    print
    print
    if "-o" in sys.argv:
        def open_browser():
            """Start a browser after waiting for half a second."""
            import webbrowser, threading
            def _open_browser():
                webbrowser.open('http://localhost:%s/%s/' % (8080, token))
            thread = threading.Timer(0.5, _open_browser)
            thread.start()
        open_browser()
    server_type = server_names.get("wsgiref")
    server = server_type(host='localhost', port=8080)
    #repl.locals['server'] = server
    run(server=server)

# from yt.mods import *; pf = load("/Users/matthewturk/Research/data/RD0005-mine/RedshiftOutput0005");proj = pf.h.proj(0,"Density");from yt.visualization.image_panner.api import VariableMeshPanner; vmp = VariableMeshPanner(proj, (512, 512), "Density"); add_image_panner(vmp)
# from yt.mods import *; pf = load("/Users/matthewturk/Research/data/RD0005-mine/RedshiftOutput0005");proj = pf.h.proj(0,"Density");from yt.visualization.image_panner.api import VariableMeshPanner; vmp = VariableMeshPanner(proj, (512, 512), "Density", add_image); vmp.zoom(1.0)