Source

yt_ajax / cp / httprepl.py

Full commit
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 tempfile
import numpy as na
from yt.utilities.amr_utils import write_png_to_file
from yt.visualization.image_writer import map_to_colors

import base64
import uuid

def scale_buffer(buffer):
    image = na.log10(buffer)
    mi = na.nanmin(image[~na.isinf(image)])
    ma = na.nanmax(image[~na.isinf(image)])
    color_bounds = mi, ma
    image = (image - color_bounds[0])/(color_bounds[1] - color_bounds[0])
    to_plot = map_to_colors(image, "algae")
    to_plot = na.clip(to_plot, 0, 255)
    tf = tempfile.TemporaryFile()
    write_png_to_file(to_plot, tf)
    tf.seek(0)
    s = tf.read()
    tf.close()
    return s

class PannerWidget(object):
    # Returns: image_data
    def __init__(self, panner, my_id):
        self.panner = panner
        self.my_id = my_id

    def do_action(self, action):
        if action == "L":
            self.panner.pan_rel_x(-0.1)
        elif action == "R":
            self.panner.pan_rel_x(+0.1)
        elif action == "D":
            self.panner.pan_rel_y(-0.1)
        elif action == "U":
            self.panner.pan_rel_y(+0.1)
        elif action == "Z2":
            self.panner.zoom(2.0)
        elif action == "ZO2":
            self.panner.zoom(0.5)
        elif action == "Z10":
            self.panner.zoom(10.0)
        elif action == "ZO10":
            self.panner.zoom(0.1)
        elif action == "REFRESH":
            self.panner.zoom(1.0)
        return scale_buffer(self.panner.buffer)

def image_adder(local_vars):
    def add_image(buffer):
        s = scale_buffer(buffer)
        local_vars['new_values'] = "<img src='data:image/png;base64,%s'>" % (
                base64.encodestring(s))
    return add_image

def panner_widget_maker(widgets, local_vars):
    def add_panner(panner):
        unique_id = str(uuid.uuid1())
        widget = PannerWidget(panner, unique_id)
        widgets[unique_id] = widget
        s = open("image_widget.html").read() % dict(my_id = unique_id)
        local_vars['new_values'] = s
        #print "Setting new values:\n\n" + s
    return add_panner

class HTTPREPL:
    
    def __init__(self, locals=None):
        self.locals = {}
        self.locals['add_image'] = image_adder(self.locals)
        if locals:
            self.locals.update(locals)
        self.buffer = []
        self._widgets = {}
        self.locals['add_panner'] = panner_widget_maker(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)
    
    
repl_dict = {}

class Root:
    
    def __init__(self, locals=None):
        self._reply = HTTPREPL(None)

    @property
    def repl(self):
        print "Session ID", cherrypy.session.id
        return self._reply
        if cherrypy.session.id not in repl_dict:
            repl_dict[cherrypy.session.id] = HTTPREPL(None)
        return repl_dict[cherrypy.session.id]
    
    def index(self):
        """Return an HTTP-based Read-Eval-Print-Loop terminal."""
        return open(os.path.join(localDir, "httprepl.html")).read()
    index.exposed = True
    
    def push(self, line):
        """Push 'line' and return exec results as a bare response."""
        result = self.repl.push(line)
        new_values = self.repl.locals.pop("new_values", "")
        if result is None:
            # More input lines needed.
            import cherrypy
            cherrypy.response.status = 204
        return json.dumps( dict(text = result, new_values = new_values ))
    push.exposed = True
    
    def dir(self, line):
        """Push 'line' and return result of eval on the final expr."""
        result = self.repl.dir(line)
        if not result:
            import cherrypy
            cherrypy.response.status = 204
            return
        return repr(result)
    dir.exposed = True
    
    def doc(self, line):
        """Push 'line' and return result of getargspec on the final expr."""
        result = self.repl.doc(line)
        if not result:
            import cherrypy
            cherrypy.response.status = 204
        return result
    doc.exposed = True

    def panner_widgets(self, widget_id, widget_action):
        widget = self.repl._widgets.get(widget_id, None)
        if not isinstance(widget, PannerWidget):
            print widget_id, self.repl._widgets.keys()
            return ""
        s = base64.encodestring(widget.do_action(widget_action))
        return json.dumps( dict(image_data = s) )
    panner_widgets.exposed = True

if __name__ == '__main__':
    argv = list(sys.argv[1:])
    if argv:
        target = argv[0]
        print "CherryPy is the only server supported at the moment. Care to add yours?"
        sys.exit(1)
    
    import cherrypy
    cherrypy.config.update({"server.environment": "production",
                            # CP 3 syntax:
                            "environment": "production",
                            "tools.sessions.on" : True,
                            "tools.sessions.storage_type" : "file",
                            "tools.sessions.storage_path" : "sessions",
                            "tools.sessions.timeout" : 60,
                            })
    cherrypy.tree.mount(Root(globals()))
    cherrypy.server.start()
    print "Started"
    cherrypy.engine.start()

# 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_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)