[Yt-svn] commit/yt: 2 new changesets

Bitbucket commits-noreply at bitbucket.org
Wed Mar 16 15:43:10 PDT 2011


2 new changesets in yt:

http://bitbucket.org/yt_analysis/yt/changeset/874bbba43b6d/
changeset:   r3849:874bbba43b6d
branch:      yt
user:        brittonsmith
date:        2011-03-16 23:42:08
summary:     Fixed image writing in light cone and halo profiler to save log fields.
affected #:  2 files (30 bytes)

--- a/yt/analysis_modules/halo_profiler/multi_halo_profiler.py	Tue Mar 15 14:35:04 2011 -0400
+++ b/yt/analysis_modules/halo_profiler/multi_halo_profiler.py	Wed Mar 16 18:42:08 2011 -0400
@@ -562,7 +562,7 @@
                         if save_images:
                             filename = "%s/Halo_%04d_%s_%s.png" % (my_output_dir, halo['id'], 
                                                                    dataset_name, axis_labels[w])
-                            write_image(frb[hp['field']], filename, cmap_name=hp['cmap'])
+                            write_image(na.log10(frb[hp['field']]), filename, cmap_name=hp['cmap'])
 
                     if save_cube: output.close()
 


--- a/yt/analysis_modules/light_cone/light_cone.py	Tue Mar 15 14:35:04 2011 -0400
+++ b/yt/analysis_modules/light_cone/light_cone.py	Wed Mar 16 18:42:08 2011 -0400
@@ -276,7 +276,7 @@
                                          weight_field=weight_field, node=node)
             if ytcfg.getint("yt", "__parallel_rank") == 0:
                 if save_slice_images:
-                    write_image(frb[field], "%s_%s.png" % (name, field), cmap_name=cmap_name)
+                    write_image(na.log10(frb[field]), "%s_%s.png" % (name, field), cmap_name=cmap_name)
 
                 if photon_field:
                     # Decrement the flux by the luminosity distance. Assume field in frb is in erg/s/cm^2/Hz
@@ -332,7 +332,7 @@
 
             # Write image.
             if save_slice_images:
-                write_image(frb[field], "%s_%s.png" % (filename, field), cmap_name=cmap_name)
+                write_image(na.log10(frb[field]), "%s_%s.png" % (filename, field), cmap_name=cmap_name)
 
             # Write stack to hdf5 file.
             if save_stack:


http://bitbucket.org/yt_analysis/yt/changeset/40d357dca679/
changeset:   r3850:40d357dca679
branch:      yt
user:        brittonsmith
date:        2011-03-16 23:42:57
summary:     Merge.
affected #:  4 files (0 bytes)

--- a/.bzrignore	Wed Mar 16 18:42:08 2011 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-__config__.py
-build
-hdf5.cfg
-setuptools-0.6c9-py2.5.egg


--- a/doc/install_script.sh	Wed Mar 16 18:42:08 2011 -0400
+++ b/doc/install_script.sh	Wed Mar 16 18:42:57 2011 -0400
@@ -239,12 +239,12 @@
     cd $1
     if [ ! -z `echo $1 | grep h5py` ]
     then
-	    echo "${DEST_DIR}/bin/python2.6 setup.py configure --hdf5=${HDF5_DIR}"
-	    ( ${DEST_DIR}/bin/python2.6 setup.py configure --hdf5=${HDF5_DIR} 2>&1 ) 1>> ${LOG_FILE} || do_exit
+	    echo "${DEST_DIR}/bin/python2.7 setup.py configure --hdf5=${HDF5_DIR}"
+	    ( ${DEST_DIR}/bin/python2.7 setup.py configure --hdf5=${HDF5_DIR} 2>&1 ) 1>> ${LOG_FILE} || do_exit
     fi
     shift
-    ( ${DEST_DIR}/bin/python2.6 setup.py build   $* 2>&1 ) 1>> ${LOG_FILE} || do_exit
-    ( ${DEST_DIR}/bin/python2.6 setup.py install    2>&1 ) 1>> ${LOG_FILE} || do_exit
+    ( ${DEST_DIR}/bin/python2.7 setup.py build   $* 2>&1 ) 1>> ${LOG_FILE} || do_exit
+    ( ${DEST_DIR}/bin/python2.7 setup.py install    2>&1 ) 1>> ${LOG_FILE} || do_exit
     touch done
     cd ..
 }
@@ -274,7 +274,7 @@
 if [ -z "$HDF5_DIR" ]
 then
     echo "Downloading HDF5"
-    get_enzotools hdf5-1.6.9.tar.gz
+    get_enzotools hdf5-1.8.6.tar.gz
 fi
 
 [ $INST_ZLIB -eq 1 ] && get_enzotools zlib-1.2.3.tar.bz2 
@@ -282,10 +282,10 @@
 [ $INST_PNG -eq 1 ] && get_enzotools libpng-1.2.43.tar.gz
 [ $INST_FTYPE -eq 1 ] && get_enzotools freetype-2.4.4.tar.gz
 [ $INST_SQLITE3 -eq 1 ] && get_enzotools sqlite-autoconf-3070500.tar.gz
-get_enzotools Python-2.6.3.tgz
+get_enzotools Python-2.7.1.tgz
 get_enzotools numpy-1.5.1.tar.gz
 get_enzotools matplotlib-1.0.0.tar.gz
-get_enzotools mercurial-1.7.3.tar.gz
+get_enzotools mercurial-1.8.1.tar.gz
 get_enzotools ipython-0.10.tar.gz
 get_enzotools h5py-1.2.0.tar.gz
 get_enzotools Cython-0.14.tar.gz
@@ -364,11 +364,11 @@
 
 if [ -z "$HDF5_DIR" ]
 then
-    if [ ! -e hdf5-1.6.9/done ]
+    if [ ! -e hdf5-1.8.6/done ]
     then
-        [ ! -e hdf5-1.6.9 ] && tar xfz hdf5-1.6.9.tar.gz
+        [ ! -e hdf5-1.8.6 ] && tar xfz hdf5-1.8.6.tar.gz
         echo "Installing HDF5"
-        cd hdf5-1.6.9
+        cd hdf5-1.8.6
         ( ./configure --prefix=${DEST_DIR}/ --enable-shared 2>&1 ) 1>> ${LOG_FILE} || do_exit
         ( make install 2>&1 ) 1>> ${LOG_FILE} || do_exit
         touch done
@@ -394,25 +394,26 @@
     fi
 fi
 
-if [ ! -e Python-2.6.3/done ]
+if [ ! -e Python-2.7.1/done ]
 then
     echo "Installing Python.  This may take a while, but don't worry.  YT loves you."
-    [ ! -e Python-2.6.3 ] && tar xfz Python-2.6.3.tgz
-    cd Python-2.6.3
+    [ ! -e Python-2.7.1 ] && tar xfz Python-2.7.1.tgz
+    cd Python-2.7.1
     ( ./configure --prefix=${DEST_DIR}/ 2>&1 ) 1>> ${LOG_FILE} || do_exit
 
     ( make 2>&1 ) 1>> ${LOG_FILE} || do_exit
     ( make install 2>&1 ) 1>> ${LOG_FILE} || do_exit
+    ( ln -sf ${DEST_DIR}/bin/python2.7 ${DEST_DIR}/bin/pyyt 2>&1 ) 1>> ${LOG_FILE}
     touch done
     cd ..
 fi
 
-export PYTHONPATH=${DEST_DIR}/lib/python2.6/site-packages/
+export PYTHONPATH=${DEST_DIR}/lib/python2.7/site-packages/
 
 if [ $INST_HG -eq 1 ]
 then
     echo "Installing Mercurial."
-    do_setup_py mercurial-1.7.3
+    do_setup_py mercurial-1.8.1
     export HG_EXEC=${DEST_DIR}/bin/hg
 else
     # We assume that hg can be found in the path.
@@ -436,16 +437,11 @@
     elif [ ! -e yt-hg ] 
     then
         YT_DIR="$PWD/yt-hg/"
+        ( ${HG_EXEC} --debug clone http://hg.enzotools.org/yt-supplemental/ 2>&1 ) 1>> ${LOG_FILE}
         # Recently the hg server has had some issues with timeouts.  In lieu of
         # a new webserver, we are now moving to a three-stage process.
         # First we clone the repo, but only up to r0.
-        ( ${HG_EXEC} --debug clone -r0 http://hg.enzotools.org/yt/ ./yt-hg 2>&1 ) 1>> ${LOG_FILE}
-        # Now we unbundle our previously downloaded bundle of changesets.
-        # This bundle has been created to include most of the recent
-        # changesets, which should avoid any problematic timeouts.
-        ( ${HG_EXEC} -R ${YT_DIR} unbundle yt.hg 2>&1 ) 1>> ${LOG_FILE}
-        # Now we pull new changes
-        ( ${HG_EXEC} -R ${YT_DIR} pull 2>&1 ) 1>> ${LOG_FILE}
+        ( ${HG_EXEC} --debug clone http://hg.enzotools.org/yt/ ./yt-hg 2>&1 ) 1>> ${LOG_FILE}
         # Now we update to the branch we're interested in.
         ( ${HG_EXEC} -R ${YT_DIR} up -C ${BRANCH} 2>&1 ) 1>> ${LOG_FILE}
     elif [ -e yt-hg ] 
@@ -459,10 +455,10 @@
 unset LDFLAGS 
 
 echo "Installing distribute"
-( ${DEST_DIR}/bin/python2.6 ${YT_DIR}/distribute_setup.py 2>&1 ) 1>> ${LOG_FILE} || do_exit
+( ${DEST_DIR}/bin/python2.7 ${YT_DIR}/distribute_setup.py 2>&1 ) 1>> ${LOG_FILE} || do_exit
 
 echo "Installing pip"
-( ${DEST_DIR}/bin/easy_install-2.6 pip 2>&1 ) 1>> ${LOG_FILE} || do_exit
+( ${DEST_DIR}/bin/easy_install-2.7 pip 2>&1 ) 1>> ${LOG_FILE} || do_exit
 
 do_setup_py numpy-1.5.1 ${NUMPY_ARGS}
 
@@ -511,7 +507,7 @@
 [ $INST_PNG -eq 1 ] && echo $PNG_DIR > png.cfg
 [ $INST_FTYPE -eq 1 ] && echo $FTYPE_DIR > freetype.cfg
 [ $INST_FORTHON -eq 1 ] && ( ( cd yt/utilities/kdtree && FORTHON_EXE=${DEST_DIR}/bin/Forthon make 2>&1 ) 1>> ${LOG_FILE} )
-( ${DEST_DIR}/bin/python2.6 setup.py develop 2>&1 ) 1>> ${LOG_FILE} || do_exit
+( ${DEST_DIR}/bin/python2.7 setup.py develop 2>&1 ) 1>> ${LOG_FILE} || do_exit
 touch done
 cd $MY_PWD
 
@@ -535,7 +531,7 @@
     echo
     echo "YT_DEST         => $DEST_DIR"
     echo "PATH            => $DEST_DIR/bin/"
-    echo "PYTHONPATH      => $DEST_DIR/lib/python2.6/site-packages/"
+    echo "PYTHONPATH      => $DEST_DIR/lib/python2.7/site-packages/"
     echo "LD_LIBRARY_PATH => $DEST_DIR/lib/"
     echo
     echo "For interactive data analysis and visualization, we recommend running"
@@ -571,14 +567,11 @@
       echo
     fi
     echo
-    echo "For support, see one of the following websites:"
+    echo "For support, see the website and join the mailing list:"
     echo
-    echo "    http://yt.enzotools.org/wiki/"
-    echo "    http://yt.enzotools.org/doc/"
+    echo "    http://yt.enzotools.org/"
+    echo "    http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org"
     echo
-    echo "Please also join the mailing list:"
-    echo 
-    echo "    http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org"
     echo
     echo "========================================================================"
     echo


--- a/yt/config.py	Wed Mar 16 18:42:08 2011 -0400
+++ b/yt/config.py	Wed Mar 16 18:42:57 2011 -0400
@@ -45,6 +45,7 @@
     loadfieldplugins = 'True',
     pluginfilename = 'my_plugins.py',
     parallel_traceback = 'False',
+    pasteboard_repo = '',
     )
 # Here is the upgrade.  We're actually going to parse the file in its entirety
 # here.  Then, if it has any of the Forbidden Sections, it will be rewritten


--- a/yt/gui/plot_editors.py	Wed Mar 16 18:42:08 2011 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +0,0 @@
-"""
-Figure editors for the Traits GUI
-
-Author: Matthew Turk <matthewturk at gmail.com>
-Affiliation: KIPAC/SLAC/Stanford
-Homepage: http://yt.enzotools.org/
-License:
-  Copyright (C) 2009 Matthew Turk.  All Rights Reserved.
-
-  This file is part of yt.
-
-  yt is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 3 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-"""
-
-import sys, matplotlib
-# We want matplotlib to use a wxPython backend
-matplotlib.use('QT4Agg')
-from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
-from matplotlib.figure import Figure
-from matplotlib.axes import Axes
-
-from enthought.traits.api import Any, Instance
-from enthought.traits.ui.qt4.editor import Editor
-from enthought.traits.ui.qt4.basic_editor_factory import BasicEditorFactory
-
-from enthought.pyface.action.api import ActionController
-
-from enthought.traits.ui.menu import \
-    Menu, Action, Separator, OKCancelButtons, OKButton
-
-from matplotlib.backend_bases import Event as MPLEvent
-
-class _MPLFigureEditor(Editor):
-    """ Snagged from Gael's tutorial """
-
-    scrollable  = True
-    mpl_control = Instance(FigureCanvas)
-
-    def init(self, parent):
-        self.control = self._create_canvas(parent)
-        self.set_tooltip()
-
-    def update_editor(self):
-        pass
-
-    def _create_canvas(self, parent):
-        """ Create the MPL canvas. """
-        # The panel lets us add additional controls.
-        panel = wx.Panel(parent, -1)
-        sizer = wx.BoxSizer(wx.VERTICAL)
-        panel.SetSizer(sizer)
-        # matplotlib commands to create a canvas
-        self.mpl_control = FigureCanvas(panel, -1, self.value)
-        sizer.Add(self.mpl_control, 1, wx.LEFT | wx.TOP | wx.GROW | wx.SHAPED)
-        self.value.canvas.SetMinSize((10,8))
-        return panel
-
-class MPLFigureEditor(BasicEditorFactory):
-    klass = _MPLFigureEditor
-
-class MPLAction(Action):
-    event = Instance(MPLEvent)
-
-class _MPLVMPlotEditor(_MPLFigureEditor, ActionController):
-
-    def _create_canvas(self, parent):
-        panel = _MPLFigureEditor._create_canvas(self, parent)
-        self.mpl_control.mpl_connect("button_press_event", self.on_click)
-        return panel
-
-    def on_click(self, event):
-        if not event.inaxes: return
-        if event.button == 3:
-            my_menu = Menu(MPLAction(name="Recenter", action="object.recenter",
-                                     event=event),
-                           MPLAction(name="Yo!", action="object.do_something",
-                                     event=event))
-            wxmenu = my_menu.create_menu(self.mpl_control, self)
-            self.mpl_control.PopupMenuXY(wxmenu)
-
-    def perform ( self, action ):
-        """
-        This is largely taken/modified from the TreeEditor _perform method.
-        """
-        object            = self.object
-        method_name       = action.action
-        info              = self.ui.info
-        handler           = self.ui.handler
-        event             = action.event
-
-        if method_name.find( '.' ) >= 0:
-            if method_name.find( '(' ) < 0:
-                method_name += '(event)'
-            try:
-                eval( method_name, globals(),
-                      { 'object':  object,
-                        'editor':  self,
-                        'info':    info,
-                        'event':   event,
-                        'handler': handler } )
-            except:
-                # fixme: Should the exception be logged somewhere?
-                print sys.exc_info()
-                
-            return
-
-        method = getattr( handler, method_name, None )
-        if method is not None:
-            method( info, object )
-            return
-
-        if action.on_perform is not None:
-            action.on_perform( object )
-
-class MPLVMPlotEditor(BasicEditorFactory):
-    klass = _MPLVMPlotEditor
-


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/basic_repl.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,140 @@
+"""
+A read-eval-print-loop.  This code was released in the CherryPy project
+initially, but has been heavily modified and again re-released in compliance
+with the terms of its original license.
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import codeop
+import inspect
+import re
+import json
+import sys
+import traceback
+from cStringIO import StringIO
+
+class ProgrammaticREPL(object):
+    
+    def __init__(self, locals=None):
+        self.locals = {}
+        if locals:
+            self.locals.update(locals)
+        self.buffer = []
+        # Nominally at this point we could populate our namespace with widgets
+        # or other useful functions.  We aren't really ready for that just yet.
+    
+    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)


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/bottle.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,2095 @@
+# -*- coding: utf-8 -*-
+"""
+Bottle is a fast and simple micro-framework for small web applications. It
+offers request dispatching (Routes) with url parameter support, templates,
+a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and
+template engines - all in a single file and with no dependencies other than the
+Python Standard Library.
+
+Homepage and documentation: http://bottle.paws.de/
+
+Copyright (c) 2010, Marcel Hellkamp.
+License: MIT (see LICENSE.txt for details)
+"""
+
+from __future__ import with_statement
+
+__author__ = 'Marcel Hellkamp'
+__version__ = '0.9.dev'
+__license__ = 'MIT'
+
+import base64
+import cgi
+import email.utils
+import functools
+import hmac
+import httplib
+import inspect
+import itertools
+import mimetypes
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import thread
+import threading
+import time
+import tokenize
+import warnings
+
+from Cookie import SimpleCookie
+from tempfile import TemporaryFile
+from traceback import format_exc
+from urllib import quote as urlquote
+from urlparse import urlunsplit, urljoin
+
+try: from collections import MutableMapping as DictMixin
+except ImportError: # pragma: no cover
+    from UserDict import DictMixin
+
+try: from urlparse import parse_qs
+except ImportError: # pragma: no cover
+    from cgi import parse_qs
+
+try: import cPickle as pickle
+except ImportError: # pragma: no cover
+    import pickle
+
+try: from json import dumps as json_dumps
+except ImportError: # pragma: no cover
+    try: from simplejson import dumps as json_dumps
+    except ImportError: # pragma: no cover
+        try: from django.utils.simplejson import dumps as json_dumps
+        except ImportError: # pragma: no cover
+            json_dumps = None
+
+if sys.version_info >= (3,0,0): # pragma: no cover
+    # See Request.POST
+    from io import BytesIO
+    from io import TextIOWrapper
+    class NCTextIOWrapper(TextIOWrapper):
+        ''' Garbage collecting an io.TextIOWrapper(buffer) instance closes the
+            wrapped buffer. This subclass keeps it open. '''
+        def close(self): pass
+    StringType = bytes
+    def touni(x, enc='utf8'):
+        """ Convert anything to unicode """
+        return str(x, encoding=enc) if isinstance(x, bytes) else str(x)
+else:
+    from StringIO import StringIO as BytesIO
+    from types import StringType
+    NCTextIOWrapper = None
+    def touni(x, enc='utf8'):
+        """ Convert anything to unicode """
+        return x if isinstance(x, unicode) else unicode(str(x), encoding=enc)
+
+def tob(data, enc='utf8'):
+    """ Convert anything to bytes """
+    return data.encode(enc) if isinstance(data, unicode) else StringType(data)
+
+# Convert strings and unicode to native strings
+if sys.version_info >= (3,0,0):
+    tonat = touni
+else:
+    tonat = tob
+tonat.__doc__ = """ Convert anything to native strings """
+
+
+# Background compatibility
+def depr(message, critical=False):
+    if critical: raise DeprecationWarning(message)
+    warnings.warn(message, DeprecationWarning, stacklevel=3)
+
+# Small helpers
+def makelist(data):
+    if isinstance(data, (tuple, list, set, dict)): return list(data)
+    elif data: return [data]
+    else: return []
+
+
+
+
+
+
+###############################################################################
+# Exceptions and Events ########################################################
+###############################################################################
+
+class BottleException(Exception):
+    """ A base class for exceptions used by bottle. """
+    pass
+
+
+class HTTPResponse(BottleException):
+    """ Used to break execution and immediately finish the response """
+    def __init__(self, output='', status=200, header=None):
+        super(BottleException, self).__init__("HTTP Response %d" % status)
+        self.status = int(status)
+        self.output = output
+        self.headers = HeaderDict(header) if header else None
+
+    def apply(self, response):
+        if self.headers:
+            for key, value in self.headers.iterallitems():
+                response.headers[key] = value
+        response.status = self.status
+
+
+class HTTPError(HTTPResponse):
+    """ Used to generate an error page """
+    def __init__(self, code=500, output='Unknown Error', exception=None, traceback=None, header=None):
+        super(HTTPError, self).__init__(output, code, header)
+        self.exception = exception
+        self.traceback = traceback
+
+    def __repr__(self):
+        return ''.join(ERROR_PAGE_TEMPLATE.render(e=self))
+
+
+
+
+
+
+###############################################################################
+# Routing ######################################################################
+###############################################################################
+
+class RouteError(BottleException):
+    """ This is a base class for all routing related exceptions """
+
+
+class RouteSyntaxError(RouteError):
+    """ The route parser found something not supported by this router """
+
+
+class RouteBuildError(RouteError):
+    """ The route could not been built """
+
+
+class Route(object):
+    ''' Represents a single route and can parse the dynamic route syntax '''
+    syntax = re.compile(r'(?<!\\):([a-zA-Z_]+)?(?:#(.*?)#)?')
+    default = '[^/]+'
+
+    def __init__(self, route, target=None, name=None, static=False):
+        """ Create a Route. The route string may contain `:key`,
+            `:key#regexp#` or `:#regexp#` tokens for each dynamic part of the
+            route. These can be escaped with a backslash in front of the `:`
+            and are completely ignored if static is true. A name may be used
+            to refer to this route later (depends on Router)
+        """
+        self.route = route.replace('\\:',':')
+        self.target = target
+        self.name = name
+        self.realroute = route.replace(':','\\:') if static else route
+        self.tokens = self.syntax.split(self.realroute)
+
+    def group_re(self):
+        ''' Return a regexp pattern with named groups '''
+        out = ''
+        for i, part in enumerate(self.tokens):
+            if i%3 == 0:   out += re.escape(part.replace('\:',':'))
+            elif i%3 == 1: out += '(?P<%s>' % part if part else '(?:'
+            else:          out += '%s)' % (part or self.default)
+        return out
+        
+    def flat_re(self):
+        ''' Return a regexp pattern with non-grouping parentheses '''
+        rf = lambda m: m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:'
+        return re.sub(r'(\\*)(\(\?P<[^>]*>|\((?!\?))', rf, self.group_re())
+
+    def format_str(self):
+        ''' Return a format string with named fields. '''
+        out, c = '', 0
+        for i, part in enumerate(self.tokens):
+            if i%3 == 0:  out += part.replace('\\:',':').replace('%','%%')
+            elif i%3 == 1:
+                if not part: part = 'anon%d' % c; c+=1
+                out += '%%(%s)s' % part
+        return out
+
+    @property
+    def static(self):
+        return len(self.tokens) == 1
+
+    def __repr__(self):
+        return "<Route(%s) />" % repr(self.realroute)
+
+    def __eq__(self, other):
+        return (self.realroute) == (other.realroute)
+
+
+class Router(object):
+    ''' A route associates a string (e.g. URL) with an object (e.g. function)
+        Some dynamic routes may extract parts of the string and provide them as
+        a dictionary. This router matches a string against multiple routes and
+        returns the associated object along with the extracted data.
+    '''
+
+    def __init__(self):
+        self.routes  = []  # List of all installed routes
+        self.named   = {}  # Cache for named routes and their format strings
+        self.static  = {}  # Cache for static routes
+        self.dynamic = []  # Search structure for dynamic routes
+        self.compiled = False
+
+    def add(self, route, target=None, **ka):
+        """ Add a route->target pair or a :class:`Route` object to the Router.
+            Return the Route object. See :class:`Route` for details.
+        """
+        if not isinstance(route, Route):
+            route = Route(route, target, **ka)
+        if self.get_route(route):
+            return RouteError('Route %s is not uniqe.' % route)
+        self.routes.append(route)
+        self.compiled, self.named, self.static, self.dynamic = False, {}, {}, []
+        return route
+
+    def get_route(self, route, target=None, **ka):
+        ''' Get a route from the router by specifying either the same
+            parameters as in :meth:`add` or comparing to an instance of
+            :class:`Route`. Note that not all parameters are considered by the
+            compare function. '''
+        if not isinstance(route, Route):
+            route = Route(route, **ka)
+        for known in self.routes:
+            if route == known:
+                return known
+        return None
+
+    def match(self, uri):
+        ''' Match an URI and return a (target, urlargs) tuple '''
+        if uri in self.static:
+            return self.static[uri], {}
+        for combined, subroutes in self.dynamic:
+            match = combined.match(uri)
+            if not match: continue
+            target, args_re = subroutes[match.lastindex - 1]
+            args = args_re.match(uri).groupdict() if args_re else {}
+            return target, args
+        if not self.compiled: # Late check to reduce overhead on hits
+            self.compile() # Compile and try again.
+            return self.match(uri)
+        return None, {}
+
+    def build(self, _name, **args):
+        ''' Build an URI out of a named route and values for the wildcards. '''
+        try:
+            return self.named[_name] % args
+        except KeyError:
+            if not self.compiled: # Late check to reduce overhead on hits
+                self.compile() # Compile and try again.
+                return self.build(_name, **args)
+            raise RouteBuildError("No route found with name '%s'." % _name)
+
+    def compile(self):
+        ''' Build the search structures. Call this before actually using the
+            router.'''
+        self.named = {}
+        self.static = {}
+        self.dynamic = []
+        for route in self.routes:
+            if route.name:
+                self.named[route.name] = route.format_str()
+            if route.static:
+                self.static[route.route] = route.target
+                continue
+            gpatt = route.group_re()
+            fpatt = route.flat_re()
+            try:
+                gregexp = re.compile('^(%s)$' % gpatt) if '(?P' in gpatt else None
+                combined = '%s|(^%s$)' % (self.dynamic[-1][0].pattern, fpatt)
+                self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1])
+                self.dynamic[-1][1].append((route.target, gregexp))
+            except (AssertionError, IndexError), e: # AssertionError: Too many groups
+                self.dynamic.append((re.compile('(^%s$)'%fpatt),
+                                    [(route.target, gregexp)]))
+            except re.error, e:
+                raise RouteSyntaxError("Could not add Route: %s (%s)" % (route, e))
+        self.compiled = True
+
+    def __eq__(self, other):
+        return self.routes == other.routes
+
+
+
+
+
+
+###############################################################################
+# Application Object ###########################################################
+###############################################################################
+
+class Bottle(object):
+    """ WSGI application """
+
+    def __init__(self, catchall=True, autojson=True, config=None):
+        """ Create a new bottle instance.
+            You usually don't do that. Use `bottle.app.push()` instead.
+        """
+        self.routes = Router()
+        self.mounts = {}
+        self.error_handler = {}
+        self.catchall = catchall
+        self.config = config or {}
+        self.serve = True
+        self.castfilter = []
+        if autojson and json_dumps:
+            self.add_filter(dict, dict2json)
+        self.hooks = {'before_request': [], 'after_request': []}
+
+    def optimize(self, *a, **ka):
+        depr("Bottle.optimize() is obsolete.")
+
+    def mount(self, app, script_path):
+        ''' Mount a Bottle application to a specific URL prefix '''
+        if not isinstance(app, Bottle):
+            raise TypeError('Only Bottle instances are supported for now.')
+        script_path = '/'.join(filter(None, script_path.split('/')))
+        path_depth = script_path.count('/') + 1
+        if not script_path:
+            raise TypeError('Empty script_path. Perhaps you want a merge()?')
+        for other in self.mounts:
+            if other.startswith(script_path):
+                raise TypeError('Conflict with existing mount: %s' % other)
+        @self.route('/%s/:#.*#' % script_path, method="ANY")
+        def mountpoint():
+            request.path_shift(path_depth)
+            return app.handle(request.path, request.method)
+        self.mounts[script_path] = app
+
+    def add_filter(self, ftype, func):
+        ''' Register a new output filter. Whenever bottle hits a handler output
+            matching `ftype`, `func` is applied to it. '''
+        if not isinstance(ftype, type):
+            raise TypeError("Expected type object, got %s" % type(ftype))
+        self.castfilter = [(t, f) for (t, f) in self.castfilter if t != ftype]
+        self.castfilter.append((ftype, func))
+        self.castfilter.sort()
+
+    def match_url(self, path, method='GET'):
+        """ Find a callback bound to a path and a specific HTTP method.
+            Return (callback, param) tuple or raise HTTPError.
+            method: HEAD falls back to GET. All methods fall back to ANY.
+        """
+        path, method = path.strip().lstrip('/'), method.upper()
+        callbacks, args = self.routes.match(path)
+        if not callbacks:
+            raise HTTPError(404, "Not found: " + path)
+        if method in callbacks:
+            return callbacks[method], args
+        if method == 'HEAD' and 'GET' in callbacks:
+            return callbacks['GET'], args
+        if 'ANY' in callbacks:
+            return callbacks['ANY'], args
+        allow = [m for m in callbacks if m != 'ANY']
+        if 'GET' in allow and 'HEAD' not in allow:
+            allow.append('HEAD')
+        raise HTTPError(405, "Method not allowed.",
+                        header=[('Allow',",".join(allow))])
+
+    def get_url(self, routename, **kargs):
+        """ Return a string that matches a named route """
+        scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/'
+        location = self.routes.build(routename, **kargs).lstrip('/')
+        return urljoin(urljoin('/', scriptname), location)
+
+    def route(self, path=None, method='GET', no_hooks=False, decorate=None,
+              template=None, template_opts={}, callback=None, **kargs):
+        """ Decorator: Bind a callback function to a request path.
+
+            :param path: The request path or a list of paths to listen to. See 
+              :class:`Router` for syntax details. If no path is specified, it
+              is automatically generated from the callback signature. See
+              :func:`yieldroutes` for details.
+            :param method: The HTTP method (POST, GET, ...) or a list of
+              methods to listen to. (default: GET)
+            :param decorate: A decorator or a list of decorators. These are
+              applied to the callback in reverse order.
+            :param no_hooks: If true, application hooks are not triggered
+              by this route. (default: False)
+            :param template: The template to use for this callback.
+              (default: no template)
+            :param template_opts: A dict with additional template parameters.
+            :param static: If true, all paths are static even if they contain
+              dynamic syntax tokens. (default: False)
+            :param name: The name for this route. (default: None)
+            :param callback: If set, the route decorator is directly applied
+              to the callback and the callback is returned instead. This
+              equals ``Bottle.route(...)(callback)``.
+        """
+        # @route can be used without any parameters
+        if callable(path): path, callback = None, path
+        # Build up the list of decorators
+        decorators = makelist(decorate)
+        if template:     decorators.insert(0, view(template, **template_opts))
+        if not no_hooks: decorators.append(self._add_hook_wrapper)
+        def wrapper(func):
+            callback = func
+            for decorator in reversed(decorators):
+                callback = decorator(callback)
+            functools.update_wrapper(callback, func)
+            for route in makelist(path) or yieldroutes(func):
+                for meth in makelist(method):
+                    route = route.strip().lstrip('/')
+                    meth = meth.strip().upper()
+                    old = self.routes.get_route(route, **kargs)
+                    if old:
+                        old.target[meth] = callback
+                    else:
+                        self.routes.add(route, {meth: callback}, **kargs)
+            return func
+        return wrapper(callback) if callback else wrapper
+
+    def _add_hook_wrapper(self, func):
+        ''' Add hooks to a callable. See #84 '''
+        @functools.wraps(func)
+        def wrapper(*a, **ka):
+            for hook in self.hooks['before_request']: hook()
+            response.output = func(*a, **ka)
+            for hook in self.hooks['after_request']: hook()
+            return response.output
+        return wrapper
+
+    def get(self, path=None, method='GET', **kargs):
+        """ Decorator: Bind a function to a GET request path.
+            See :meth:'route' for details. """
+        return self.route(path, method, **kargs)
+
+    def post(self, path=None, method='POST', **kargs):
+        """ Decorator: Bind a function to a POST request path.
+            See :meth:'route' for details. """
+        return self.route(path, method, **kargs)
+
+    def put(self, path=None, method='PUT', **kargs):
+        """ Decorator: Bind a function to a PUT request path.
+            See :meth:'route' for details. """
+        return self.route(path, method, **kargs)
+
+    def delete(self, path=None, method='DELETE', **kargs):
+        """ Decorator: Bind a function to a DELETE request path.
+            See :meth:'route' for details. """
+        return self.route(path, method, **kargs)
+
+    def error(self, code=500):
+        """ Decorator: Register an output handler for a HTTP error code"""
+        def wrapper(handler):
+            self.error_handler[int(code)] = handler
+            return handler
+        return wrapper
+
+    def hook(self, name):
+        """ Return a decorator that adds a callback to the specified hook. """
+        def wrapper(func):
+            self.add_hook(name, func)
+            return func
+        return wrapper
+
+    def add_hook(self, name, func):
+        ''' Add a callback from a hook. '''
+        if name not in self.hooks:
+            raise ValueError("Unknown hook name %s" % name)
+        if name in ('after_request'):
+            self.hooks[name].insert(0, func)
+        else:
+            self.hooks[name].append(func)
+
+    def remove_hook(self, name, func):
+        ''' Remove a callback from a hook. '''
+        if name not in self.hooks:
+            raise ValueError("Unknown hook name %s" % name)
+        self.hooks[name].remove(func)
+
+    def handle(self, url, method):
+        """ Execute the handler bound to the specified url and method and return
+        its output. If catchall is true, exceptions are catched and returned as
+        HTTPError(500) objects. """
+        if not self.serve:
+            return HTTPError(503, "Server stopped")
+        try:
+            handler, args = self.match_url(url, method)
+            return handler(**args)
+        except HTTPResponse, e:
+            return e
+        except Exception, e:
+            if isinstance(e, (KeyboardInterrupt, SystemExit, MemoryError))\
+            or not self.catchall:
+                raise
+            return HTTPError(500, 'Unhandled exception', e, format_exc(10))
+
+    def _cast(self, out, request, response, peek=None):
+        """ Try to convert the parameter into something WSGI compatible and set
+        correct HTTP headers when possible.
+        Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like,
+        iterable of strings and iterable of unicodes
+        """
+        # Filtered types (recursive, because they may return anything)
+        for testtype, filterfunc in self.castfilter:
+            if isinstance(out, testtype):
+                return self._cast(filterfunc(out), request, response)
+
+        # Empty output is done here
+        if not out:
+            response.headers['Content-Length'] = 0
+            return []
+        # Join lists of byte or unicode strings. Mixed lists are NOT supported
+        if isinstance(out, (tuple, list))\
+        and isinstance(out[0], (StringType, unicode)):
+            out = out[0][0:0].join(out) # b'abc'[0:0] -> b''
+        # Encode unicode strings
+        if isinstance(out, unicode):
+            out = out.encode(response.charset)
+        # Byte Strings are just returned
+        if isinstance(out, StringType):
+            response.headers['Content-Length'] = str(len(out))
+            return [out]
+        # HTTPError or HTTPException (recursive, because they may wrap anything)
+        if isinstance(out, HTTPError):
+            out.apply(response)
+            return self._cast(self.error_handler.get(out.status, repr)(out), request, response)
+        if isinstance(out, HTTPResponse):
+            out.apply(response)
+            return self._cast(out.output, request, response)
+
+        # File-like objects.
+        if hasattr(out, 'read'):
+            if 'wsgi.file_wrapper' in request.environ:
+                return request.environ['wsgi.file_wrapper'](out)
+            elif hasattr(out, 'close') or not hasattr(out, '__iter__'):
+                return WSGIFileWrapper(out)
+
+        # Handle Iterables. We peek into them to detect their inner type.
+        try:
+            out = iter(out)
+            first = out.next()
+            while not first:
+                first = out.next()
+        except StopIteration:
+            return self._cast('', request, response)
+        except HTTPResponse, e:
+            first = e
+        except Exception, e:
+            first = HTTPError(500, 'Unhandled exception', e, format_exc(10))
+            if isinstance(e, (KeyboardInterrupt, SystemExit, MemoryError))\
+            or not self.catchall:
+                raise
+        # These are the inner types allowed in iterator or generator objects.
+        if isinstance(first, HTTPResponse):
+            return self._cast(first, request, response)
+        if isinstance(first, StringType):
+            return itertools.chain([first], out)
+        if isinstance(first, unicode):
+            return itertools.imap(lambda x: x.encode(response.charset),
+                                  itertools.chain([first], out))
+        return self._cast(HTTPError(500, 'Unsupported response type: %s'\
+                                         % type(first)), request, response)
+
+    def wsgi(self, environ, start_response):
+        """ The bottle WSGI-interface. """
+        try:
+            environ['bottle.app'] = self
+            request.bind(environ)
+            response.bind()
+            out = self.handle(request.path, request.method)
+            out = self._cast(out, request, response)
+            # rfc2616 section 4.3
+            if response.status in (100, 101, 204, 304) or request.method == 'HEAD':
+                if hasattr(out, 'close'): out.close()
+                out = []
+            status = '%d %s' % (response.status, HTTP_CODES[response.status])
+            start_response(status, response.headerlist)
+            return out
+        except (KeyboardInterrupt, SystemExit, MemoryError):
+            raise
+        except Exception, e:
+            if not self.catchall: raise
+            err = '<h1>Critical error while processing request: %s</h1>' \
+                  % environ.get('PATH_INFO', '/')
+            if DEBUG:
+                err += '<h2>Error:</h2>\n<pre>%s</pre>\n' % repr(e)
+                err += '<h2>Traceback:</h2>\n<pre>%s</pre>\n' % format_exc(10)
+            environ['wsgi.errors'].write(err) #TODO: wsgi.error should not get html
+            start_response('500 INTERNAL SERVER ERROR', [('Content-Type', 'text/html')])
+            return [tob(err)]
+        
+    def __call__(self, environ, start_response):
+        return self.wsgi(environ, start_response)
+
+
+
+
+
+
+###############################################################################
+# HTTP and WSGI Tools ##########################################################
+###############################################################################
+
+class Request(threading.local, DictMixin):
+    """ Represents a single HTTP request using thread-local attributes.
+        The Request object wraps a WSGI environment and can be used as such.
+    """
+    def __init__(self, environ=None):
+        """ Create a new Request instance.
+        
+            You usually don't do this but use the global `bottle.request`
+            instance instead.
+        """
+        self.bind(environ or {},)
+
+    def bind(self, environ):
+        """ Bind a new WSGI environment.
+            
+            This is done automatically for the global `bottle.request`
+            instance on every request.
+        """
+        self.environ = environ
+        # These attributes are used anyway, so it is ok to compute them here
+        self.path = '/' + environ.get('PATH_INFO', '/').lstrip('/')
+        self.method = environ.get('REQUEST_METHOD', 'GET').upper()
+
+    @property
+    def _environ(self):
+        depr("Request._environ renamed to Request.environ")
+        return self.environ
+
+    def copy(self):
+        ''' Returns a copy of self '''
+        return Request(self.environ.copy())
+
+    def path_shift(self, shift=1):
+        ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
+
+          :param shift: The number of path fragments to shift. May be negative to
+            change the shift direction. (default: 1)
+        '''
+        script_name = self.environ.get('SCRIPT_NAME','/')
+        self['SCRIPT_NAME'], self.path = path_shift(script_name, self.path, shift)
+        self['PATH_INFO'] = self.path
+
+    def __getitem__(self, key): return self.environ[key]
+    def __delitem__(self, key): self[key] = ""; del(self.environ[key])
+    def __iter__(self): return iter(self.environ)
+    def __len__(self): return len(self.environ)
+    def keys(self): return self.environ.keys()
+    def __setitem__(self, key, value):
+        """ Shortcut for Request.environ.__setitem__ """
+        self.environ[key] = value
+        todelete = []
+        if key in ('PATH_INFO','REQUEST_METHOD'):
+            self.bind(self.environ)
+        elif key == 'wsgi.input': todelete = ('body','forms','files','params')
+        elif key == 'QUERY_STRING': todelete = ('get','params')
+        elif key.startswith('HTTP_'): todelete = ('headers', 'cookies')
+        for key in todelete:
+            if 'bottle.' + key in self.environ:
+                del self.environ['bottle.' + key]
+
+    @property
+    def query_string(self):
+        """ The content of the QUERY_STRING environment variable. """
+        return self.environ.get('QUERY_STRING', '')
+
+    @property
+    def fullpath(self):
+        """ Request path including SCRIPT_NAME (if present) """
+        return self.environ.get('SCRIPT_NAME', '').rstrip('/') + self.path
+
+    @property
+    def url(self):
+        """ Full URL as requested by the client (computed).
+
+            This value is constructed out of different environment variables
+            and includes scheme, host, port, scriptname, path and query string. 
+        """
+        scheme = self.environ.get('wsgi.url_scheme', 'http')
+        host   = self.environ.get('HTTP_X_FORWARDED_HOST', self.environ.get('HTTP_HOST', None))
+        if not host:
+            host = self.environ.get('SERVER_NAME')
+            port = self.environ.get('SERVER_PORT', '80')
+            if scheme + port not in ('https443', 'http80'):
+                host += ':' + port
+        parts = (scheme, host, urlquote(self.fullpath), self.query_string, '')
+        return urlunsplit(parts)
+
+    @property
+    def content_length(self):
+        """ Content-Length header as an integer, -1 if not specified """
+        return int(self.environ.get('CONTENT_LENGTH','') or -1)
+
+    @property
+    def header(self):
+        depr("The Request.header property was renamed to Request.headers")
+        return self.headers
+
+    @property
+    def headers(self):
+        ''' :class:`WSGIHeaderDict` filled with request headers. '''
+        if 'bottle.headers' not in self.environ:
+            self.environ['bottle.headers'] = WSGIHeaderDict(self.environ)
+        return self.environ['bottle.headers']
+
+    @property
+    def GET(self):
+        """ The QUERY_STRING parsed into a MultiDict.
+
+            Keys and values are strings. Multiple values per key are possible.
+            See MultiDict for details.
+        """
+        if 'bottle.get' not in self.environ:
+            data = parse_qs(self.query_string, keep_blank_values=True)
+            get = self.environ['bottle.get'] = MultiDict()
+            for key, values in data.iteritems():
+                for value in values:
+                    get[key] = value
+        return self.environ['bottle.get']
+
+    @property
+    def POST(self):
+        """ Property: The HTTP POST body parsed into a MultiDict.
+
+            This supports url-encoded and multipart POST requests. Multipart
+            is commonly used for file uploads and may result in some of the
+            values being cgi.FieldStorage objects instead of strings.
+
+            Multiple values per key are possible. See MultiDict for details.
+        """
+        if 'bottle.post' not in self.environ:
+            self.environ['bottle.post'] = MultiDict()
+            self.environ['bottle.forms'] = MultiDict()
+            self.environ['bottle.files'] = MultiDict()
+            safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi
+            for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):
+                if key in self.environ: safe_env[key] = self.environ[key]
+            if NCTextIOWrapper:
+                fb = NCTextIOWrapper(self.body, encoding='ISO-8859-1', newline='\n')
+                # TODO: Content-Length may be wrong now. Does cgi.FieldStorage
+                # use it at all? I think not, because all tests pass.
+            else:
+                fb = self.body
+            data = cgi.FieldStorage(fp=fb, environ=safe_env, keep_blank_values=True)
+            for item in data.list or []:
+                if item.filename:
+                    self.environ['bottle.post'][item.name] = item
+                    self.environ['bottle.files'][item.name] = item
+                else:
+                    self.environ['bottle.post'][item.name] = item.value
+                    self.environ['bottle.forms'][item.name] = item.value
+        return self.environ['bottle.post']
+
+    @property
+    def forms(self):
+        """ Property: HTTP POST form data parsed into a MultiDict. """
+        if 'bottle.forms' not in self.environ: self.POST
+        return self.environ['bottle.forms']
+
+    @property
+    def files(self):
+        """ Property: HTTP POST file uploads parsed into a MultiDict. """
+        if 'bottle.files' not in self.environ: self.POST
+        return self.environ['bottle.files']
+        
+    @property
+    def params(self):
+        """ A combined MultiDict with POST and GET parameters. """
+        if 'bottle.params' not in self.environ:
+            self.environ['bottle.params'] = MultiDict(self.GET)
+            self.environ['bottle.params'].update(dict(self.forms))
+        return self.environ['bottle.params']
+
+    @property
+    def body(self):
+        """ The HTTP request body as a seekable buffer object.
+        
+            This property returns a copy of the `wsgi.input` stream and should
+            be used instead of `environ['wsgi.input']`.
+         """
+        if 'bottle.body' not in self.environ:
+            maxread = max(0, self.content_length)
+            stream = self.environ['wsgi.input']
+            body = BytesIO() if maxread < MEMFILE_MAX else TemporaryFile(mode='w+b')
+            while maxread > 0:
+                part = stream.read(min(maxread, MEMFILE_MAX))
+                if not part: #TODO: Wrong content_length. Error? Do nothing?
+                    break
+                body.write(part)
+                maxread -= len(part)
+            self.environ['wsgi.input'] = body
+            self.environ['bottle.body'] = body
+        self.environ['bottle.body'].seek(0)
+        return self.environ['bottle.body']
+
+    @property
+    def auth(self): #TODO: Tests and docs. Add support for digest. namedtuple?
+        """ HTTP authorization data as a (user, passwd) tuple. (experimental)
+        
+            This implementation currently only supports basic auth and returns
+            None on errors.
+        """
+        return parse_auth(self.headers.get('Authorization',''))
+
+    @property
+    def COOKIES(self):
+        """ Cookie information parsed into a dictionary.
+        
+            Secure cookies are NOT decoded automatically. See
+            Request.get_cookie() for details.
+        """
+        if 'bottle.cookies' not in self.environ:
+            raw_dict = SimpleCookie(self.headers.get('Cookie',''))
+            self.environ['bottle.cookies'] = {}
+            for cookie in raw_dict.itervalues():
+                self.environ['bottle.cookies'][cookie.key] = cookie.value
+        return self.environ['bottle.cookies']
+
+    def get_cookie(self, key, secret=None):
+        """ Return the content of a cookie. To read a `Secure Cookies`, use the
+            same `secret` as used to create the cookie (see
+            :meth:`Response.set_cookie`). If anything goes wrong, None is
+            returned.
+        """
+        value = self.COOKIES.get(key)
+        if secret and value:
+            dec = cookie_decode(value, secret) # (key, value) tuple or None
+            return dec[1] if dec and dec[0] == key else None
+        return value or None
+
+    @property
+    def is_ajax(self):
+        ''' True if the request was generated using XMLHttpRequest '''
+        #TODO: write tests
+        return self.header.get('X-Requested-With') == 'XMLHttpRequest'
+
+
+
+class Response(threading.local):
+    """ Represents a single HTTP response using thread-local attributes.
+    """
+
+    def __init__(self):
+        self.bind()
+
+    def bind(self):
+        """ Resets the Response object to its factory defaults. """
+        self._COOKIES = None
+        self.status = 200
+        self.headers = HeaderDict()
+        self.content_type = 'text/html; charset=UTF-8'
+
+    @property
+    def header(self):
+        depr("Response.header renamed to Response.headers")
+        return self.headers
+
+    def copy(self):
+        ''' Returns a copy of self '''
+        copy = Response()
+        copy.status = self.status
+        copy.headers = self.headers.copy()
+        copy.content_type = self.content_type
+        return copy
+
+    def wsgiheader(self):
+        ''' Returns a wsgi conform list of header/value pairs. '''
+        for c in self.COOKIES.values():
+            if c.OutputString() not in self.headers.getall('Set-Cookie'):
+                self.headers.append('Set-Cookie', c.OutputString())
+        # rfc2616 section 10.2.3, 10.3.5
+        if self.status in (204, 304) and 'content-type' in self.headers:
+            del self.headers['content-type']
+        if self.status == 304:
+            for h in ('allow', 'content-encoding', 'content-language',
+                      'content-length', 'content-md5', 'content-range',
+                      'content-type', 'last-modified'): # + c-location, expires?
+                if h in self.headers:
+                     del self.headers[h]
+        return list(self.headers.iterallitems())
+    headerlist = property(wsgiheader)
+
+    @property
+    def charset(self):
+        """ Return the charset specified in the content-type header.
+        
+            This defaults to `UTF-8`.
+        """
+        if 'charset=' in self.content_type:
+            return self.content_type.split('charset=')[-1].split(';')[0].strip()
+        return 'UTF-8'
+
+    @property
+    def COOKIES(self):
+        """ A dict-like SimpleCookie instance. Use Response.set_cookie() instead. """
+        if not self._COOKIES:
+            self._COOKIES = SimpleCookie()
+        return self._COOKIES
+
+    def set_cookie(self, key, value, secret=None, **kargs):
+        ''' Add a cookie. If the `secret` parameter is set, this creates a
+            `Secure Cookie` (described below).
+
+            |param key| the name of the cookie.
+            |param value| the value of the cookie.
+            |param secret| required for secure cookies. (default: None)
+            |param max_age| maximum age in seconds. (default: None)
+            |param expires| a datetime object or UNIX timestamp. (defaut: None)
+            |param domain| the domain that is allowed to read the cookie.
+              (default: current domain)
+            |param path| limits the cookie to a given path (default: /)
+
+            If neither `expires` nor `max_age` are set (default), the cookie
+            lasts only as long as the browser is not closed.
+
+            Secure cookies may store any pickle-able object and are
+            cryptographically signed to prevent manipulation. Keep in mind that
+            cookies are limited to 4kb in most browsers.
+            
+            Warning: Secure cookies are not encrypted (the client can still see
+            the content) and not copy-protected (the client can restore an old
+            cookie). The main intention is to make pickling and unpickling
+            save, not to store secret information at client side.
+        '''
+        if secret:
+            value = touni(cookie_encode((key, value), secret))
+        elif not isinstance(value, basestring):
+            raise TypeError('Secret missing for non-string Cookie.')
+            
+        self.COOKIES[key] = value
+        for k, v in kargs.iteritems():
+            self.COOKIES[key][k.replace('_', '-')] = v
+
+    def delete_cookie(self, key, **kwargs):
+        ''' Delete a cookie. Be sure to use the same `domain` and `path`
+            parameters as used to create the cookie.
+        '''
+        kwargs['max_age'] = -1
+        kwargs['expires'] = 0
+        self.set_cookie(key, **kwargs)
+
+    def get_content_type(self):
+        """ Current 'Content-Type' header. """
+        return self.headers['Content-Type']
+
+    def set_content_type(self, value):
+        self.headers['Content-Type'] = value
+
+    content_type = property(get_content_type, set_content_type, None,
+                            get_content_type.__doc__)
+
+
+
+
+
+
+###############################################################################
+# Common Utilities #############################################################
+###############################################################################
+
+class MultiDict(DictMixin):
+    """ A dict that remembers old values for each key """
+    # collections.MutableMapping would be better for Python >= 2.6
+    def __init__(self, *a, **k):
+        self.dict = dict()
+        for k, v in dict(*a, **k).iteritems():
+            self[k] = v
+
+    def __len__(self): return len(self.dict)
+    def __iter__(self): return iter(self.dict)
+    def __contains__(self, key): return key in self.dict
+    def __delitem__(self, key): del self.dict[key]
+    def keys(self): return self.dict.keys()
+    def __getitem__(self, key): return self.get(key, KeyError, -1)
+    def __setitem__(self, key, value): self.append(key, value)
+
+    def append(self, key, value): self.dict.setdefault(key, []).append(value)
+    def replace(self, key, value): self.dict[key] = [value]
+    def getall(self, key): return self.dict.get(key) or []
+
+    def get(self, key, default=None, index=-1):
+        if key not in self.dict and default != KeyError:
+            return [default][index]
+        return self.dict[key][index]
+
+    def iterallitems(self):
+        for key, values in self.dict.iteritems():
+            for value in values:
+                yield key, value
+
+
+class HeaderDict(MultiDict):
+    """ Same as :class:`MultiDict`, but title()s the keys and overwrites by default. """
+    def __contains__(self, key): return MultiDict.__contains__(self, self.httpkey(key))
+    def __getitem__(self, key): return MultiDict.__getitem__(self, self.httpkey(key))
+    def __delitem__(self, key): return MultiDict.__delitem__(self, self.httpkey(key))
+    def __setitem__(self, key, value): self.replace(key, value)
+    def get(self, key, default=None, index=-1): return MultiDict.get(self, self.httpkey(key), default, index)
+    def append(self, key, value): return MultiDict.append(self, self.httpkey(key), str(value))
+    def replace(self, key, value): return MultiDict.replace(self, self.httpkey(key), str(value))
+    def getall(self, key): return MultiDict.getall(self, self.httpkey(key))
+    def httpkey(self, key): return str(key).replace('_','-').title()
+
+
+
+class WSGIHeaderDict(DictMixin):
+    ''' This dict-like class takes a WSGI environ dict and provides convenient
+        access to HTTP_* fields. Keys and values are stored as native strings
+        (bytes/unicode) based on the python version used (2/3) and keys are
+        case-insensitive. If the WSGI environment contains non-native strings,
+        these are de- or encoded using 'utf8' (default) or 'latin1' (fallback)
+        charset. To get the original value, use the .raw(key) method.
+
+        This is not a MultiDict because incoming headers are unique. The API
+        will remain stable even on WSGI spec changes, if possible.
+        '''
+
+    def __init__(self, environ):
+        self.cache = {}
+        self.environ = environ
+        for key, value in self.environ.iteritems():
+            key = tonat(key, 'latin1') # Headers are limited to ASCII anyway
+            if key.startswith('HTTP_'):
+                self[key[5:].replace('_','-')] = value
+
+    def __len__(self): return len(self.cache)
+    def keys(self): return self.cache.keys()
+    def __iter__(self): return iter(self.cache)
+    def __contains__(self, key): return key.title() in self.keys()
+    def __delitem__(self, key): del self.cache[key.title()]
+    def __getitem__(self, key): return self.cache[key.title()]
+    def __setitem__(self, key, value):
+        try:
+            self.cache[key.title()] = tonat(value, 'utf8')
+        except UnicodeError:
+            self.cache[key.title()] = tonat(value, 'latin1')
+
+    def raw(self, key, default=None):
+        ''' Return the raw WSGI header value for that key. '''
+        ekey = 'HTTP_%s' % key.replace('-','_').upper()
+        return self.environ.get(ekey, default)
+
+
+
+
+class AppStack(list):
+    """ A stack implementation. """
+
+    def __call__(self):
+        """ Return the current default app. """
+        return self[-1]
+
+    def push(self, value=None):
+        """ Add a new Bottle instance to the stack """
+        if not isinstance(value, Bottle):
+            value = Bottle()
+        self.append(value)
+        return value
+
+class WSGIFileWrapper(object):
+
+   def __init__(self, fp, buffer_size=1024*64):
+       self.fp, self.buffer_size = fp, buffer_size
+       for attr in ('fileno', 'close', 'read', 'readlines'):
+           if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
+
+   def __iter__(self):
+       read, buff = self.fp.read, self.buffer_size
+       while True:
+           part = read(buff)
+           if not part: break
+           yield part
+
+
+
+
+
+
+###############################################################################
+# Application Helper ###########################################################
+###############################################################################
+
+def dict2json(d):
+    response.content_type = 'application/json'
+    return json_dumps(d)
+
+
+def abort(code=500, text='Unknown Error: Application stopped.'):
+    """ Aborts execution and causes a HTTP error. """
+    raise HTTPError(code, text)
+
+
+def redirect(url, code=303):
+    """ Aborts execution and causes a 303 redirect """
+    scriptname = request.environ.get('SCRIPT_NAME', '').rstrip('/') + '/'
+    location = urljoin(request.url, urljoin(scriptname, url))
+    raise HTTPResponse("", status=code, header=dict(Location=location))
+
+
+def send_file(*a, **k): #BC 0.6.4
+    """ Raises the output of static_file(). (deprecated) """
+    raise static_file(*a, **k)
+
+
+def static_file(filename, root, guessmime=True, mimetype=None, download=False):
+    """ Opens a file in a safe way and returns a HTTPError object with status
+        code 200, 305, 401 or 404. Sets Content-Type, Content-Length and
+        Last-Modified header. Obeys If-Modified-Since header and HEAD requests.
+    """
+    root = os.path.abspath(root) + os.sep
+    filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
+    header = dict()
+
+    if not filename.startswith(root):
+        return HTTPError(403, "Access denied.")
+    if not os.path.exists(filename) or not os.path.isfile(filename):
+        return HTTPError(404, "File does not exist.")
+    if not os.access(filename, os.R_OK):
+        return HTTPError(403, "You do not have permission to access this file.")
+
+    if not mimetype and guessmime:
+        header['Content-Type'] = mimetypes.guess_type(filename)[0]
+    else:
+        header['Content-Type'] = mimetype if mimetype else 'text/plain'
+
+    if download == True:
+        download = os.path.basename(filename)
+    if download:
+        header['Content-Disposition'] = 'attachment; filename="%s"' % download
+
+    stats = os.stat(filename)
+    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
+    header['Last-Modified'] = lm
+    ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
+    if ims:
+        ims = ims.split(";")[0].strip() # IE sends "<date>; length=146"
+        ims = parse_date(ims)
+        if ims is not None and ims >= int(stats.st_mtime):
+            header['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
+            return HTTPResponse(status=304, header=header)
+    header['Content-Length'] = stats.st_size
+    if request.method == 'HEAD':
+        return HTTPResponse('', header=header)
+    else:
+        return HTTPResponse(open(filename, 'rb'), header=header)
+
+
+
+
+
+
+###############################################################################
+# HTTP Utilities and MISC (TODO) ###############################################
+###############################################################################
+
+def debug(mode=True):
+    """ Change the debug level.
+    There is only one debug level supported at the moment."""
+    global DEBUG
+    DEBUG = bool(mode)
+
+
+def parse_date(ims):
+    """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """
+    try:
+        ts = email.utils.parsedate_tz(ims)
+        return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone
+    except (TypeError, ValueError, IndexError):
+        return None
+
+
+def parse_auth(header):
+    """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None"""
+    try:
+        method, data = header.split(None, 1)
+        if method.lower() == 'basic':
+            name, pwd = base64.b64decode(data).split(':', 1)
+            return name, pwd
+    except (KeyError, ValueError, TypeError):
+        return None
+
+
+def cookie_encode(data, key):
+    ''' Encode and sign a pickle-able object. Return a (byte) string '''
+    msg = base64.b64encode(pickle.dumps(data, -1))
+    sig = base64.b64encode(hmac.new(key, msg).digest())
+    return tob('!') + sig + tob('?') + msg
+
+
+def cookie_decode(data, key):
+    ''' Verify and decode an encoded string. Return an object or None.'''
+    data = tob(data)
+    if cookie_is_encoded(data):
+        sig, msg = data.split(tob('?'), 1)
+        if sig[1:] == base64.b64encode(hmac.new(key, msg).digest()):
+            return pickle.loads(base64.b64decode(msg))
+    return None
+
+
+def cookie_is_encoded(data):
+    ''' Return True if the argument looks like a encoded cookie.'''
+    return bool(data.startswith(tob('!')) and tob('?') in data)
+
+
+def yieldroutes(func):
+    """ Return a generator for routes that match the signature (name, args) 
+    of the func parameter. This may yield more than one route if the function
+    takes optional keyword arguments. The output is best described by example::
+    
+        a()         -> '/a'
+        b(x, y)     -> '/b/:x/:y'
+        c(x, y=5)   -> '/c/:x' and '/c/:x/:y'
+        d(x=5, y=6) -> '/d' and '/d/:x' and '/d/:x/:y'
+    """
+    path = func.__name__.replace('__','/').lstrip('/')
+    spec = inspect.getargspec(func)
+    argc = len(spec[0]) - len(spec[3] or [])
+    path += ('/:%s' * argc) % tuple(spec[0][:argc])
+    yield path
+    for arg in spec[0][argc:]:
+        path += '/:%s' % arg
+        yield path
+
+def path_shift(script_name, path_info, shift=1):
+    ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
+
+        :return: The modified paths.
+        :param script_name: The SCRIPT_NAME path.
+        :param script_name: The PATH_INFO path.
+        :param shift: The number of path fragments to shift. May be negative to
+          change the shift direction. (default: 1)
+    '''
+    if shift == 0: return script_name, path_info
+    pathlist = path_info.strip('/').split('/')
+    scriptlist = script_name.strip('/').split('/')
+    if pathlist and pathlist[0] == '': pathlist = []
+    if scriptlist and scriptlist[0] == '': scriptlist = []
+    if shift > 0 and shift <= len(pathlist):
+        moved = pathlist[:shift]
+        scriptlist = scriptlist + moved
+        pathlist = pathlist[shift:]
+    elif shift < 0 and shift >= -len(scriptlist):
+        moved = scriptlist[shift:]
+        pathlist = moved + pathlist
+        scriptlist = scriptlist[:shift]
+    else:
+        empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO'
+        raise AssertionError("Cannot shift. Nothing left from %s" % empty)
+    new_script_name = '/' + '/'.join(scriptlist)
+    new_path_info = '/' + '/'.join(pathlist)
+    if path_info.endswith('/') and pathlist: new_path_info += '/'
+    return new_script_name, new_path_info
+
+
+
+# Decorators
+#TODO: Replace default_app() with app()
+
+def validate(**vkargs):
+    """
+    Validates and manipulates keyword arguments by user defined callables.
+    Handles ValueError and missing arguments by raising HTTPError(403).
+    """
+    def decorator(func):
+        def wrapper(**kargs):
+            for key, value in vkargs.iteritems():
+                if key not in kargs:
+                    abort(403, 'Missing parameter: %s' % key)
+                try:
+                    kargs[key] = value(kargs[key])
+                except ValueError:
+                    abort(403, 'Wrong parameter format for: %s' % key)
+            return func(**kargs)
+        return wrapper
+    return decorator
+
+
+def make_default_app_wrapper(name):
+    ''' Return a callable that relays calls to the current default app. '''
+    @functools.wraps(getattr(Bottle, name))
+    def wrapper(*a, **ka):
+        return getattr(app(), name)(*a, **ka)
+    return wrapper
+
+for name in 'route get post put delete error mount hook'.split():
+    globals()[name] = make_default_app_wrapper(name)
+
+url = make_default_app_wrapper('get_url')
+
+
+def default():
+    depr("The default() decorator is deprecated. Use @error(404) instead.")
+    return error(404)
+
+
+
+
+
+
+###############################################################################
+# Server Adapter ###############################################################
+###############################################################################
+
+class ServerAdapter(object):
+    quiet = False
+    def __init__(self, host='127.0.0.1', port=8080, **kargs):
+        self.options = kargs
+        self.host = host
+        self.port = int(port)
+
+    def run(self, handler): # pragma: no cover
+        pass
+        
+    def __repr__(self):
+        args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()])
+        return "%s(%s)" % (self.__class__.__name__, args)
+
+
+class CGIServer(ServerAdapter):
+    quiet = True
+    def run(self, handler): # pragma: no cover
+        from wsgiref.handlers import CGIHandler
+        CGIHandler().run(handler) # Just ignore host and port here
+
+
+class FlupFCGIServer(ServerAdapter):
+    def run(self, handler): # pragma: no cover
+        import flup.server.fcgi
+        flup.server.fcgi.WSGIServer(handler, bindAddress=(self.host, self.port)).run()
+
+
+class WSGIRefServer(ServerAdapter):
+    def run(self, handler): # pragma: no cover
+        from wsgiref.simple_server import make_server, WSGIRequestHandler
+        if self.quiet:
+            class QuietHandler(WSGIRequestHandler):
+                def log_request(*args, **kw): pass
+            self.options['handler_class'] = QuietHandler
+        srv = make_server(self.host, self.port, handler, **self.options)
+        srv.serve_forever()
+
+
+class CherryPyServer(ServerAdapter):
+    def run(self, handler): # pragma: no cover
+        from cherrypy import wsgiserver
+        server = wsgiserver.CherryPyWSGIServer((self.host, self.port), handler)
+        server.start()
+
+
+class PasteServer(ServerAdapter):
+    def run(self, handler): # pragma: no cover
+        from paste import httpserver
+        if not self.quiet:
+            from paste.translogger import TransLogger
+            handler = TransLogger(handler)
+        httpserver.serve(handler, host=self.host, port=str(self.port),
+                         **self.options)
+                         
+class MeinheldServer(ServerAdapter):
+    def run(self, handler):
+        from meinheld import server
+        server.listen((self.host, self.port))
+        server.run(handler)
+
+class FapwsServer(ServerAdapter):
+    """
+    Extremely fast webserver using libev.
+    See http://william-os4y.livejournal.com/
+    """
+    def run(self, handler): # pragma: no cover
+        import fapws._evwsgi as evwsgi
+        from fapws import base, config
+        port = self.port
+        if float(config.SERVER_IDENT[-2:]) > 0.4:
+            # fapws3 silently changed its API in 0.5
+            port = str(port)
+        evwsgi.start(self.host, port)
+        # fapws3 never releases the GIL. Complain upstream. I tried. No luck.
+        if 'BOTTLE_CHILD' in os.environ and not self.quiet:
+            print "WARNING: Auto-reloading does not work with Fapws3."
+            print "         (Fapws3 breaks python thread support)"
+        evwsgi.set_base_module(base)
+        def app(environ, start_response):
+            environ['wsgi.multiprocess'] = False
+            return handler(environ, start_response)
+        evwsgi.wsgi_cb(('', app))
+        evwsgi.run()
+
+
+class TornadoServer(ServerAdapter):
+    """ Untested. As described here:
+        http://github.com/facebook/tornado/blob/master/tornado/wsgi.py#L187 """
+    def run(self, handler): # pragma: no cover
+        import tornado.wsgi
+        import tornado.httpserver
+        import tornado.ioloop
+        container = tornado.wsgi.WSGIContainer(handler)
+        server = tornado.httpserver.HTTPServer(container)
+        server.listen(port=self.port)
+        tornado.ioloop.IOLoop.instance().start()
+
+
+class AppEngineServer(ServerAdapter):
+    """ Untested. """
+    quiet = True
+    def run(self, handler):
+        from google.appengine.ext.webapp import util
+        util.run_wsgi_app(handler)
+
+
+class TwistedServer(ServerAdapter):
+    """ Untested. """
+    def run(self, handler):
+        from twisted.web import server, wsgi
+        from twisted.python.threadpool import ThreadPool
+        from twisted.internet import reactor
+        thread_pool = ThreadPool()
+        thread_pool.start()
+        reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
+        factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler))
+        reactor.listenTCP(self.port, factory, interface=self.host)
+        reactor.run()
+
+
+class DieselServer(ServerAdapter):
+    """ Untested. """
+    def run(self, handler):
+        from diesel.protocols.wsgi import WSGIApplication
+        app = WSGIApplication(handler, port=self.port)
+        app.run()
+
+
+class GeventServer(ServerAdapter):
+    """ Untested. """
+    def run(self, handler):
+        from gevent import wsgi
+        #from gevent.hub import getcurrent
+        #self.set_context_ident(getcurrent, weakref=True) # see contextlocal
+        wsgi.WSGIServer((self.host, self.port), handler).serve_forever()
+
+
+class GunicornServer(ServerAdapter):
+    """ Untested. """
+    def run(self, handler):
+        from gunicorn.arbiter import Arbiter
+        from gunicorn.config import Config
+        handler.cfg = Config({'bind': "%s:%d" % (self.host, self.port), 'workers': 4})
+        arbiter = Arbiter(handler)
+        arbiter.run()
+
+
+class EventletServer(ServerAdapter):
+    """ Untested """
+    def run(self, handler):
+        from eventlet import wsgi, listen
+        wsgi.server(listen((self.host, self.port)), handler)
+
+
+class RocketServer(ServerAdapter):
+    """ Untested. As requested in issue 63
+        http://github.com/defnull/bottle/issues/#issue/63 """
+    def run(self, handler):
+        from rocket import Rocket
+        server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler })
+        server.start()
+            
+        
+class AutoServer(ServerAdapter):
+    """ Untested. """
+    adapters = [PasteServer, CherryPyServer, TwistedServer, WSGIRefServer]
+    def run(self, handler):
+        for sa in self.adapters:
+            try:
+                return sa(self.host, self.port, **self.options).run(handler)
+            except ImportError:
+                pass
+
+
+server_names = {
+    'cgi': CGIServer,
+    'flup': FlupFCGIServer,
+    'wsgiref': WSGIRefServer,
+    'cherrypy': CherryPyServer,
+    'paste': PasteServer,
+    'fapws3': FapwsServer,
+    'tornado': TornadoServer,
+    'gae': AppEngineServer,
+    'twisted': TwistedServer,
+    'diesel': DieselServer,
+    'meinheld': MeinheldServer,
+    'gunicorn': GunicornServer,
+    'eventlet': EventletServer,
+    'gevent': GeventServer,
+    'rocket': RocketServer,
+    'auto': AutoServer,
+}
+
+
+
+
+
+
+###############################################################################
+# Application Control ##########################################################
+###############################################################################
+
+def load_app(target):
+    """ Load a bottle application based on a target string and return the app
+        object.
+        
+        The target should be a valid python import path
+        (e.g. mypackage.mymodule). The default application is returned.
+        If the target contains a colon (e.g. mypackage.mymodule:myapp) the
+        module variable specified after the colon is returned instead.
+    """
+    path, name = target.split(":", 1) if ':' in target else (target, None)
+    rv = None if name else app.push()
+    __import__(path)
+    module = sys.modules[path]
+    if rv and rv in app: app.remove(rv)
+    return rv if rv else getattr(module, target)
+
+
+def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
+        interval=1, reloader=False, quiet=False, **kargs):
+    """ Start a server instance. This method blocks until the server
+        terminates.
+
+        :param app: WSGI application or target string supported by
+               :func:`load_app`. (default: :func:`default_app`)
+        :param server: Server adapter to use. See :data:`server_names` dict
+               for valid names or pass a :class:`ServerAdapter` subclass.
+               (default: wsgiref)
+        :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
+               all interfaces including the external one. (default: 127.0.0.1)
+        :param host: Server port to bind to. Values below 1024 require root
+               privileges. (default: 8080)
+        :param reloader: Start auto-reloading server? (default: False)
+        :param interval: Auto-reloader interval in seconds (default: 1)
+        :param quiet: Suppress output to stdout and stderr? (default: False)
+        :param options: Options passed to the server adapter.
+     """
+    app = app or default_app()
+    if isinstance(app, basestring):
+        app = load_app(app)
+    if isinstance(server, basestring):
+        server = server_names.get(server)
+    if isinstance(server, type):
+        server = server(host=host, port=port, **kargs)
+    if not isinstance(server, ServerAdapter):
+        raise RuntimeError("Server must be a subclass of ServerAdapter")
+    server.quiet = server.quiet or quiet
+    if not server.quiet and not os.environ.get('BOTTLE_CHILD'):
+        print "Bottle server starting up (using %s)..." % repr(server)
+        print "Listening on http://%s:%d/" % (server.host, server.port)
+        print "Use Ctrl-C to quit."
+        print
+    try:
+        if reloader:
+            interval = min(interval, 1)
+            if os.environ.get('BOTTLE_CHILD'):
+                _reloader_child(server, app, interval)
+            else:
+                _reloader_observer(server, app, interval)
+        else:
+            server.run(app)
+    except KeyboardInterrupt:
+        pass
+    if not server.quiet and not os.environ.get('BOTTLE_CHILD'):
+        print "Shutting down..."
+
+
+class FileCheckerThread(threading.Thread):
+    ''' Thread that periodically checks for changed module files. '''
+
+    def __init__(self, lockfile, interval):
+        threading.Thread.__init__(self)
+        self.lockfile, self.interval = lockfile, interval
+        #1: lockfile to old; 2: lockfile missing
+        #3: module file changed; 5: external exit
+        self.status = 0
+
+    def run(self):
+        exists = os.path.exists
+        mtime = lambda path: os.stat(path).st_mtime
+        files = dict()
+        for module in sys.modules.values():
+            try:
+                path = inspect.getsourcefile(module)
+                if path and exists(path): files[path] = mtime(path)
+            except TypeError:
+                pass
+        while not self.status:
+            for path, lmtime in files.iteritems():
+                if not exists(path) or mtime(path) > lmtime:
+                    self.status = 3
+            if not exists(self.lockfile):
+                self.status = 2
+            elif mtime(self.lockfile) < time.time() - self.interval - 5:
+                self.status = 1
+            if not self.status:
+                time.sleep(self.interval)
+        if self.status != 5:
+            thread.interrupt_main()
+
+
+def _reloader_child(server, app, interval):
+    ''' Start the server and check for modified files in a background thread.
+        As soon as an update is detected, KeyboardInterrupt is thrown in
+        the main thread to exit the server loop. The process exists with status
+        code 3 to request a reload by the observer process. If the lockfile
+        is not modified in 2*interval second or missing, we assume that the
+        observer process died and exit with status code 1 or 2.
+    '''
+    lockfile = os.environ.get('BOTTLE_LOCKFILE')
+    bgcheck = FileCheckerThread(lockfile, interval)
+    try:
+        bgcheck.start()
+        server.run(app)
+    except KeyboardInterrupt:
+        pass
+    bgcheck.status, status = 5, bgcheck.status
+    bgcheck.join() # bgcheck.status == 5 --> silent exit
+    if status: sys.exit(status)
+
+
+def _reloader_observer(server, app, interval):
+    ''' Start a child process with identical commandline arguments and restart
+        it as long as it exists with status code 3. Also create a lockfile and
+        touch it (update mtime) every interval seconds.
+    '''
+    fd, lockfile = tempfile.mkstemp(prefix='bottle-reloader.', suffix='.lock')
+    os.close(fd) # We only need this file to exist. We never write to it
+    try:
+        while os.path.exists(lockfile):
+            args = [sys.executable] + sys.argv
+            environ = os.environ.copy()
+            environ['BOTTLE_CHILD'] = 'true'
+            environ['BOTTLE_LOCKFILE'] = lockfile
+            p = subprocess.Popen(args, env=environ)
+            while p.poll() is None: # Busy wait...
+                os.utime(lockfile, None) # I am alive!
+                time.sleep(interval)
+            if p.poll() != 3:
+                if os.path.exists(lockfile): os.unlink(lockfile)
+                sys.exit(p.poll())
+            elif not server.quiet:
+                print "Reloading server..."
+    except KeyboardInterrupt:
+        pass
+    if os.path.exists(lockfile): os.unlink(lockfile)
+
+
+
+
+
+
+###############################################################################
+# Template Adapters ############################################################
+###############################################################################
+
+class TemplateError(HTTPError):
+    def __init__(self, message):
+        HTTPError.__init__(self, 500, message)
+
+
+class BaseTemplate(object):
+    """ Base class and minimal API for template adapters """
+    extentions = ['tpl','html','thtml','stpl']
+    settings = {} #used in prepare()
+    defaults = {} #used in render()
+
+    def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings):
+        """ Create a new template.
+        If the source parameter (str or buffer) is missing, the name argument
+        is used to guess a template filename. Subclasses can assume that
+        self.source and/or self.filename are set. Both are strings.
+        The lookup, encoding and settings parameters are stored as instance
+        variables.
+        The lookup parameter stores a list containing directory paths.
+        The encoding parameter should be used to decode byte strings or files.
+        The settings parameter contains a dict for engine-specific settings.
+        """
+        self.name = name
+        self.source = source.read() if hasattr(source, 'read') else source
+        self.filename = source.filename if hasattr(source, 'filename') else None
+        self.lookup = map(os.path.abspath, lookup)
+        self.encoding = encoding
+        self.settings = self.settings.copy() # Copy from class variable
+        self.settings.update(settings) # Apply 
+        if not self.source and self.name:
+            self.filename = self.search(self.name, self.lookup)
+            if not self.filename:
+                raise TemplateError('Template %s not found.' % repr(name))
+        if not self.source and not self.filename:
+            raise TemplateError('No template specified.')
+        self.prepare(**self.settings)
+
+    @classmethod
+    def search(cls, name, lookup=[]):
+        """ Search name in all directories specified in lookup.
+        First without, then with common extensions. Return first hit. """
+        if os.path.isfile(name): return name
+        for spath in lookup:
+            fname = os.path.join(spath, name)
+            if os.path.isfile(fname):
+                return fname
+            for ext in cls.extentions:
+                if os.path.isfile('%s.%s' % (fname, ext)):
+                    return '%s.%s' % (fname, ext)
+
+    @classmethod
+    def global_config(cls, key, *args):
+        ''' This reads or sets the global settings stored in class.settings. '''
+        if args:
+            cls.settings[key] = args[0]
+        else:
+            return cls.settings[key]
+
+    def prepare(self, **options):
+        """ Run preparations (parsing, caching, ...).
+        It should be possible to call this again to refresh a template or to
+        update settings.
+        """
+        raise NotImplementedError
+
+    def render(self, *args, **kwargs):
+        """ Render the template with the specified local variables and return
+        a single byte or unicode string. If it is a byte string, the encoding
+        must match self.encoding. This method must be thread-safe!
+        Local variables may be provided in dictionaries (*args)
+        or directly, as keywords (**kwargs).
+        """
+        raise NotImplementedError
+
+
+class MakoTemplate(BaseTemplate):
+    def prepare(self, **options):
+        from mako.template import Template
+        from mako.lookup import TemplateLookup
+        options.update({'input_encoding':self.encoding})
+        #TODO: This is a hack... http://github.com/defnull/bottle/issues#issue/8
+        mylookup = TemplateLookup(directories=['.']+self.lookup, **options)
+        if self.source:
+            self.tpl = Template(self.source, lookup=mylookup)
+        else: #mako cannot guess extentions. We can, but only at top level...
+            name = self.name
+            if not os.path.splitext(name)[1]:
+                name += os.path.splitext(self.filename)[1]
+            self.tpl = mylookup.get_template(name)
+
+    def render(self, *args, **kwargs):
+        for dictarg in args: kwargs.update(dictarg)
+        _defaults = self.defaults.copy()
+        _defaults.update(kwargs)
+        return self.tpl.render(**_defaults)
+
+
+class CheetahTemplate(BaseTemplate):
+    def prepare(self, **options):
+        from Cheetah.Template import Template
+        self.context = threading.local()
+        self.context.vars = {}
+        options['searchList'] = [self.context.vars]
+        if self.source:
+            self.tpl = Template(source=self.source, **options)
+        else:
+            self.tpl = Template(file=self.filename, **options)
+
+    def render(self, *args, **kwargs):
+        for dictarg in args: kwargs.update(dictarg)
+        self.context.vars.update(self.defaults)
+        self.context.vars.update(kwargs)
+        out = str(self.tpl)
+        self.context.vars.clear()
+        return [out]
+
+
+class Jinja2Template(BaseTemplate):
+    def prepare(self, filters=None, tests=None, **kwargs):
+        from jinja2 import Environment, FunctionLoader
+        if 'prefix' in kwargs: # TODO: to be removed after a while
+            raise RuntimeError('The keyword argument `prefix` has been removed. '
+                'Use the full jinja2 environment name line_statement_prefix instead.')
+        self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
+        if filters: self.env.filters.update(filters)
+        if tests: self.env.tests.update(tests)
+        if self.source:
+            self.tpl = self.env.from_string(self.source)
+        else:
+            self.tpl = self.env.get_template(self.filename)
+
+    def render(self, *args, **kwargs):
+        for dictarg in args: kwargs.update(dictarg)
+        _defaults = self.defaults.copy()
+        _defaults.update(kwargs)
+        return self.tpl.render(**_defaults).encode("utf-8")
+
+    def loader(self, name):
+        fname = self.search(name, self.lookup)
+        if fname:
+            with open(fname, "rb") as f:
+                return f.read().decode(self.encoding)
+
+
+class SimpleTemplate(BaseTemplate):
+    blocks = ('if','elif','else','try','except','finally','for','while','with','def','class')
+    dedent_blocks = ('elif', 'else', 'except', 'finally')
+
+    def prepare(self, escape_func=cgi.escape, noescape=False):
+        self.cache = {}
+        if self.source:
+            self.code = self.translate(self.source)
+            self.co = compile(self.code, '<string>', 'exec')
+        else:
+            self.code = self.translate(open(self.filename).read())
+            self.co = compile(self.code, self.filename, 'exec')
+        enc = self.encoding
+        self._str = lambda x: touni(x, enc)
+        self._escape = lambda x: escape_func(touni(x, enc))
+        if noescape:
+            self._str, self._escape = self._escape, self._str
+
+    def translate(self, template):
+        stack = [] # Current Code indentation
+        lineno = 0 # Current line of code
+        ptrbuffer = [] # Buffer for printable strings and token tuple instances
+        codebuffer = [] # Buffer for generated python code
+        multiline = dedent = oneline = False
+
+        def yield_tokens(line):
+            for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)):
+                if i % 2:
+                    if part.startswith('!'): yield 'RAW', part[1:]
+                    else: yield 'CMD', part
+                else: yield 'TXT', part
+
+        def split_comment(codeline):
+            """ Removes comments from a line of code. """
+            line = codeline.splitlines()[0]
+            try:
+                tokens = list(tokenize.generate_tokens(iter(line).next))
+            except tokenize.TokenError:
+                return line.rsplit('#',1) if '#' in line else (line, '')
+            for token in tokens:
+                if token[0] == tokenize.COMMENT:
+                    start, end = token[2][1], token[3][1]
+                    return codeline[:start] + codeline[end:], codeline[start:end]
+            return line, ''
+
+        def flush(): # Flush the ptrbuffer
+            if not ptrbuffer: return
+            cline = ''
+            for line in ptrbuffer:
+                for token, value in line:
+                    if token == 'TXT': cline += repr(value)
+                    elif token == 'RAW': cline += '_str(%s)' % value
+                    elif token == 'CMD': cline += '_escape(%s)' % value
+                    cline +=  ', '
+                cline = cline[:-2] + '\\\n'
+            cline = cline[:-2]
+            if cline[:-1].endswith('\\\\\\\\\\n'):
+                cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr'
+            cline = '_printlist([' + cline + '])'
+            del ptrbuffer[:] # Do this before calling code() again
+            code(cline)
+
+        def code(stmt):
+            for line in stmt.splitlines():
+                codebuffer.append('  ' * len(stack) + line.strip())
+
+        for line in template.splitlines(True):
+            lineno += 1
+            line = line if isinstance(line, unicode)\
+                        else unicode(line, encoding=self.encoding)
+            if lineno <= 2:
+                m = re.search(r"%.*coding[:=]\s*([-\w\.]+)", line)
+                if m: self.encoding = m.group(1)
+                if m: line = line.replace('coding','coding (removed)')
+            if line.strip()[:2].count('%') == 1:
+                line = line.split('%',1)[1].lstrip() # Full line following the %
+                cline = split_comment(line)[0].strip()
+                cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0]
+                flush() ##encodig (TODO: why?)
+                if cmd in self.blocks or multiline:
+                    cmd = multiline or cmd
+                    dedent = cmd in self.dedent_blocks # "else:"
+                    if dedent and not oneline and not multiline:
+                        cmd = stack.pop()
+                    code(line)
+                    oneline = not cline.endswith(':') # "if 1: pass"
+                    multiline = cmd if cline.endswith('\\') else False
+                    if not oneline and not multiline:
+                        stack.append(cmd)
+                elif cmd == 'end' and stack:
+                    code('#end(%s) %s' % (stack.pop(), line.strip()[3:]))
+                elif cmd == 'include':
+                    p = cline.split(None, 2)[1:]
+                    if len(p) == 2:
+                        code("_=_include(%s, _stdout, %s)" % (repr(p[0]), p[1]))
+                    elif p:
+                        code("_=_include(%s, _stdout)" % repr(p[0]))
+                    else: # Empty %include -> reverse of %rebase
+                        code("_printlist(_base)")
+                elif cmd == 'rebase':
+                    p = cline.split(None, 2)[1:]
+                    if len(p) == 2:
+                        code("globals()['_rebase']=(%s, dict(%s))" % (repr(p[0]), p[1]))
+                    elif p:
+                        code("globals()['_rebase']=(%s, {})" % repr(p[0]))
+                else:
+                    code(line)
+            else: # Line starting with text (not '%') or '%%' (escaped)
+                if line.strip().startswith('%%'):
+                    line = line.replace('%%', '%', 1)
+                ptrbuffer.append(yield_tokens(line))
+        flush()
+        return '\n'.join(codebuffer) + '\n'
+
+    def subtemplate(self, _name, _stdout, *args, **kwargs):
+        for dictarg in args: kwargs.update(dictarg)
+        if _name not in self.cache:
+            self.cache[_name] = self.__class__(name=_name, lookup=self.lookup)
+        return self.cache[_name].execute(_stdout, kwargs)
+
+    def execute(self, _stdout, *args, **kwargs):
+        for dictarg in args: kwargs.update(dictarg)
+        env = self.defaults.copy()
+        env.update({'_stdout': _stdout, '_printlist': _stdout.extend,
+               '_include': self.subtemplate, '_str': self._str,
+               '_escape': self._escape})
+        env.update(kwargs)
+        eval(self.co, env)
+        if '_rebase' in env:
+            subtpl, rargs = env['_rebase']
+            subtpl = self.__class__(name=subtpl, lookup=self.lookup)
+            rargs['_base'] = _stdout[:] #copy stdout
+            del _stdout[:] # clear stdout
+            return subtpl.execute(_stdout, rargs)
+        return env
+
+    def render(self, *args, **kwargs):
+        """ Render the template using keyword arguments as local variables. """
+        for dictarg in args: kwargs.update(dictarg)
+        stdout = []
+        self.execute(stdout, kwargs)
+        return ''.join(stdout)
+
+
+def template(*args, **kwargs):
+    '''
+    Get a rendered template as a string iterator.
+    You can use a name, a filename or a template string as first parameter.
+    Template rendering arguments can be passed as dictionaries
+    or directly (as keyword arguments).
+    '''
+    tpl = args[0] if args else None
+    template_adapter = kwargs.pop('template_adapter', SimpleTemplate)
+    if tpl not in TEMPLATES or DEBUG:
+        settings = kwargs.pop('template_settings', {})
+        lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
+        if isinstance(tpl, template_adapter):
+            TEMPLATES[tpl] = tpl
+            if settings: TEMPLATES[tpl].prepare(**settings)
+        elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
+            TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, **settings)
+        else:
+            TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, **settings)
+    if not TEMPLATES[tpl]:
+        abort(500, 'Template (%s) not found' % tpl)
+    for dictarg in args[1:]: kwargs.update(dictarg)
+    return TEMPLATES[tpl].render(kwargs)
+
+mako_template = functools.partial(template, template_adapter=MakoTemplate)
+cheetah_template = functools.partial(template, template_adapter=CheetahTemplate)
+jinja2_template = functools.partial(template, template_adapter=Jinja2Template)
+
+def view(tpl_name, **defaults):
+    ''' Decorator: renders a template for a handler.
+        The handler can control its behavior like that:
+
+          - return a dict of template vars to fill out the template
+          - return something other than a dict and the view decorator will not
+            process the template, but return the handler result as is.
+            This includes returning a HTTPResponse(dict) to get,
+            for instance, JSON with autojson or other castfilters.
+    '''
+    def decorator(func):
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            result = func(*args, **kwargs)
+            if isinstance(result, dict):
+                tplvars = defaults.copy()
+                tplvars.update(result)
+                return template(tpl_name, **tplvars)
+            return result
+        return wrapper
+    return decorator
+
+mako_view = functools.partial(view, template_adapter=MakoTemplate)
+cheetah_view = functools.partial(view, template_adapter=CheetahTemplate)
+jinja2_view = functools.partial(view, template_adapter=Jinja2Template)
+
+
+
+
+
+
+###############################################################################
+# Constants and Globals ########################################################
+###############################################################################
+
+TEMPLATE_PATH = ['./', './views/']
+TEMPLATES = {}
+DEBUG = False
+MEMFILE_MAX = 1024*100
+HTTP_CODES = httplib.responses
+HTTP_CODES[418] = "I'm a teapot" # RFC 2324
+
+ERROR_PAGE_TEMPLATE = SimpleTemplate("""
+%try:
+    %from bottle import DEBUG, HTTP_CODES, request
+    %status_name = HTTP_CODES.get(e.status, 'Unknown').title()
+    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+    <html>
+        <head>
+            <title>Error {{e.status}}: {{status_name}}</title>
+            <style type="text/css">
+              html {background-color: #eee; font-family: sans;}
+              body {background-color: #fff; border: 1px solid #ddd; padding: 15px; margin: 15px;}
+              pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
+            </style>
+        </head>
+        <body>
+            <h1>Error {{e.status}}: {{status_name}}</h1>
+            <p>Sorry, the requested URL <tt>{{request.url}}</tt> caused an error:</p>
+            <pre>{{str(e.output)}}</pre>
+            %if DEBUG and e.exception:
+              <h2>Exception:</h2>
+              <pre>{{repr(e.exception)}}</pre>
+            %end
+            %if DEBUG and e.traceback:
+              <h2>Traceback:</h2>
+              <pre>{{e.traceback}}</pre>
+            %end
+        </body>
+    </html>
+%except ImportError:
+    <b>ImportError:</b> Could not generate the error page. Please add bottle to sys.path
+%end
+""")
+""" The HTML template used for error messages """
+
+request = Request()
+""" Whenever a page is requested, the :class:`Bottle` WSGI handler stores
+metadata about the current request into this instance of :class:`Request`.
+It is thread-safe and can be accessed from within handler functions. """
+
+response = Response()
+""" The :class:`Bottle` WSGI handler uses metadata assigned to this instance
+of :class:`Response` to generate the WSGI response. """
+
+local = threading.local()
+""" Thread-local namespace. Not used by Bottle, but could get handy """
+
+# Initialize app stack (create first empty Bottle app)
+# BC: 0.6.4 and needed for run()
+app = default_app = AppStack()
+app.push()


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/bottle_mods.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,71 @@
+"""
+Modifications and extensions to Bottle, to make it slightly more useful for
+yt's purposes
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from .bottle import server_names, debug, route
+import uuid
+
+route_functions = {}
+
+def preroute(future_route, *args, **kwargs):
+    def router(func):
+        route_functions[future_route] = (args, kwargs, func)
+        return func
+    return router
+
+def uuid_serve_functions(pre_routed, open_browser=False):
+    debug(mode=True)
+    token = uuid.uuid1()
+    for r in pre_routed:
+        args, kwargs, f = pre_routed[r]
+        if r[0] == "/": r = r[1:]
+        rp = "/%s/%s" % (token, r)
+        print "Routing from %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 open_browser:
+        # We do some fancy footwork so that we can open the browser while the
+        # server starts up.  I got this from some recipe whose URL escapes me.
+        # Thank you, to whoever wrote it!
+        def local_browse():
+            """Start a browser after waiting for half a second."""
+            import webbrowser, threading
+            def _local_browse():
+                webbrowser.open('http://localhost:%s/%s/' % (8080, token))
+            thread = threading.Timer(0.5, _open_browser)
+            thread.start()
+        local_browse()
+    # Right now we only really support the built-in wsgiref, but this may
+    # change if we start using Rocket.
+    server_type = server_names.get("wsgiref")
+    server = server_type(host='localhost', port=8080)
+    #repl.locals['server'] = server
+    run(server=server)


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/http_repl.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,81 @@
+"""
+A read-eval-print-loop that is served up through Bottle and accepts its
+commands through HTTP
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import json
+import os
+
+from .bottle_mods import preroute
+from .basic_repl import ProgrammaticREPL
+
+class HTTPREPL(ProgrammaticREPL):
+
+    def __init__(self, locals=None):
+        # First we do the standard initialization
+        ProgrammaticREPL.__init__(self, locals)
+        # Now, since we want to only preroute functions we know about, and
+        # since they have different arguments, and most of all because we only
+        # want to add them to the routing tables (which are a singleton for the
+        # entire interpreter state) we apply all the pre-routing now, rather
+        # than through metaclasses or other fancy decorating.
+        preroute_table = dict(index = ("/", "GET"),
+                              push = ("/push", "POST"),
+                              dir = ("/dir", "GET"),
+                              doc = ("/doc", "GET"))
+        for v, args in preroute_table:
+            preroute(args[0], method=args[1])(getattr(self, v))
+
+    def index(self):
+        """Return an HTTP-based Read-Eval-Print-Loop terminal."""
+        # For now this doesn't work!  We will need to move to a better method
+        # for this.
+        return open(os.path.join(localDir, "httprepl.html")).read()
+        
+    def push(self):
+        """Push 'line' and return exec results as a bare response."""
+        line = request.POST['line']
+        result = ProgrammaticREPL.push(self, line)
+        new_values = self.locals.pop("new_values", "")
+        if result is None:
+            # More input lines needed.
+            response.status = 204
+        return json.dumps( dict(text = result, new_values = new_values ))
+
+    def dir(self):
+        """Push 'line' and return result of eval on the final expr."""
+        line = request.GET['line']
+        result = ProgrammaticREPL.dir(self, line)
+        if not result:
+            response.status = 204
+            return
+        return repr(result)
+
+    def doc(self):
+        """Push 'line' and return result of getargspec on the final expr."""
+        line = request.GET['line']
+        result = ProgrammaticREPL.doc(self, line)
+        if not result:
+            response.status = 204
+        return result


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/setup.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import setuptools
+import os, sys, os.path
+
+def configuration(parent_package='',top_path=None):
+    from numpy.distutils.misc_util import Configuration
+    config = Configuration('reason',parent_package,top_path)
+    config.make_config_py() # installs __config__.py
+    config.make_svn_version_py()
+    return config


--- a/yt/gui/reason_v2.py	Wed Mar 16 18:42:08 2011 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,479 +0,0 @@
-"""
-New version of Reason, using a TraitsUI-based approach
-
-Author: Matthew Turk <matthewturk at gmail.com>
-Affiliation: KIPAC/SLAC/Stanford
-Homepage: http://yt.enzotools.org/
-License:
-  Copyright (C) 2009 Matthew Turk.  All Rights Reserved.
-
-  This file is part of yt.
-
-  yt is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 3 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-"""
-
-from yt.mods import *
-from yt.utilities.definitions import \
-    x_dict, \
-    y_dict
-#pf = EnzoStaticOutput("/Users/matthewturk/Research/data/galaxy1200.dir/galaxy1200")
-
-from enthought.traits.api import \
-    HasTraits, List, Instance, Str, Float, Any, Code, PythonValue, Int, CArray, \
-    Property, Enum, cached_property, DelegatesTo, Callable, Array, \
-    Button
-from enthought.traits.ui.api import \
-    Group, VGroup, HGroup, Tabbed, View, Item, ShellEditor, InstanceEditor, ListStrEditor, \
-    ListEditor, VSplit, VFlow, HSplit, VFold, ValueEditor, TreeEditor, TreeNode, RangeEditor, \
-    EnumEditor, Handler, Controller, DNDEditor
-from enthought.traits.ui.menu import \
-    Menu, Action, Separator, OKCancelButtons, OKButton
-from enthought.pyface.action.api import \
-    ActionController
-from enthought.tvtk.pyface.scene_editor import SceneEditor
-from enthought.tvtk.pyface.api import \
-    DecoratedScene
-from enthought.tvtk.pyface.scene_model import SceneModel
-
-from plot_editors import Figure, MPLFigureEditor, MPLVMPlotEditor, Axes
-
-from yt.visualization.plot_types import VMPlot, ProjectionPlot, SlicePlot
-
-import traceback
-from tvtk_interface import \
-    HierarchyImporter, YTScene
-
-class PlotCreationHandler(Controller):
-    main_window = Instance(HasTraits)
-    pnode = Instance(HasTraits)
-
-    format = Str
-    plot_type = Any
-    
-    def close(self, info, is_ok):
-        if not is_ok:
-            super(Controller, self).close(info, True)
-            return
-        spt = self.plot_type(plot_spec=self.model, pf=self.pnode.pf,
-                           name=self.format % (self.model.axis))
-        self.pnode.data_objects.append(spt)
-        self.main_window.plot_frame_tabs.append(spt)
-        spt.plot
-
-class VTKSceneCreationHandler(PlotCreationHandler):
-    importer = Instance(HierarchyImporter)
-
-    def close(self, info, is_ok):
-        if is_ok: 
-            yt_scene = YTScene(importer=self.importer,
-                scene=SceneModel())
-            spt = VTKDataObject(name = "VTK: %s" % self.pnode.pf,
-                    scene=yt_scene.scene,
-                    yt_scene=yt_scene)
-            self.pnode.data_objects.append(spt)
-            self.main_window.plot_frame_tabs.append(spt)
-        super(Controller, self).close(info, True)
-        return True
-
-
-class DataObject(HasTraits):
-    name = Str
-
-class VTKDataObject(DataObject):
-    yt_scene = Instance(YTScene)
-    scene = DelegatesTo("yt_scene")
-    add_contours = Button
-    add_isocontour = Button
-    add_x_plane = Button
-    add_y_plane = Button
-    add_z_plane = Button
-    edit_camera = Button
-    edit_operators = Button
-    edit_pipeline = Button
-    center_on_max = Button
-    operators = DelegatesTo("yt_scene")
-    traits_view = View(
-            Item("scene", editor = 
-        SceneEditor(scene_class=DecoratedScene),
-                    resizable=True, show_label=False),
-            HGroup(Item("add_contours", show_label=False),
-                   Item("add_isocontour", show_label=False),
-                   Item("add_x_plane", show_label=False),
-                   Item("add_y_plane", show_label=False),
-                   Item("add_z_plane", show_label=False),
-                   Item("edit_camera", show_label=False),
-                   Item("edit_operators", show_label=False),
-                   Item("edit_pipeline", show_label=False),
-                   Item("center_on_max", show_label=False),
-                ),
-            )
-
-    operators_edit = View(
-        Item("operators", style='custom', show_label=False,
-             editor=ListEditor(editor=InstanceEditor(),
-                               use_notebook=True),
-              name="Edit Operators"),
-        height=500.0, width=500.0, resizable=True)
-    
-    def _edit_camera_fired(self):
-        self.yt_scene.camera_path.edit_traits()
-
-    def _edit_operators_fired(self):
-        self.edit_traits(view='operators_edit')
-
-    def _edit_pipeline_fired(self):
-        from enthought.tvtk.pipeline.browser import PipelineBrowser
-        pb = PipelineBrowser(self.scene)
-        pb.show()
-
-    def _add_contours_fired(self):
-        self.yt_scene.add_contour()
-
-    def _add_isocontour_fired(self):
-        self.yt_scene.add_isocontour()
-
-    def _add_x_plane_fired(self):
-        self.yt_scene.add_x_plane()
-
-    def _add_y_plane_fired(self):
-        self.yt_scene.add_y_plane()
-
-    def _add_z_plane_fired(self):
-        self.yt_scene.add_z_plane()
-
-    def _center_on_max_fired(self):
-        self.yt_scene.do_center_on_max()
-
-class ParameterFile(HasTraits):
-    pf = Instance(EnzoStaticOutput)
-    data_objects = List(Instance(DataObject))
-    name = Str
-
-    def _name_default(self):
-        return str(self.pf)
-
-    def do_slice(self):
-        cons_view = View(
-                Item('axis'), 
-                Item('center'), 
-                Item('field', editor=EnumEditor(name='field_list')),
-                buttons=OKCancelButtons, title="Slicer: %s" % self.pf)
-        ps = SlicePlotSpec(pf=self.pf)
-        hand = PlotCreationHandler(main_window=mw, pnode=self, model=ps,
-                                   plot_type=SlicePlotTab, format="Slice: %s")
-        ps.edit_traits(cons_view, handler=hand)
-
-    def do_proj(self):
-        cons_view = View(
-                Item('axis'), 
-                Item('field', editor=EnumEditor(name='field_list')),
-                Item('weight_field', editor=EnumEditor(name='none_field_list')),
-                buttons=OKCancelButtons, title="Projector: %s" % self.pf)
-        ps = ProjPlotSpec(pf=self.pf)
-        hand = PlotCreationHandler(main_window=mw, pnode=self, model=ps,
-                                   plot_type=ProjPlotTab, format="Proj: %s")
-        ps.edit_traits(cons_view, handler=hand)
-
-    def do_vtk(self):
-        from tvtk_interface import HierarchyImporter, \
-            HierarchyImportHandler
-        importer = HierarchyImporter(pf=self.pf, max_level=self.pf.h.max_level)
-        importer.edit_traits(handler = VTKSceneCreationHandler(
-            main_window=mw, pnode=self, importer = importer))
-
-class ParameterFileCollection(HasTraits):
-    parameter_files = List(Instance(ParameterFile))
-    name = Str
-    collection = Any
-
-    def _parameter_files_default(self):
-        my_list = []
-        for f in self.collection:
-            try:
-                pf = EnzoStaticOutput(f)
-                my_list.append(
-                    ParameterFile(pf=pf, 
-                            data_objects = []))
-            except IOError: pass
-        return my_list
-
-    def _name_default(self):
-        return str(self.collection)
-
-class ParameterFileCollectionList(HasTraits):
-    parameter_file_collections = List(Instance(ParameterFileCollection))
-
-    def _parameter_file_collections_default(self):
-        return [ParameterFileCollection(collection=c)
-                for c in fido.GrabCollections()]
-
-class DataObjectList(HasTraits):
-    data_objects = List(Str)
-
-    traits_view = View(
-              Item('data_objects', show_label=False,
-                   editor=ListStrEditor())
-               )
-
-    def _data_objects_default(self):
-        return ['a','b','c']
-
-class PlotFrameTab(DataObject):
-    figure = Instance(Figure)
-
-class VMPlotSpec(HasTraits):
-    pf = Instance(EnzoStaticOutput)
-    field = Str('Density')
-    field_list = Property(depends_on = 'pf')
-
-    center = Array(shape=(3,), dtype='float64')
-    axis = Enum(0,1,2)
-
-    @cached_property
-    def _get_field_list(self):
-        fl = self.pf.h.field_list
-        df = self.pf.h.derived_field_list
-        fl.sort(); df.sort()
-        return fl + df
-
-    def _center_default(self):
-        return self.pf.h.find_max("Density")[1]
-
-class SlicePlotSpec(VMPlotSpec):
-    pass
-
-class ProjPlotSpec(VMPlotSpec):
-    weight_field = Str("None")
-    none_field_list = Property(depends_on = 'field_list')
-
-    @cached_property
-    def _get_none_field_list(self):
-        return ["None"] + self.field_list
-
-class VMPlotTab(PlotFrameTab):
-    pf = Instance(EnzoStaticOutput)
-    figure = Instance(Figure, args=())
-    field = DelegatesTo('plot_spec')
-    field_list = DelegatesTo('plot_spec')
-    plot = Instance(VMPlot)
-    axes = Instance(Axes)
-    disp_width = Float(1.0)
-    unit = Str('unitary')
-    min_width = Property(Float, depends_on=['pf','unit'])
-    max_width = Property(Float, depends_on=['pf','unit'])
-    unit_list = Property(depends_on = 'pf')
-    smallest_dx = Property(depends_on = 'pf')
-
-    traits_view = View(VGroup(
-            HGroup(Item('figure', editor=MPLVMPlotEditor(),
-                     show_label=False)),
-            HGroup(Item('disp_width',
-                     editor=RangeEditor(format="%0.2e",
-                        low_name='min_width', high_name='max_width',
-                        mode='logslider', enter_set=True),
-                     show_label=False, width=400.0),
-                   Item('unit',
-                      editor=EnumEditor(name='unit_list')),),
-            HGroup(Item('field',
-                      editor=EnumEditor(name='field_list')),
-                )),
-             resizable=True)
-
-    def __init__(self, **traits):
-        super(VMPlotTab, self).__init__(**traits)
-        self.axes = self.figure.add_subplot(111, aspect='equal')
-
-    def _field_changed(self, old, new):
-        self.plot.switch_z(new)
-        self._redraw()
-
-    @cached_property
-    def _get_min_width(self):
-        return 50.0*self.smallest_dx*self.pf[self.unit]
-
-    @cached_property
-    def _get_max_width(self):
-        return self.pf['unitary']*self.pf[self.unit]
-
-    @cached_property
-    def _get_smallest_dx(self):
-        return self.pf.h.get_smallest_dx()
-
-    @cached_property
-    def _get_unit_list(self):
-        return self.pf.units.keys()
-
-    def _unit_changed(self, old, new):
-        self.disp_width = self.disp_width * self.pf[new]/self.pf[old]
-
-    def _disp_width_changed(self, old, new):
-        self.plot.set_width(new, self.unit)
-        self._redraw()
-
-    def _redraw(self):
-        self.figure.canvas.draw()
-
-    def recenter(self, event):
-        xp, yp = event.xdata, event.ydata
-        dx = abs(self.plot.xlim[0] - self.plot.xlim[1])/self.plot.pix[0]
-        dy = abs(self.plot.ylim[0] - self.plot.ylim[1])/self.plot.pix[1]
-        x = (dx * xp) + self.plot.xlim[0]
-        y = (dy * yp) + self.plot.ylim[0]
-        xi = x_dict[self.axis]
-        yi = y_dict[self.axis]
-        cc = self.center[:]
-        cc[xi] = x; cc[yi] = y
-        self.plot.data.center = cc[:]
-        self.plot.data.set_field_parameter('center', cc.copy())
-        self.center = cc
-
-class SlicePlotTab(VMPlotTab):
-    plot_spec = Instance(SlicePlotSpec)
-
-    axis = DelegatesTo('plot_spec')
-    center = DelegatesTo('plot_spec')
-    
-    plot = Instance(SlicePlot)
-
-    def _plot_default(self):
-        coord = self.center[self.axis]
-        sl = self.pf.h.slice(self.axis, coord, center=self.center[:])
-        sp = SlicePlot(sl, self.field, self.figure, self.axes)
-        self.figure.canvas.draw()
-        return sp
-
-    def _center_changed(self, old, new):
-        #traceback.print_stack()
-        if na.all(na.abs(old - new) == 0.0): return
-        print na.abs(old-new)
-        print "Re-slicing", old, new
-        pp = self.center
-        self.plot.data.reslice(pp[self.axis])
-        self.plot._refresh_display_width()
-        self.figure.canvas.draw()
-
-class ProjPlotTab(VMPlotTab):
-    plot_spec = Instance(ProjPlotSpec)
-
-    axis = DelegatesTo('plot_spec')
-    center = DelegatesTo('plot_spec')
-    weight_field = DelegatesTo('plot_spec')
-
-    plot = Instance(ProjectionPlot)
-
-    def _plot_default(self):
-        self.field = self.field[:]
-        self.weight_field = self.weight_field[:]
-        wf = self.weight_field
-        if str(wf) == "None": wf = None
-        proj = self.pf.h.proj(self.axis, self.field, wf,
-                        center=self.center[:])
-        pp = ProjectionPlot(proj, self.field, self.figure, self.axes)
-        self.figure.canvas.draw()
-        return pp
-
-    def _center_changed(self, old, new):
-        self.plot._refresh_display_width()
-
-class SphereWrapper(DataObject):
-    radius = Float
-    unit = Str
-
-class MainWindow(HasTraits):
-    parameter_file_collections = Instance(ParameterFileCollectionList)
-    parameter_files = Instance(ParameterFileCollection)
-    plot_frame_tabs = List(Instance(DataObject))
-    open_parameterfile = Button
-    shell = PythonValue
-
-    def _shell_default(self):
-        return globals()
-    notebook_editor = ListEditor(editor=InstanceEditor(editable=True),
-                                 use_notebook=True)
-
-    traits_view = View(VSplit(
-                    HSplit(VGroup(
-                       Item('parameter_file_collections', 
-                            width=120.0, height=500.0,
-                            show_label=False,
-                            editor = TreeEditor(editable=False,
-                    nodes=[
-                        TreeNode(node_for=[ParameterFileCollectionList],
-                                 children='parameter_file_collections',
-                                 label="=Data Collections"),
-                        TreeNode(node_for=[ParameterFileCollection],
-                                 children='parameter_files',
-                                 label="name",
-                                 view=View()),
-                        TreeNode(node_for=[ParameterFile],
-                                 children='data_objects',
-                                 label="name",
-                                 menu = Menu(Action(name='Slice',
-                                                    action='object.do_slice'),
-                                             Action(name='Project',
-                                                    action='object.do_proj'),
-                                             Action(name='VTK',
-                                                    action='object.do_vtk')),
-                                 view=View()),
-                        TreeNode(node_for=[DataObject],
-                                 children='',
-                                 label="name"),
-                                ], show_icons=False),),
-                        Item('open_parameterfile', show_label=False)),
-                       Item('plot_frame_tabs', style='custom',
-                            editor = notebook_editor,
-                            show_label=False, height=500.0, width=500.0),
-                    ),
-                    HGroup(
-                       #Item('shell', editor=ShellEditor(share=True),
-                            #show_label=False, height=120.0),
-                    ),
-                ),
-               resizable=True, width=800.0, height=660.0,
-               title="reason v2 [prototype]")
-
-    def _open_parameterfile_fired(self):
-        print "OPENING"
-
-    def _parameter_file_collections_default(self):
-        return ParameterFileCollectionList()
-
-class YTScript(HasTraits):
-    code = Code
-    traits_view = View(Item('code', show_label=False),
-                       height=0.8, width=0.8, resizable=True,
-                       buttons=OKCancelButtons)
-
-class ObjectViewer(HasTraits):
-    to_view=Any
-    traits_view = View(
-            Item('to_view', editor=ValueEditor(), show_label=False),
-                     resizable=True, height=0.8, width=0.8)
-
-def view_object(obj):
-    ObjectViewer(to_view=obj).edit_traits()
-
-def run_script():
-    my_script = YTScript()
-    my_script.edit_traits()
-    return my_script
-
-class event_mock(object):
-    inaxes = True
-    button = 3
-
-dol = DataObjectList()
-mw = MainWindow(plot_frame_tabs = [])
-mw.edit_traits()
-#mw.edit_traits()


--- a/yt/gui/setup.py	Wed Mar 16 18:42:08 2011 -0400
+++ b/yt/gui/setup.py	Wed Mar 16 18:42:57 2011 -0400
@@ -5,6 +5,9 @@
 def configuration(parent_package='',top_path=None):
     from numpy.distutils.misc_util import Configuration
     config = Configuration('gui',parent_package,top_path)
+    config.add_subpackage('opengl_widgets')
+    config.add_subpackage('traited_explorer')
+    config.add_subpackage('reason')
     config.make_config_py() # installs __config__.py
     config.make_svn_version_py()
     return config


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/traited_explorer/plot_editors.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,128 @@
+"""
+Figure editors for the Traits GUI
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: KIPAC/SLAC/Stanford
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2009 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import sys, matplotlib
+# We want matplotlib to use a wxPython backend
+matplotlib.use('QT4Agg')
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.figure import Figure
+from matplotlib.axes import Axes
+
+from enthought.traits.api import Any, Instance
+from enthought.traits.ui.qt4.editor import Editor
+from enthought.traits.ui.qt4.basic_editor_factory import BasicEditorFactory
+
+from enthought.pyface.action.api import ActionController
+
+from enthought.traits.ui.menu import \
+    Menu, Action, Separator, OKCancelButtons, OKButton
+
+from matplotlib.backend_bases import Event as MPLEvent
+
+class _MPLFigureEditor(Editor):
+    """ Snagged from Gael's tutorial """
+
+    scrollable  = True
+    mpl_control = Instance(FigureCanvas)
+
+    def init(self, parent):
+        self.control = self._create_canvas(parent)
+        self.set_tooltip()
+
+    def update_editor(self):
+        pass
+
+    def _create_canvas(self, parent):
+        """ Create the MPL canvas. """
+        # The panel lets us add additional controls.
+        panel = wx.Panel(parent, -1)
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        panel.SetSizer(sizer)
+        # matplotlib commands to create a canvas
+        self.mpl_control = FigureCanvas(panel, -1, self.value)
+        sizer.Add(self.mpl_control, 1, wx.LEFT | wx.TOP | wx.GROW | wx.SHAPED)
+        self.value.canvas.SetMinSize((10,8))
+        return panel
+
+class MPLFigureEditor(BasicEditorFactory):
+    klass = _MPLFigureEditor
+
+class MPLAction(Action):
+    event = Instance(MPLEvent)
+
+class _MPLVMPlotEditor(_MPLFigureEditor, ActionController):
+
+    def _create_canvas(self, parent):
+        panel = _MPLFigureEditor._create_canvas(self, parent)
+        self.mpl_control.mpl_connect("button_press_event", self.on_click)
+        return panel
+
+    def on_click(self, event):
+        if not event.inaxes: return
+        if event.button == 3:
+            my_menu = Menu(MPLAction(name="Recenter", action="object.recenter",
+                                     event=event),
+                           MPLAction(name="Yo!", action="object.do_something",
+                                     event=event))
+            wxmenu = my_menu.create_menu(self.mpl_control, self)
+            self.mpl_control.PopupMenuXY(wxmenu)
+
+    def perform ( self, action ):
+        """
+        This is largely taken/modified from the TreeEditor _perform method.
+        """
+        object            = self.object
+        method_name       = action.action
+        info              = self.ui.info
+        handler           = self.ui.handler
+        event             = action.event
+
+        if method_name.find( '.' ) >= 0:
+            if method_name.find( '(' ) < 0:
+                method_name += '(event)'
+            try:
+                eval( method_name, globals(),
+                      { 'object':  object,
+                        'editor':  self,
+                        'info':    info,
+                        'event':   event,
+                        'handler': handler } )
+            except:
+                # fixme: Should the exception be logged somewhere?
+                print sys.exc_info()
+                
+            return
+
+        method = getattr( handler, method_name, None )
+        if method is not None:
+            method( info, object )
+            return
+
+        if action.on_perform is not None:
+            action.on_perform( object )
+
+class MPLVMPlotEditor(BasicEditorFactory):
+    klass = _MPLVMPlotEditor
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/traited_explorer/setup.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import setuptools
+import os, sys, os.path
+
+def configuration(parent_package='',top_path=None):
+    from numpy.distutils.misc_util import Configuration
+    config = Configuration('traited_explorer',parent_package,top_path)
+    config.make_config_py() # installs __config__.py
+    config.make_svn_version_py()
+    return config


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/traited_explorer/traited_explorer.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,479 @@
+"""
+New version of Reason, using a TraitsUI-based approach
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: KIPAC/SLAC/Stanford
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2009 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from yt.mods import *
+from yt.utilities.definitions import \
+    x_dict, \
+    y_dict
+#pf = EnzoStaticOutput("/Users/matthewturk/Research/data/galaxy1200.dir/galaxy1200")
+
+from enthought.traits.api import \
+    HasTraits, List, Instance, Str, Float, Any, Code, PythonValue, Int, CArray, \
+    Property, Enum, cached_property, DelegatesTo, Callable, Array, \
+    Button
+from enthought.traits.ui.api import \
+    Group, VGroup, HGroup, Tabbed, View, Item, ShellEditor, InstanceEditor, ListStrEditor, \
+    ListEditor, VSplit, VFlow, HSplit, VFold, ValueEditor, TreeEditor, TreeNode, RangeEditor, \
+    EnumEditor, Handler, Controller, DNDEditor
+from enthought.traits.ui.menu import \
+    Menu, Action, Separator, OKCancelButtons, OKButton
+from enthought.pyface.action.api import \
+    ActionController
+from enthought.tvtk.pyface.scene_editor import SceneEditor
+from enthought.tvtk.pyface.api import \
+    DecoratedScene
+from enthought.tvtk.pyface.scene_model import SceneModel
+
+from plot_editors import Figure, MPLFigureEditor, MPLVMPlotEditor, Axes
+
+from yt.visualization.plot_types import VMPlot, ProjectionPlot, SlicePlot
+
+import traceback
+from tvtk_interface import \
+    HierarchyImporter, YTScene
+
+class PlotCreationHandler(Controller):
+    main_window = Instance(HasTraits)
+    pnode = Instance(HasTraits)
+
+    format = Str
+    plot_type = Any
+    
+    def close(self, info, is_ok):
+        if not is_ok:
+            super(Controller, self).close(info, True)
+            return
+        spt = self.plot_type(plot_spec=self.model, pf=self.pnode.pf,
+                           name=self.format % (self.model.axis))
+        self.pnode.data_objects.append(spt)
+        self.main_window.plot_frame_tabs.append(spt)
+        spt.plot
+
+class VTKSceneCreationHandler(PlotCreationHandler):
+    importer = Instance(HierarchyImporter)
+
+    def close(self, info, is_ok):
+        if is_ok: 
+            yt_scene = YTScene(importer=self.importer,
+                scene=SceneModel())
+            spt = VTKDataObject(name = "VTK: %s" % self.pnode.pf,
+                    scene=yt_scene.scene,
+                    yt_scene=yt_scene)
+            self.pnode.data_objects.append(spt)
+            self.main_window.plot_frame_tabs.append(spt)
+        super(Controller, self).close(info, True)
+        return True
+
+
+class DataObject(HasTraits):
+    name = Str
+
+class VTKDataObject(DataObject):
+    yt_scene = Instance(YTScene)
+    scene = DelegatesTo("yt_scene")
+    add_contours = Button
+    add_isocontour = Button
+    add_x_plane = Button
+    add_y_plane = Button
+    add_z_plane = Button
+    edit_camera = Button
+    edit_operators = Button
+    edit_pipeline = Button
+    center_on_max = Button
+    operators = DelegatesTo("yt_scene")
+    traits_view = View(
+            Item("scene", editor = 
+        SceneEditor(scene_class=DecoratedScene),
+                    resizable=True, show_label=False),
+            HGroup(Item("add_contours", show_label=False),
+                   Item("add_isocontour", show_label=False),
+                   Item("add_x_plane", show_label=False),
+                   Item("add_y_plane", show_label=False),
+                   Item("add_z_plane", show_label=False),
+                   Item("edit_camera", show_label=False),
+                   Item("edit_operators", show_label=False),
+                   Item("edit_pipeline", show_label=False),
+                   Item("center_on_max", show_label=False),
+                ),
+            )
+
+    operators_edit = View(
+        Item("operators", style='custom', show_label=False,
+             editor=ListEditor(editor=InstanceEditor(),
+                               use_notebook=True),
+              name="Edit Operators"),
+        height=500.0, width=500.0, resizable=True)
+    
+    def _edit_camera_fired(self):
+        self.yt_scene.camera_path.edit_traits()
+
+    def _edit_operators_fired(self):
+        self.edit_traits(view='operators_edit')
+
+    def _edit_pipeline_fired(self):
+        from enthought.tvtk.pipeline.browser import PipelineBrowser
+        pb = PipelineBrowser(self.scene)
+        pb.show()
+
+    def _add_contours_fired(self):
+        self.yt_scene.add_contour()
+
+    def _add_isocontour_fired(self):
+        self.yt_scene.add_isocontour()
+
+    def _add_x_plane_fired(self):
+        self.yt_scene.add_x_plane()
+
+    def _add_y_plane_fired(self):
+        self.yt_scene.add_y_plane()
+
+    def _add_z_plane_fired(self):
+        self.yt_scene.add_z_plane()
+
+    def _center_on_max_fired(self):
+        self.yt_scene.do_center_on_max()
+
+class ParameterFile(HasTraits):
+    pf = Instance(EnzoStaticOutput)
+    data_objects = List(Instance(DataObject))
+    name = Str
+
+    def _name_default(self):
+        return str(self.pf)
+
+    def do_slice(self):
+        cons_view = View(
+                Item('axis'), 
+                Item('center'), 
+                Item('field', editor=EnumEditor(name='field_list')),
+                buttons=OKCancelButtons, title="Slicer: %s" % self.pf)
+        ps = SlicePlotSpec(pf=self.pf)
+        hand = PlotCreationHandler(main_window=mw, pnode=self, model=ps,
+                                   plot_type=SlicePlotTab, format="Slice: %s")
+        ps.edit_traits(cons_view, handler=hand)
+
+    def do_proj(self):
+        cons_view = View(
+                Item('axis'), 
+                Item('field', editor=EnumEditor(name='field_list')),
+                Item('weight_field', editor=EnumEditor(name='none_field_list')),
+                buttons=OKCancelButtons, title="Projector: %s" % self.pf)
+        ps = ProjPlotSpec(pf=self.pf)
+        hand = PlotCreationHandler(main_window=mw, pnode=self, model=ps,
+                                   plot_type=ProjPlotTab, format="Proj: %s")
+        ps.edit_traits(cons_view, handler=hand)
+
+    def do_vtk(self):
+        from tvtk_interface import HierarchyImporter, \
+            HierarchyImportHandler
+        importer = HierarchyImporter(pf=self.pf, max_level=self.pf.h.max_level)
+        importer.edit_traits(handler = VTKSceneCreationHandler(
+            main_window=mw, pnode=self, importer = importer))
+
+class ParameterFileCollection(HasTraits):
+    parameter_files = List(Instance(ParameterFile))
+    name = Str
+    collection = Any
+
+    def _parameter_files_default(self):
+        my_list = []
+        for f in self.collection:
+            try:
+                pf = EnzoStaticOutput(f)
+                my_list.append(
+                    ParameterFile(pf=pf, 
+                            data_objects = []))
+            except IOError: pass
+        return my_list
+
+    def _name_default(self):
+        return str(self.collection)
+
+class ParameterFileCollectionList(HasTraits):
+    parameter_file_collections = List(Instance(ParameterFileCollection))
+
+    def _parameter_file_collections_default(self):
+        return [ParameterFileCollection(collection=c)
+                for c in fido.GrabCollections()]
+
+class DataObjectList(HasTraits):
+    data_objects = List(Str)
+
+    traits_view = View(
+              Item('data_objects', show_label=False,
+                   editor=ListStrEditor())
+               )
+
+    def _data_objects_default(self):
+        return ['a','b','c']
+
+class PlotFrameTab(DataObject):
+    figure = Instance(Figure)
+
+class VMPlotSpec(HasTraits):
+    pf = Instance(EnzoStaticOutput)
+    field = Str('Density')
+    field_list = Property(depends_on = 'pf')
+
+    center = Array(shape=(3,), dtype='float64')
+    axis = Enum(0,1,2)
+
+    @cached_property
+    def _get_field_list(self):
+        fl = self.pf.h.field_list
+        df = self.pf.h.derived_field_list
+        fl.sort(); df.sort()
+        return fl + df
+
+    def _center_default(self):
+        return self.pf.h.find_max("Density")[1]
+
+class SlicePlotSpec(VMPlotSpec):
+    pass
+
+class ProjPlotSpec(VMPlotSpec):
+    weight_field = Str("None")
+    none_field_list = Property(depends_on = 'field_list')
+
+    @cached_property
+    def _get_none_field_list(self):
+        return ["None"] + self.field_list
+
+class VMPlotTab(PlotFrameTab):
+    pf = Instance(EnzoStaticOutput)
+    figure = Instance(Figure, args=())
+    field = DelegatesTo('plot_spec')
+    field_list = DelegatesTo('plot_spec')
+    plot = Instance(VMPlot)
+    axes = Instance(Axes)
+    disp_width = Float(1.0)
+    unit = Str('unitary')
+    min_width = Property(Float, depends_on=['pf','unit'])
+    max_width = Property(Float, depends_on=['pf','unit'])
+    unit_list = Property(depends_on = 'pf')
+    smallest_dx = Property(depends_on = 'pf')
+
+    traits_view = View(VGroup(
+            HGroup(Item('figure', editor=MPLVMPlotEditor(),
+                     show_label=False)),
+            HGroup(Item('disp_width',
+                     editor=RangeEditor(format="%0.2e",
+                        low_name='min_width', high_name='max_width',
+                        mode='logslider', enter_set=True),
+                     show_label=False, width=400.0),
+                   Item('unit',
+                      editor=EnumEditor(name='unit_list')),),
+            HGroup(Item('field',
+                      editor=EnumEditor(name='field_list')),
+                )),
+             resizable=True)
+
+    def __init__(self, **traits):
+        super(VMPlotTab, self).__init__(**traits)
+        self.axes = self.figure.add_subplot(111, aspect='equal')
+
+    def _field_changed(self, old, new):
+        self.plot.switch_z(new)
+        self._redraw()
+
+    @cached_property
+    def _get_min_width(self):
+        return 50.0*self.smallest_dx*self.pf[self.unit]
+
+    @cached_property
+    def _get_max_width(self):
+        return self.pf['unitary']*self.pf[self.unit]
+
+    @cached_property
+    def _get_smallest_dx(self):
+        return self.pf.h.get_smallest_dx()
+
+    @cached_property
+    def _get_unit_list(self):
+        return self.pf.units.keys()
+
+    def _unit_changed(self, old, new):
+        self.disp_width = self.disp_width * self.pf[new]/self.pf[old]
+
+    def _disp_width_changed(self, old, new):
+        self.plot.set_width(new, self.unit)
+        self._redraw()
+
+    def _redraw(self):
+        self.figure.canvas.draw()
+
+    def recenter(self, event):
+        xp, yp = event.xdata, event.ydata
+        dx = abs(self.plot.xlim[0] - self.plot.xlim[1])/self.plot.pix[0]
+        dy = abs(self.plot.ylim[0] - self.plot.ylim[1])/self.plot.pix[1]
+        x = (dx * xp) + self.plot.xlim[0]
+        y = (dy * yp) + self.plot.ylim[0]
+        xi = x_dict[self.axis]
+        yi = y_dict[self.axis]
+        cc = self.center[:]
+        cc[xi] = x; cc[yi] = y
+        self.plot.data.center = cc[:]
+        self.plot.data.set_field_parameter('center', cc.copy())
+        self.center = cc
+
+class SlicePlotTab(VMPlotTab):
+    plot_spec = Instance(SlicePlotSpec)
+
+    axis = DelegatesTo('plot_spec')
+    center = DelegatesTo('plot_spec')
+    
+    plot = Instance(SlicePlot)
+
+    def _plot_default(self):
+        coord = self.center[self.axis]
+        sl = self.pf.h.slice(self.axis, coord, center=self.center[:])
+        sp = SlicePlot(sl, self.field, self.figure, self.axes)
+        self.figure.canvas.draw()
+        return sp
+
+    def _center_changed(self, old, new):
+        #traceback.print_stack()
+        if na.all(na.abs(old - new) == 0.0): return
+        print na.abs(old-new)
+        print "Re-slicing", old, new
+        pp = self.center
+        self.plot.data.reslice(pp[self.axis])
+        self.plot._refresh_display_width()
+        self.figure.canvas.draw()
+
+class ProjPlotTab(VMPlotTab):
+    plot_spec = Instance(ProjPlotSpec)
+
+    axis = DelegatesTo('plot_spec')
+    center = DelegatesTo('plot_spec')
+    weight_field = DelegatesTo('plot_spec')
+
+    plot = Instance(ProjectionPlot)
+
+    def _plot_default(self):
+        self.field = self.field[:]
+        self.weight_field = self.weight_field[:]
+        wf = self.weight_field
+        if str(wf) == "None": wf = None
+        proj = self.pf.h.proj(self.axis, self.field, wf,
+                        center=self.center[:])
+        pp = ProjectionPlot(proj, self.field, self.figure, self.axes)
+        self.figure.canvas.draw()
+        return pp
+
+    def _center_changed(self, old, new):
+        self.plot._refresh_display_width()
+
+class SphereWrapper(DataObject):
+    radius = Float
+    unit = Str
+
+class MainWindow(HasTraits):
+    parameter_file_collections = Instance(ParameterFileCollectionList)
+    parameter_files = Instance(ParameterFileCollection)
+    plot_frame_tabs = List(Instance(DataObject))
+    open_parameterfile = Button
+    shell = PythonValue
+
+    def _shell_default(self):
+        return globals()
+    notebook_editor = ListEditor(editor=InstanceEditor(editable=True),
+                                 use_notebook=True)
+
+    traits_view = View(VSplit(
+                    HSplit(VGroup(
+                       Item('parameter_file_collections', 
+                            width=120.0, height=500.0,
+                            show_label=False,
+                            editor = TreeEditor(editable=False,
+                    nodes=[
+                        TreeNode(node_for=[ParameterFileCollectionList],
+                                 children='parameter_file_collections',
+                                 label="=Data Collections"),
+                        TreeNode(node_for=[ParameterFileCollection],
+                                 children='parameter_files',
+                                 label="name",
+                                 view=View()),
+                        TreeNode(node_for=[ParameterFile],
+                                 children='data_objects',
+                                 label="name",
+                                 menu = Menu(Action(name='Slice',
+                                                    action='object.do_slice'),
+                                             Action(name='Project',
+                                                    action='object.do_proj'),
+                                             Action(name='VTK',
+                                                    action='object.do_vtk')),
+                                 view=View()),
+                        TreeNode(node_for=[DataObject],
+                                 children='',
+                                 label="name"),
+                                ], show_icons=False),),
+                        Item('open_parameterfile', show_label=False)),
+                       Item('plot_frame_tabs', style='custom',
+                            editor = notebook_editor,
+                            show_label=False, height=500.0, width=500.0),
+                    ),
+                    HGroup(
+                       #Item('shell', editor=ShellEditor(share=True),
+                            #show_label=False, height=120.0),
+                    ),
+                ),
+               resizable=True, width=800.0, height=660.0,
+               title="reason v2 [prototype]")
+
+    def _open_parameterfile_fired(self):
+        print "OPENING"
+
+    def _parameter_file_collections_default(self):
+        return ParameterFileCollectionList()
+
+class YTScript(HasTraits):
+    code = Code
+    traits_view = View(Item('code', show_label=False),
+                       height=0.8, width=0.8, resizable=True,
+                       buttons=OKCancelButtons)
+
+class ObjectViewer(HasTraits):
+    to_view=Any
+    traits_view = View(
+            Item('to_view', editor=ValueEditor(), show_label=False),
+                     resizable=True, height=0.8, width=0.8)
+
+def view_object(obj):
+    ObjectViewer(to_view=obj).edit_traits()
+
+def run_script():
+    my_script = YTScript()
+    my_script.edit_traits()
+    return my_script
+
+class event_mock(object):
+    inaxes = True
+    button = 3
+
+dol = DataObjectList()
+mw = MainWindow(plot_frame_tabs = [])
+mw.edit_traits()
+#mw.edit_traits()


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/traited_explorer/tvtk_interface.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,692 @@
+"""
+This is the preliminary interface to VTK.  Note that as of VTK 5.2, it still
+requires a patchset prepared here:
+http://yt.enzotools.org/files/vtk_composite_data.zip
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: KIPAC/SLAC/Stanford
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2007-2009 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from enthought.tvtk.tools import ivtk
+from enthought.tvtk.api import tvtk 
+from enthought.traits.api import \
+    Float, HasTraits, Instance, Range, Any, Delegate, Tuple, File, Int, Str, \
+    CArray, List, Button, Bool, Property, cached_property
+from enthought.traits.ui.api import View, Item, HGroup, VGroup, TableEditor, \
+    Handler, Controller, RangeEditor, EnumEditor, InstanceEditor
+from enthought.traits.ui.menu import \
+    Menu, Action, Separator, OKCancelButtons, OKButton
+from enthought.traits.ui.table_column import ObjectColumn
+from enthought.tvtk.pyface.api import DecoratedScene
+
+import enthought.pyface.api as pyface
+
+#from yt.reason import *
+import sys
+import numpy as na
+import time, pickle, os, os.path
+from yt.funcs import *
+from yt.analysis_modules.hierarchy_subset.api import \
+        ExtractedHierarchy, ExtractedParameterFile
+
+#from enthought.tvtk.pyface.ui.wx.wxVTKRenderWindowInteractor \
+     #import wxVTKRenderWindowInteractor
+
+from enthought.mayavi.core.lut_manager import LUTManager
+
+#wxVTKRenderWindowInteractor.USE_STEREO = 1
+
+class TVTKMapperWidget(HasTraits):
+    alpha = Float(1.0)
+    post_call = Any
+    lut_manager = Instance(LUTManager)
+
+    def _alpha_changed(self, old, new):
+        self.lut_manager.lut.alpha_range = (new, new)
+        self.post_call()
+
+class MappingPlane(TVTKMapperWidget):
+    plane = Instance(tvtk.Plane)
+    _coord_redit = editor=RangeEditor(format="%0.2e",
+                              low_name='vmin', high_name='vmax',
+                              auto_set=False, enter_set=True)
+    auto_set = Bool(False)
+    traits_view = View(Item('coord', editor=_coord_redit),
+                       Item('auto_set'),
+                       Item('alpha', editor=RangeEditor(
+                              low=0.0, high=1.0,
+                              enter_set=True, auto_set=False)),
+                       Item('lut_manager', show_label=False,
+                            editor=InstanceEditor(), style='custom'))
+    vmin = Float
+    vmax = Float
+
+    def _auto_set_changed(self, old, new):
+        if new is True:
+            self._coord_redit.auto_set = True
+            self._coord_redit.enter_set = False
+        else:
+            self._coord_redit.auto_set = False
+            self._coord_redit.enter_set = True
+
+    def __init__(self, vmin, vmax, vdefault, **traits):
+        HasTraits.__init__(self, **traits)
+        self.vmin = vmin
+        self.vmax = vmax
+        trait = Range(float(vmin), float(vmax), value=vdefault)
+        self.add_trait("coord", trait)
+        self.coord = vdefault
+
+    def _coord_changed(self, old, new):
+        orig = self.plane.origin[:]
+        orig[self.axis] = new
+        self.plane.origin = orig
+        self.post_call()
+
+class MappingMarchingCubes(TVTKMapperWidget):
+    operator = Instance(tvtk.MarchingCubes)
+    mapper = Instance(tvtk.HierarchicalPolyDataMapper)
+    vmin = Float
+    vmax = Float
+    auto_set = Bool(False)
+    _val_redit = RangeEditor(format="%0.2f",
+                             low_name='vmin', high_name='vmax',
+                             auto_set=False, enter_set=True)
+    traits_view = View(Item('value', editor=_val_redit),
+                       Item('auto_set'),
+                       Item('alpha', editor=RangeEditor(
+                            low=0.0, high=1.0,
+                            enter_set=True, auto_set=False,)),
+                       Item('lut_manager', show_label=False,
+                            editor=InstanceEditor(), style='custom'))
+
+    def __init__(self, vmin, vmax, vdefault, **traits):
+        HasTraits.__init__(self, **traits)
+        self.vmin = vmin
+        self.vmax = vmax
+        trait = Range(float(vmin), float(vmax), value=vdefault)
+        self.add_trait("value", trait)
+        self.value = vdefault
+
+    def _auto_set_changed(self, old, new):
+        if new is True:
+            self._val_redit.auto_set = True
+            self._val_redit.enter_set = False
+        else:
+            self._val_redit.auto_set = False
+            self._val_redit.enter_set = True
+
+    def _value_changed(self, old, new):
+        self.operator.set_value(0, new)
+        self.post_call()
+
+class MappingIsoContour(MappingMarchingCubes):
+    operator = Instance(tvtk.ContourFilter)
+
+class CameraPosition(HasTraits):
+    position = CArray(shape=(3,), dtype='float64')
+    focal_point = CArray(shape=(3,), dtype='float64')
+    view_up = CArray(shape=(3,), dtype='float64')
+    clipping_range = CArray(shape=(2,), dtype='float64')
+    distance = Float
+    num_steps = Int(10)
+    orientation_wxyz = CArray(shape=(4,), dtype='float64')
+
+class CameraControl(HasTraits):
+    # Traits
+    positions = List(CameraPosition)
+    yt_scene = Instance('YTScene')
+    center = Delegate('yt_scene')
+    scene = Delegate('yt_scene')
+    camera = Instance(tvtk.OpenGLCamera)
+    reset_position = Instance(CameraPosition)
+    fps = Float(25.0)
+    export_filename = 'frames'
+    periodic = Bool
+
+    # UI elements
+    snapshot = Button()
+    play = Button()
+    export_frames = Button()
+    reset_path = Button()
+    recenter = Button()
+    save_path = Button()
+    load_path = Button()
+    export_path = Button()
+
+    table_def = TableEditor(
+        columns = [ ObjectColumn(name='position'),
+                    ObjectColumn(name='focal_point'),
+                    ObjectColumn(name='view_up'),
+                    ObjectColumn(name='clipping_range'),
+                    ObjectColumn(name='num_steps') ],
+        reorderable=True, deletable=True,
+        sortable=True, sort_model=True,
+        show_toolbar=True,
+        selection_mode='row',
+        selected = 'reset_position'
+                )
+
+    default_view = View(
+                VGroup(
+                  HGroup(
+                    Item('camera', show_label=False),
+                    Item('recenter', show_label=False),
+                    label='Camera'),
+                  HGroup(
+                    Item('snapshot', show_label=False),
+                    Item('play', show_label=False),
+                    Item('export_frames',show_label=False),
+                    Item('reset_path', show_label=False),
+                    Item('save_path', show_label=False),
+                    Item('load_path', show_label=False),
+                    Item('export_path', show_label=False),
+                    Item('export_filename'),
+                    Item('periodic'),
+                    Item('fps'),
+                    label='Playback'),
+                  VGroup(
+                    Item('positions', show_label=False,
+                        editor=table_def),
+                    label='Camera Path'),
+                 ),
+                resizable=True, title="Camera Path Editor",
+                       )
+
+    def _reset_position_changed(self, old, new):
+        if new is None: return
+        cam = self.scene.camera
+        cam.position = new.position
+        cam.focal_point = new.focal_point
+        cam.view_up = new.view_up
+        cam.clipping_range = new.clipping_range
+        self.scene.render()
+
+    def __init__(self, **traits):
+        HasTraits.__init__(self, **traits)
+
+    def take_snapshot(self):
+        cam = self.scene.camera
+        self.positions.append(CameraPosition(
+                position=cam.position,
+                focal_point=cam.focal_point,
+                view_up=cam.view_up,
+                clipping_range=cam.clipping_range,
+                distance=cam.distance,
+                orientation_wxyz=cam.orientation_wxyz))
+
+    def _export_path_fired(self): 
+        dlg = pyface.FileDialog(
+            action='save as',
+            wildcard="*.cpath",
+        )
+        if dlg.open() == pyface.OK:
+            print "Saving:", dlg.path
+            self.export_camera_path(dlg.path)
+
+    def export_camera_path(self, fn):
+        to_dump = dict(positions=[], focal_points=[],
+                       view_ups=[], clipping_ranges=[],
+                       distances=[], orientation_wxyzs=[])
+        def _write(cam):
+            to_dump['positions'].append(cam.position)
+            to_dump['focal_points'].append(cam.focal_point)
+            to_dump['view_ups'].append(cam.view_up)
+            to_dump['clipping_ranges'].append(cam.clipping_range)
+            to_dump['distances'].append(cam.distance)
+            to_dump['orientation_wxyzs'].append(cam.orientation_wxyz)
+        self.step_through(0.0, callback=_write)
+        pickle.dump(to_dump, open(fn, "wb"))
+
+    def _save_path_fired(self): 
+        dlg = pyface.FileDialog(
+            action='save as',
+            wildcard="*.cpath",
+        )
+        if dlg.open() == pyface.OK:
+            print "Saving:", dlg.path
+            self.dump_camera_path(dlg.path)
+
+    def dump_camera_path(self, fn):
+        to_dump = dict(positions=[], focal_points=[],
+                       view_ups=[], clipping_ranges=[],
+                       distances=[], orientation_wxyzs=[],
+                       num_stepss=[])
+        for p in self.positions:
+            to_dump['positions'].append(p.position)
+            to_dump['focal_points'].append(p.focal_point)
+            to_dump['view_ups'].append(p.view_up)
+            to_dump['clipping_ranges'].append(p.clipping_range)
+            to_dump['distances'].append(p.distance)
+            to_dump['num_stepss'].append(p.num_steps) # stupid s
+            to_dump['orientation_wxyzs'].append(p.orientation_wxyz)
+        pickle.dump(to_dump, open(fn, "wb"))
+
+    def _load_path_fired(self):
+        dlg = pyface.FileDialog(
+            action='open',
+            wildcard="*.cpath",
+        )
+        if dlg.open() == pyface.OK:
+            print "Loading:", dlg.path
+            self.load_camera_path(dlg.path)
+
+    def load_camera_path(self, fn):
+        to_use = pickle.load(open(fn, "rb"))
+        self.positions = []
+        for i in range(len(to_use['positions'])):
+            dd = {}
+            for kw in to_use:
+                # Strip the s
+                dd[kw[:-1]] = to_use[kw][i]
+            self.positions.append(
+                CameraPosition(**dd))
+
+    def _recenter_fired(self):
+        self.camera.focal_point = self.center
+        self.scene.render()
+
+    def _snapshot_fired(self):
+        self.take_snapshot()
+
+    def _play_fired(self):
+        self.step_through()
+
+    def _export_frames_fired(self):
+        self.step_through(save_frames=True)
+
+    def _reset_path_fired(self):
+        self.positions = []
+
+    def step_through(self, pause = 1.0, callback=None, save_frames=False):
+        cam = self.scene.camera
+        frame_counter=0
+        if self.periodic:
+            cyclic_pos = self.positions + [self.positions[0]]
+        else:
+            cyclic_pos = self.positions
+        for i in range(len(cyclic_pos)-1):
+            pos1 = cyclic_pos[i]
+            pos2 = cyclic_pos[i+1]
+            r = pos1.num_steps
+            for p in range(pos1.num_steps):
+                po = _interpolate(pos1.position, pos2.position, p, r)
+                fp = _interpolate(pos1.focal_point, pos2.focal_point, p, r)
+                vu = _interpolate(pos1.view_up, pos2.view_up, p, r)
+                cr = _interpolate(pos1.clipping_range, pos2.clipping_range, p, r)
+                _set_cpos(cam, po, fp, vu, cr)
+                self.scene.render()
+                if callback is not None: callback(cam)
+                if save_frames:
+                    self.scene.save("%s_%0.5d.png" % (self.export_filename,frame_counter))
+                else:
+                    time.sleep(pause * 1.0/self.fps)
+                frame_counter += 1
+
+def _interpolate(q1, q2, p, r):
+    return q1 + p*(q2 - q1)/float(r)
+
+def _set_cpos(cam, po, fp, vu, cr):
+    cam.position = po
+    cam.focal_point = fp
+    cam.view_up = vu
+    cam.clipping_range = cr
+
+class HierarchyImporter(HasTraits):
+    pf = Any
+    min_grid_level = Int(0)
+    max_level = Int(1)
+    number_of_levels = Range(0, 13)
+    max_import_levels = Property(depends_on='min_grid_level')
+    field = Str("Density")
+    field_list = List
+    center_on_max = Bool(True)
+    center = CArray(shape = (3,), dtype = 'float64')
+    cache = Bool(True)
+    smoothed = Bool(True)
+    show_grids = Bool(True)
+
+    def _field_list_default(self):
+        fl = self.pf.h.field_list
+        df = self.pf.h.derived_field_list
+        fl.sort(); df.sort()
+        return fl + df
+    
+    default_view = View(Item('min_grid_level',
+                              editor=RangeEditor(low=0,
+                                                 high_name='max_level')),
+                        Item('number_of_levels', 
+                              editor=RangeEditor(low=1,
+                                                 high_name='max_import_levels')),
+                        Item('field', editor=EnumEditor(name='field_list')),
+                        Item('center_on_max'),
+                        Item('center', enabled_when='not object.center_on_max'),
+                        Item('smoothed'),
+                        Item('cache', label='Pre-load data'),
+                        Item('show_grids'),
+                        buttons=OKCancelButtons)
+
+    def _center_default(self):
+        return [0.5,0.5,0.5]
+
+    @cached_property
+    def _get_max_import_levels(self):
+        return min(13, self.pf.h.max_level - self.min_grid_level + 1)
+
+class HierarchyImportHandler(Controller):
+    importer = Instance(HierarchyImporter)
+    
+
+    def close(self, info, is_ok):
+        if is_ok: 
+            yt_scene = YTScene(
+                importer=self.importer)
+        super(Controller, self).close(info, True)
+        return
+
+
+class YTScene(HasTraits):
+
+    # Traits
+    importer = Instance(HierarchyImporter)
+    pf = Delegate("importer")
+    min_grid_level = Delegate("importer")
+    number_of_levels = Delegate("importer")
+    field = Delegate("importer")
+    center = CArray(shape = (3,), dtype = 'float64')
+    center_on_max = Delegate("importer")
+    smoothed = Delegate("importer")
+    cache = Delegate("importer")
+    show_grids = Delegate("importer")
+
+    camera_path = Instance(CameraControl)
+    #window = Instance(ivtk.IVTKWithCrustAndBrowser)
+    #python_shell = Delegate('window')
+    #scene = Delegate('window')
+    scene = Instance(HasTraits)
+    operators = List(HasTraits)
+
+    # State variables
+    _grid_boundaries_actor = None
+
+    # Views
+    def _window_default(self):
+        # Should experiment with passing in a pipeline browser
+        # that has two root objects -- one for TVTKBases, i.e. the render
+        # window, and one that accepts our objects
+        return ivtk.IVTKWithCrustAndBrowser(size=(800,600), stereo=1)
+
+    def _camera_path_default(self):
+        return CameraControl(yt_scene=self, camera=self.scene.camera)
+
+    def __init__(self, **traits):
+        HasTraits.__init__(self, **traits)
+        max_level = min(self.pf.h.max_level,
+                        self.min_grid_level + self.number_of_levels - 1)
+        self.extracted_pf = ExtractedParameterFile(self.pf,
+                             self.min_grid_level, max_level, offset=None)
+        self.extracted_hierarchy = self.extracted_pf.h
+        self._hdata_set = tvtk.HierarchicalBoxDataSet()
+        self._ugs = []
+        self._grids = []
+        self._min_val = 1e60
+        self._max_val = -1e60
+        gid = 0
+        if self.cache:
+            for grid_set in self.extracted_hierarchy.get_levels():
+                for grid in grid_set:
+                    grid[self.field]
+        for l, grid_set in enumerate(self.extracted_hierarchy.get_levels()):
+            gid = self._add_level(grid_set, l, gid)
+        if self.show_grids:
+            self.toggle_grid_boundaries()
+            
+    def _center_default(self):
+        return self.extracted_hierarchy._convert_coords(
+                [0.5, 0.5, 0.5])
+
+    def do_center_on_max(self):
+        self.center = self.extracted_hierarchy._convert_coords(
+            self.pf.h.find_max("Density")[1])
+        self.scene.camera.focal_point = self.center
+
+    def _add_level(self, grid_set, level, gid):
+        for grid in grid_set:
+            self._hdata_set.set_refinement_ratio(level, 2)
+            gid = self._add_grid(grid, gid, level)
+        return gid
+
+    def _add_grid(self, grid, gid, level=0):
+        mylog.debug("Adding grid %s on level %s (%s)",
+                    grid.id, level, grid.Level)
+        if grid in self._grids: return
+        self._grids.append(grid)
+
+        scalars = grid.get_vertex_centered_data(self.field, smoothed=self.smoothed)
+
+        left_index = grid.get_global_startindex()
+        origin = grid.LeftEdge
+        dds = grid.dds
+        right_index = left_index + scalars.shape - 1
+        ug = tvtk.UniformGrid(origin=origin, spacing=dds,
+                              dimensions=grid.ActiveDimensions+1)
+        if self.field not in self.pf.field_info or \
+            self.pf.field_info[self.field].take_log:
+            scalars = na.log10(scalars)
+        ug.point_data.scalars = scalars.transpose().ravel()
+        ug.point_data.scalars.name = self.field
+        if grid.Level != self.min_grid_level + self.number_of_levels - 1:
+            ug.cell_visibility_array = grid.child_mask.transpose().ravel()
+        else:
+            ug.cell_visibility_array = na.ones(
+                    grid.ActiveDimensions, dtype='int').ravel()
+        self._ugs.append((grid,ug))
+        self._hdata_set.set_data_set(level, gid, left_index, right_index, ug)
+
+        self._min_val = min(self._min_val, scalars.min())
+        self._max_val = max(self._max_val, scalars.max())
+
+        gid += 1
+        return gid
+
+    def _add_data_to_ug(self, field):
+        for g, ug in self._ugs:
+            scalars_temp = grid.get_vertex_centered_data(field, smoothed=self.smoothed)
+            ii = ug.point_data.add_array(scalars_temp.transpose().ravel())
+            ug.point_data.get_array(ii).name = field
+
+    def zoom(self, dist, unit='1'):
+        vec = self.scene.camera.focal_point - \
+              self.scene.camera.position
+        self.scene.camera.position += \
+            vec * dist/self._grids[0].pf[unit]
+        self.scene.render()
+
+    def toggle_grid_boundaries(self):
+        if self._grid_boundaries_actor is None:
+            # We don't need to track this stuff right now.
+            ocf = tvtk.OutlineCornerFilter(
+                    executive=tvtk.CompositeDataPipeline(),
+                    corner_factor = 0.5)
+            ocf.input = self._hdata_set
+            ocm = tvtk.HierarchicalPolyDataMapper(
+                input_connection = ocf.output_port)
+            self._grid_boundaries_actor = tvtk.Actor(mapper = ocm)
+            self.scene.add_actor(self._grid_boundaries_actor)
+        else:
+            self._grid_boundaries_actor.visibility = \
+            (not self._grid_boundaries_actor.visibility)
+
+    def _add_sphere(self, origin=(0.0,0.0,0.0), normal=(0,1,0)):
+        sphere = tvtk.Sphere(center=origin, radius=0.25)
+        cutter = tvtk.Cutter(executive = tvtk.CompositeDataPipeline(),
+                             cut_function = sphere)
+        cutter.input = self._hdata_set
+        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
+        smap = tvtk.HierarchicalPolyDataMapper(
+                        scalar_range=(self._min_val, self._max_val),
+                        lookup_table=lut_manager.lut,
+                        input_connection = cutter.output_port)
+        sactor = tvtk.Actor(mapper=smap)
+        self.scene.add_actors(sactor)
+        return sphere, lut_manager
+
+    def _add_plane(self, origin=(0.0,0.0,0.0), normal=(0,1,0)):
+        plane = tvtk.Plane(origin=origin, normal=normal)
+        cutter = tvtk.Cutter(executive = tvtk.CompositeDataPipeline(),
+                             cut_function = plane)
+        cutter.input = self._hdata_set
+        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
+        smap = tvtk.HierarchicalPolyDataMapper(
+                        scalar_range=(self._min_val, self._max_val),
+                        lookup_table=lut_manager.lut,
+                        input_connection = cutter.output_port)
+        sactor = tvtk.Actor(mapper=smap)
+        self.scene.add_actors(sactor)
+        return plane, lut_manager
+
+    def add_plane(self, origin=(0.0,0.0,0.0), normal=(0,1,0)):
+        self.operators.append(self._add_plane(origin, normal))
+        return self.operators[-1]
+
+    def _add_axis_plane(self, axis):
+        normal = [0,0,0]
+        normal[axis] = 1
+        np, lut_manager = self._add_plane(self.center, normal=normal)
+        LE = self.extracted_hierarchy.min_left_edge
+        RE = self.extracted_hierarchy.max_right_edge
+        self.operators.append(MappingPlane(
+                vmin=LE[axis], vmax=RE[axis],
+                vdefault = self.center[axis],
+                post_call = self.scene.render,
+                plane = np, axis=axis, coord=0.0,
+                lut_manager = lut_manager,
+                scene=self.scene))
+
+    def add_x_plane(self):
+        self._add_axis_plane(0)
+        return self.operators[-1]
+
+    def add_y_plane(self):
+        self._add_axis_plane(1)
+        return self.operators[-1]
+
+    def add_z_plane(self):
+        self._add_axis_plane(2)
+        return self.operators[-1]
+
+    def add_contour(self, val=None):
+        if val is None: 
+            if self._min_val != self._min_val:
+                self._min_val = 1.0
+            val = (self._max_val+self._min_val) * 0.5
+        cubes = tvtk.MarchingCubes(
+                    executive = tvtk.CompositeDataPipeline())
+        cubes.input = self._hdata_set
+        cubes.set_value(0, val)
+        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
+        cube_mapper = tvtk.HierarchicalPolyDataMapper(
+                                input_connection = cubes.output_port,
+                                lookup_table=lut_manager.lut)
+        cube_mapper.color_mode = 'map_scalars'
+        cube_mapper.scalar_range = (self._min_val, self._max_val)
+        cube_actor = tvtk.Actor(mapper=cube_mapper)
+        self.scene.add_actors(cube_actor)
+        self.operators.append(MappingMarchingCubes(operator=cubes,
+                    vmin=self._min_val, vmax=self._max_val,
+                    vdefault=val,
+                    mapper = cube_mapper,
+                    post_call = self.scene.render,
+                    lut_manager = lut_manager,
+                    scene=self.scene))
+        return self.operators[-1]
+
+    def add_isocontour(self, val=None):
+        if val is None: val = (self._max_val+self._min_val) * 0.5
+        isocontour = tvtk.ContourFilter(
+                    executive = tvtk.CompositeDataPipeline())
+        isocontour.input = self._hdata_set
+        isocontour.generate_values(1, (val, val))
+        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
+        isocontour_normals = tvtk.PolyDataNormals(
+            executive=tvtk.CompositeDataPipeline())
+        isocontour_normals.input_connection = isocontour.output_port
+        iso_mapper = tvtk.HierarchicalPolyDataMapper(
+                                input_connection = isocontour_normals.output_port,
+                                lookup_table=lut_manager.lut)
+        iso_mapper.scalar_range = (self._min_val, self._max_val)
+        iso_actor = tvtk.Actor(mapper=iso_mapper)
+        self.scene.add_actors(iso_actor)
+        self.operators.append(MappingIsoContour(operator=isocontour,
+                    vmin=self._min_val, vmax=self._max_val,
+                    vdefault=val,
+                    mapper = iso_mapper,
+                    post_call = self.scene.render,
+                    lut_manager = lut_manager,
+                    scene=self.scene))
+        return self.operators[-1]
+
+    def display_points(self):
+        dd = self.pf.h.all_data()
+        points = tvtk.Points()
+        good = (dd["creation_time"] > 0.0)
+        points.data = na.array([ dd["particle_position_%s" % ax][good] for ax in 'xyz' ]).transpose()
+        mass = na.log10(dd["ParticleAge"][good])
+        self.conn = tvtk.CellArray()
+        for i in xrange(mass.shape[0]):
+            self.conn.insert_next_cell(1)
+            self.conn.insert_cell_point(i)
+        self.points = points
+        self.pd = tvtk.PolyData(points = self.points, verts = self.conn)
+        self.pd.point_data.scalars = mass
+        lut = tvtk.LookupTable()
+        self.pdm = tvtk.PolyDataMapper(input = self.pd,
+                                       lookup_table = lut)
+        self.pdm.scalar_range = (mass.min(), mass.max())
+        self.pdm.scalar_mode = 'use_point_data'
+        self.point_actor = tvtk.Actor(mapper = self.pdm)
+        self.scene.add_actor(self.point_actor)
+
+def get_all_parents(grid):
+    parents = []
+    if len(grid.Parents) == 0: return grid
+    for parent in grid.Parents: parents.append(get_all_parents(parent))
+    return list(set(parents))
+
+def run_vtk():
+    gui = pyface.GUI()
+    importer = HierarchyImporter()
+    importer.edit_traits(handler = HierarchyImportHandler(
+            importer = importer))
+    #ehds.edit_traits()
+    gui.start_event_loop()
+
+
+if __name__=="__main__":
+    print "This code probably won't work.  But if you want to give it a try,"
+    print "you need:"
+    print
+    print "VTK (CVS)"
+    print "Mayavi2 (from Enthought)"
+    print
+    print "If you have 'em, give it a try!"
+    print
+    run_vtk()


--- a/yt/gui/tvtk_interface.py	Wed Mar 16 18:42:08 2011 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,692 +0,0 @@
-"""
-This is the preliminary interface to VTK.  Note that as of VTK 5.2, it still
-requires a patchset prepared here:
-http://yt.enzotools.org/files/vtk_composite_data.zip
-
-Author: Matthew Turk <matthewturk at gmail.com>
-Affiliation: KIPAC/SLAC/Stanford
-Homepage: http://yt.enzotools.org/
-License:
-  Copyright (C) 2007-2009 Matthew Turk.  All Rights Reserved.
-
-  This file is part of yt.
-
-  yt is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 3 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-"""
-
-from enthought.tvtk.tools import ivtk
-from enthought.tvtk.api import tvtk 
-from enthought.traits.api import \
-    Float, HasTraits, Instance, Range, Any, Delegate, Tuple, File, Int, Str, \
-    CArray, List, Button, Bool, Property, cached_property
-from enthought.traits.ui.api import View, Item, HGroup, VGroup, TableEditor, \
-    Handler, Controller, RangeEditor, EnumEditor, InstanceEditor
-from enthought.traits.ui.menu import \
-    Menu, Action, Separator, OKCancelButtons, OKButton
-from enthought.traits.ui.table_column import ObjectColumn
-from enthought.tvtk.pyface.api import DecoratedScene
-
-import enthought.pyface.api as pyface
-
-#from yt.reason import *
-import sys
-import numpy as na
-import time, pickle, os, os.path
-from yt.funcs import *
-from yt.analysis_modules.hierarchy_subset.api import \
-        ExtractedHierarchy, ExtractedParameterFile
-
-#from enthought.tvtk.pyface.ui.wx.wxVTKRenderWindowInteractor \
-     #import wxVTKRenderWindowInteractor
-
-from enthought.mayavi.core.lut_manager import LUTManager
-
-#wxVTKRenderWindowInteractor.USE_STEREO = 1
-
-class TVTKMapperWidget(HasTraits):
-    alpha = Float(1.0)
-    post_call = Any
-    lut_manager = Instance(LUTManager)
-
-    def _alpha_changed(self, old, new):
-        self.lut_manager.lut.alpha_range = (new, new)
-        self.post_call()
-
-class MappingPlane(TVTKMapperWidget):
-    plane = Instance(tvtk.Plane)
-    _coord_redit = editor=RangeEditor(format="%0.2e",
-                              low_name='vmin', high_name='vmax',
-                              auto_set=False, enter_set=True)
-    auto_set = Bool(False)
-    traits_view = View(Item('coord', editor=_coord_redit),
-                       Item('auto_set'),
-                       Item('alpha', editor=RangeEditor(
-                              low=0.0, high=1.0,
-                              enter_set=True, auto_set=False)),
-                       Item('lut_manager', show_label=False,
-                            editor=InstanceEditor(), style='custom'))
-    vmin = Float
-    vmax = Float
-
-    def _auto_set_changed(self, old, new):
-        if new is True:
-            self._coord_redit.auto_set = True
-            self._coord_redit.enter_set = False
-        else:
-            self._coord_redit.auto_set = False
-            self._coord_redit.enter_set = True
-
-    def __init__(self, vmin, vmax, vdefault, **traits):
-        HasTraits.__init__(self, **traits)
-        self.vmin = vmin
-        self.vmax = vmax
-        trait = Range(float(vmin), float(vmax), value=vdefault)
-        self.add_trait("coord", trait)
-        self.coord = vdefault
-
-    def _coord_changed(self, old, new):
-        orig = self.plane.origin[:]
-        orig[self.axis] = new
-        self.plane.origin = orig
-        self.post_call()
-
-class MappingMarchingCubes(TVTKMapperWidget):
-    operator = Instance(tvtk.MarchingCubes)
-    mapper = Instance(tvtk.HierarchicalPolyDataMapper)
-    vmin = Float
-    vmax = Float
-    auto_set = Bool(False)
-    _val_redit = RangeEditor(format="%0.2f",
-                             low_name='vmin', high_name='vmax',
-                             auto_set=False, enter_set=True)
-    traits_view = View(Item('value', editor=_val_redit),
-                       Item('auto_set'),
-                       Item('alpha', editor=RangeEditor(
-                            low=0.0, high=1.0,
-                            enter_set=True, auto_set=False,)),
-                       Item('lut_manager', show_label=False,
-                            editor=InstanceEditor(), style='custom'))
-
-    def __init__(self, vmin, vmax, vdefault, **traits):
-        HasTraits.__init__(self, **traits)
-        self.vmin = vmin
-        self.vmax = vmax
-        trait = Range(float(vmin), float(vmax), value=vdefault)
-        self.add_trait("value", trait)
-        self.value = vdefault
-
-    def _auto_set_changed(self, old, new):
-        if new is True:
-            self._val_redit.auto_set = True
-            self._val_redit.enter_set = False
-        else:
-            self._val_redit.auto_set = False
-            self._val_redit.enter_set = True
-
-    def _value_changed(self, old, new):
-        self.operator.set_value(0, new)
-        self.post_call()
-
-class MappingIsoContour(MappingMarchingCubes):
-    operator = Instance(tvtk.ContourFilter)
-
-class CameraPosition(HasTraits):
-    position = CArray(shape=(3,), dtype='float64')
-    focal_point = CArray(shape=(3,), dtype='float64')
-    view_up = CArray(shape=(3,), dtype='float64')
-    clipping_range = CArray(shape=(2,), dtype='float64')
-    distance = Float
-    num_steps = Int(10)
-    orientation_wxyz = CArray(shape=(4,), dtype='float64')
-
-class CameraControl(HasTraits):
-    # Traits
-    positions = List(CameraPosition)
-    yt_scene = Instance('YTScene')
-    center = Delegate('yt_scene')
-    scene = Delegate('yt_scene')
-    camera = Instance(tvtk.OpenGLCamera)
-    reset_position = Instance(CameraPosition)
-    fps = Float(25.0)
-    export_filename = 'frames'
-    periodic = Bool
-
-    # UI elements
-    snapshot = Button()
-    play = Button()
-    export_frames = Button()
-    reset_path = Button()
-    recenter = Button()
-    save_path = Button()
-    load_path = Button()
-    export_path = Button()
-
-    table_def = TableEditor(
-        columns = [ ObjectColumn(name='position'),
-                    ObjectColumn(name='focal_point'),
-                    ObjectColumn(name='view_up'),
-                    ObjectColumn(name='clipping_range'),
-                    ObjectColumn(name='num_steps') ],
-        reorderable=True, deletable=True,
-        sortable=True, sort_model=True,
-        show_toolbar=True,
-        selection_mode='row',
-        selected = 'reset_position'
-                )
-
-    default_view = View(
-                VGroup(
-                  HGroup(
-                    Item('camera', show_label=False),
-                    Item('recenter', show_label=False),
-                    label='Camera'),
-                  HGroup(
-                    Item('snapshot', show_label=False),
-                    Item('play', show_label=False),
-                    Item('export_frames',show_label=False),
-                    Item('reset_path', show_label=False),
-                    Item('save_path', show_label=False),
-                    Item('load_path', show_label=False),
-                    Item('export_path', show_label=False),
-                    Item('export_filename'),
-                    Item('periodic'),
-                    Item('fps'),
-                    label='Playback'),
-                  VGroup(
-                    Item('positions', show_label=False,
-                        editor=table_def),
-                    label='Camera Path'),
-                 ),
-                resizable=True, title="Camera Path Editor",
-                       )
-
-    def _reset_position_changed(self, old, new):
-        if new is None: return
-        cam = self.scene.camera
-        cam.position = new.position
-        cam.focal_point = new.focal_point
-        cam.view_up = new.view_up
-        cam.clipping_range = new.clipping_range
-        self.scene.render()
-
-    def __init__(self, **traits):
-        HasTraits.__init__(self, **traits)
-
-    def take_snapshot(self):
-        cam = self.scene.camera
-        self.positions.append(CameraPosition(
-                position=cam.position,
-                focal_point=cam.focal_point,
-                view_up=cam.view_up,
-                clipping_range=cam.clipping_range,
-                distance=cam.distance,
-                orientation_wxyz=cam.orientation_wxyz))
-
-    def _export_path_fired(self): 
-        dlg = pyface.FileDialog(
-            action='save as',
-            wildcard="*.cpath",
-        )
-        if dlg.open() == pyface.OK:
-            print "Saving:", dlg.path
-            self.export_camera_path(dlg.path)
-
-    def export_camera_path(self, fn):
-        to_dump = dict(positions=[], focal_points=[],
-                       view_ups=[], clipping_ranges=[],
-                       distances=[], orientation_wxyzs=[])
-        def _write(cam):
-            to_dump['positions'].append(cam.position)
-            to_dump['focal_points'].append(cam.focal_point)
-            to_dump['view_ups'].append(cam.view_up)
-            to_dump['clipping_ranges'].append(cam.clipping_range)
-            to_dump['distances'].append(cam.distance)
-            to_dump['orientation_wxyzs'].append(cam.orientation_wxyz)
-        self.step_through(0.0, callback=_write)
-        pickle.dump(to_dump, open(fn, "wb"))
-
-    def _save_path_fired(self): 
-        dlg = pyface.FileDialog(
-            action='save as',
-            wildcard="*.cpath",
-        )
-        if dlg.open() == pyface.OK:
-            print "Saving:", dlg.path
-            self.dump_camera_path(dlg.path)
-
-    def dump_camera_path(self, fn):
-        to_dump = dict(positions=[], focal_points=[],
-                       view_ups=[], clipping_ranges=[],
-                       distances=[], orientation_wxyzs=[],
-                       num_stepss=[])
-        for p in self.positions:
-            to_dump['positions'].append(p.position)
-            to_dump['focal_points'].append(p.focal_point)
-            to_dump['view_ups'].append(p.view_up)
-            to_dump['clipping_ranges'].append(p.clipping_range)
-            to_dump['distances'].append(p.distance)
-            to_dump['num_stepss'].append(p.num_steps) # stupid s
-            to_dump['orientation_wxyzs'].append(p.orientation_wxyz)
-        pickle.dump(to_dump, open(fn, "wb"))
-
-    def _load_path_fired(self):
-        dlg = pyface.FileDialog(
-            action='open',
-            wildcard="*.cpath",
-        )
-        if dlg.open() == pyface.OK:
-            print "Loading:", dlg.path
-            self.load_camera_path(dlg.path)
-
-    def load_camera_path(self, fn):
-        to_use = pickle.load(open(fn, "rb"))
-        self.positions = []
-        for i in range(len(to_use['positions'])):
-            dd = {}
-            for kw in to_use:
-                # Strip the s
-                dd[kw[:-1]] = to_use[kw][i]
-            self.positions.append(
-                CameraPosition(**dd))
-
-    def _recenter_fired(self):
-        self.camera.focal_point = self.center
-        self.scene.render()
-
-    def _snapshot_fired(self):
-        self.take_snapshot()
-
-    def _play_fired(self):
-        self.step_through()
-
-    def _export_frames_fired(self):
-        self.step_through(save_frames=True)
-
-    def _reset_path_fired(self):
-        self.positions = []
-
-    def step_through(self, pause = 1.0, callback=None, save_frames=False):
-        cam = self.scene.camera
-        frame_counter=0
-        if self.periodic:
-            cyclic_pos = self.positions + [self.positions[0]]
-        else:
-            cyclic_pos = self.positions
-        for i in range(len(cyclic_pos)-1):
-            pos1 = cyclic_pos[i]
-            pos2 = cyclic_pos[i+1]
-            r = pos1.num_steps
-            for p in range(pos1.num_steps):
-                po = _interpolate(pos1.position, pos2.position, p, r)
-                fp = _interpolate(pos1.focal_point, pos2.focal_point, p, r)
-                vu = _interpolate(pos1.view_up, pos2.view_up, p, r)
-                cr = _interpolate(pos1.clipping_range, pos2.clipping_range, p, r)
-                _set_cpos(cam, po, fp, vu, cr)
-                self.scene.render()
-                if callback is not None: callback(cam)
-                if save_frames:
-                    self.scene.save("%s_%0.5d.png" % (self.export_filename,frame_counter))
-                else:
-                    time.sleep(pause * 1.0/self.fps)
-                frame_counter += 1
-
-def _interpolate(q1, q2, p, r):
-    return q1 + p*(q2 - q1)/float(r)
-
-def _set_cpos(cam, po, fp, vu, cr):
-    cam.position = po
-    cam.focal_point = fp
-    cam.view_up = vu
-    cam.clipping_range = cr
-
-class HierarchyImporter(HasTraits):
-    pf = Any
-    min_grid_level = Int(0)
-    max_level = Int(1)
-    number_of_levels = Range(0, 13)
-    max_import_levels = Property(depends_on='min_grid_level')
-    field = Str("Density")
-    field_list = List
-    center_on_max = Bool(True)
-    center = CArray(shape = (3,), dtype = 'float64')
-    cache = Bool(True)
-    smoothed = Bool(True)
-    show_grids = Bool(True)
-
-    def _field_list_default(self):
-        fl = self.pf.h.field_list
-        df = self.pf.h.derived_field_list
-        fl.sort(); df.sort()
-        return fl + df
-    
-    default_view = View(Item('min_grid_level',
-                              editor=RangeEditor(low=0,
-                                                 high_name='max_level')),
-                        Item('number_of_levels', 
-                              editor=RangeEditor(low=1,
-                                                 high_name='max_import_levels')),
-                        Item('field', editor=EnumEditor(name='field_list')),
-                        Item('center_on_max'),
-                        Item('center', enabled_when='not object.center_on_max'),
-                        Item('smoothed'),
-                        Item('cache', label='Pre-load data'),
-                        Item('show_grids'),
-                        buttons=OKCancelButtons)
-
-    def _center_default(self):
-        return [0.5,0.5,0.5]
-
-    @cached_property
-    def _get_max_import_levels(self):
-        return min(13, self.pf.h.max_level - self.min_grid_level + 1)
-
-class HierarchyImportHandler(Controller):
-    importer = Instance(HierarchyImporter)
-    
-
-    def close(self, info, is_ok):
-        if is_ok: 
-            yt_scene = YTScene(
-                importer=self.importer)
-        super(Controller, self).close(info, True)
-        return
-
-
-class YTScene(HasTraits):
-
-    # Traits
-    importer = Instance(HierarchyImporter)
-    pf = Delegate("importer")
-    min_grid_level = Delegate("importer")
-    number_of_levels = Delegate("importer")
-    field = Delegate("importer")
-    center = CArray(shape = (3,), dtype = 'float64')
-    center_on_max = Delegate("importer")
-    smoothed = Delegate("importer")
-    cache = Delegate("importer")
-    show_grids = Delegate("importer")
-
-    camera_path = Instance(CameraControl)
-    #window = Instance(ivtk.IVTKWithCrustAndBrowser)
-    #python_shell = Delegate('window')
-    #scene = Delegate('window')
-    scene = Instance(HasTraits)
-    operators = List(HasTraits)
-
-    # State variables
-    _grid_boundaries_actor = None
-
-    # Views
-    def _window_default(self):
-        # Should experiment with passing in a pipeline browser
-        # that has two root objects -- one for TVTKBases, i.e. the render
-        # window, and one that accepts our objects
-        return ivtk.IVTKWithCrustAndBrowser(size=(800,600), stereo=1)
-
-    def _camera_path_default(self):
-        return CameraControl(yt_scene=self, camera=self.scene.camera)
-
-    def __init__(self, **traits):
-        HasTraits.__init__(self, **traits)
-        max_level = min(self.pf.h.max_level,
-                        self.min_grid_level + self.number_of_levels - 1)
-        self.extracted_pf = ExtractedParameterFile(self.pf,
-                             self.min_grid_level, max_level, offset=None)
-        self.extracted_hierarchy = self.extracted_pf.h
-        self._hdata_set = tvtk.HierarchicalBoxDataSet()
-        self._ugs = []
-        self._grids = []
-        self._min_val = 1e60
-        self._max_val = -1e60
-        gid = 0
-        if self.cache:
-            for grid_set in self.extracted_hierarchy.get_levels():
-                for grid in grid_set:
-                    grid[self.field]
-        for l, grid_set in enumerate(self.extracted_hierarchy.get_levels()):
-            gid = self._add_level(grid_set, l, gid)
-        if self.show_grids:
-            self.toggle_grid_boundaries()
-            
-    def _center_default(self):
-        return self.extracted_hierarchy._convert_coords(
-                [0.5, 0.5, 0.5])
-
-    def do_center_on_max(self):
-        self.center = self.extracted_hierarchy._convert_coords(
-            self.pf.h.find_max("Density")[1])
-        self.scene.camera.focal_point = self.center
-
-    def _add_level(self, grid_set, level, gid):
-        for grid in grid_set:
-            self._hdata_set.set_refinement_ratio(level, 2)
-            gid = self._add_grid(grid, gid, level)
-        return gid
-
-    def _add_grid(self, grid, gid, level=0):
-        mylog.debug("Adding grid %s on level %s (%s)",
-                    grid.id, level, grid.Level)
-        if grid in self._grids: return
-        self._grids.append(grid)
-
-        scalars = grid.get_vertex_centered_data(self.field, smoothed=self.smoothed)
-
-        left_index = grid.get_global_startindex()
-        origin = grid.LeftEdge
-        dds = grid.dds
-        right_index = left_index + scalars.shape - 1
-        ug = tvtk.UniformGrid(origin=origin, spacing=dds,
-                              dimensions=grid.ActiveDimensions+1)
-        if self.field not in self.pf.field_info or \
-            self.pf.field_info[self.field].take_log:
-            scalars = na.log10(scalars)
-        ug.point_data.scalars = scalars.transpose().ravel()
-        ug.point_data.scalars.name = self.field
-        if grid.Level != self.min_grid_level + self.number_of_levels - 1:
-            ug.cell_visibility_array = grid.child_mask.transpose().ravel()
-        else:
-            ug.cell_visibility_array = na.ones(
-                    grid.ActiveDimensions, dtype='int').ravel()
-        self._ugs.append((grid,ug))
-        self._hdata_set.set_data_set(level, gid, left_index, right_index, ug)
-
-        self._min_val = min(self._min_val, scalars.min())
-        self._max_val = max(self._max_val, scalars.max())
-
-        gid += 1
-        return gid
-
-    def _add_data_to_ug(self, field):
-        for g, ug in self._ugs:
-            scalars_temp = grid.get_vertex_centered_data(field, smoothed=self.smoothed)
-            ii = ug.point_data.add_array(scalars_temp.transpose().ravel())
-            ug.point_data.get_array(ii).name = field
-
-    def zoom(self, dist, unit='1'):
-        vec = self.scene.camera.focal_point - \
-              self.scene.camera.position
-        self.scene.camera.position += \
-            vec * dist/self._grids[0].pf[unit]
-        self.scene.render()
-
-    def toggle_grid_boundaries(self):
-        if self._grid_boundaries_actor is None:
-            # We don't need to track this stuff right now.
-            ocf = tvtk.OutlineCornerFilter(
-                    executive=tvtk.CompositeDataPipeline(),
-                    corner_factor = 0.5)
-            ocf.input = self._hdata_set
-            ocm = tvtk.HierarchicalPolyDataMapper(
-                input_connection = ocf.output_port)
-            self._grid_boundaries_actor = tvtk.Actor(mapper = ocm)
-            self.scene.add_actor(self._grid_boundaries_actor)
-        else:
-            self._grid_boundaries_actor.visibility = \
-            (not self._grid_boundaries_actor.visibility)
-
-    def _add_sphere(self, origin=(0.0,0.0,0.0), normal=(0,1,0)):
-        sphere = tvtk.Sphere(center=origin, radius=0.25)
-        cutter = tvtk.Cutter(executive = tvtk.CompositeDataPipeline(),
-                             cut_function = sphere)
-        cutter.input = self._hdata_set
-        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
-        smap = tvtk.HierarchicalPolyDataMapper(
-                        scalar_range=(self._min_val, self._max_val),
-                        lookup_table=lut_manager.lut,
-                        input_connection = cutter.output_port)
-        sactor = tvtk.Actor(mapper=smap)
-        self.scene.add_actors(sactor)
-        return sphere, lut_manager
-
-    def _add_plane(self, origin=(0.0,0.0,0.0), normal=(0,1,0)):
-        plane = tvtk.Plane(origin=origin, normal=normal)
-        cutter = tvtk.Cutter(executive = tvtk.CompositeDataPipeline(),
-                             cut_function = plane)
-        cutter.input = self._hdata_set
-        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
-        smap = tvtk.HierarchicalPolyDataMapper(
-                        scalar_range=(self._min_val, self._max_val),
-                        lookup_table=lut_manager.lut,
-                        input_connection = cutter.output_port)
-        sactor = tvtk.Actor(mapper=smap)
-        self.scene.add_actors(sactor)
-        return plane, lut_manager
-
-    def add_plane(self, origin=(0.0,0.0,0.0), normal=(0,1,0)):
-        self.operators.append(self._add_plane(origin, normal))
-        return self.operators[-1]
-
-    def _add_axis_plane(self, axis):
-        normal = [0,0,0]
-        normal[axis] = 1
-        np, lut_manager = self._add_plane(self.center, normal=normal)
-        LE = self.extracted_hierarchy.min_left_edge
-        RE = self.extracted_hierarchy.max_right_edge
-        self.operators.append(MappingPlane(
-                vmin=LE[axis], vmax=RE[axis],
-                vdefault = self.center[axis],
-                post_call = self.scene.render,
-                plane = np, axis=axis, coord=0.0,
-                lut_manager = lut_manager,
-                scene=self.scene))
-
-    def add_x_plane(self):
-        self._add_axis_plane(0)
-        return self.operators[-1]
-
-    def add_y_plane(self):
-        self._add_axis_plane(1)
-        return self.operators[-1]
-
-    def add_z_plane(self):
-        self._add_axis_plane(2)
-        return self.operators[-1]
-
-    def add_contour(self, val=None):
-        if val is None: 
-            if self._min_val != self._min_val:
-                self._min_val = 1.0
-            val = (self._max_val+self._min_val) * 0.5
-        cubes = tvtk.MarchingCubes(
-                    executive = tvtk.CompositeDataPipeline())
-        cubes.input = self._hdata_set
-        cubes.set_value(0, val)
-        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
-        cube_mapper = tvtk.HierarchicalPolyDataMapper(
-                                input_connection = cubes.output_port,
-                                lookup_table=lut_manager.lut)
-        cube_mapper.color_mode = 'map_scalars'
-        cube_mapper.scalar_range = (self._min_val, self._max_val)
-        cube_actor = tvtk.Actor(mapper=cube_mapper)
-        self.scene.add_actors(cube_actor)
-        self.operators.append(MappingMarchingCubes(operator=cubes,
-                    vmin=self._min_val, vmax=self._max_val,
-                    vdefault=val,
-                    mapper = cube_mapper,
-                    post_call = self.scene.render,
-                    lut_manager = lut_manager,
-                    scene=self.scene))
-        return self.operators[-1]
-
-    def add_isocontour(self, val=None):
-        if val is None: val = (self._max_val+self._min_val) * 0.5
-        isocontour = tvtk.ContourFilter(
-                    executive = tvtk.CompositeDataPipeline())
-        isocontour.input = self._hdata_set
-        isocontour.generate_values(1, (val, val))
-        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
-        isocontour_normals = tvtk.PolyDataNormals(
-            executive=tvtk.CompositeDataPipeline())
-        isocontour_normals.input_connection = isocontour.output_port
-        iso_mapper = tvtk.HierarchicalPolyDataMapper(
-                                input_connection = isocontour_normals.output_port,
-                                lookup_table=lut_manager.lut)
-        iso_mapper.scalar_range = (self._min_val, self._max_val)
-        iso_actor = tvtk.Actor(mapper=iso_mapper)
-        self.scene.add_actors(iso_actor)
-        self.operators.append(MappingIsoContour(operator=isocontour,
-                    vmin=self._min_val, vmax=self._max_val,
-                    vdefault=val,
-                    mapper = iso_mapper,
-                    post_call = self.scene.render,
-                    lut_manager = lut_manager,
-                    scene=self.scene))
-        return self.operators[-1]
-
-    def display_points(self):
-        dd = self.pf.h.all_data()
-        points = tvtk.Points()
-        good = (dd["creation_time"] > 0.0)
-        points.data = na.array([ dd["particle_position_%s" % ax][good] for ax in 'xyz' ]).transpose()
-        mass = na.log10(dd["ParticleAge"][good])
-        self.conn = tvtk.CellArray()
-        for i in xrange(mass.shape[0]):
-            self.conn.insert_next_cell(1)
-            self.conn.insert_cell_point(i)
-        self.points = points
-        self.pd = tvtk.PolyData(points = self.points, verts = self.conn)
-        self.pd.point_data.scalars = mass
-        lut = tvtk.LookupTable()
-        self.pdm = tvtk.PolyDataMapper(input = self.pd,
-                                       lookup_table = lut)
-        self.pdm.scalar_range = (mass.min(), mass.max())
-        self.pdm.scalar_mode = 'use_point_data'
-        self.point_actor = tvtk.Actor(mapper = self.pdm)
-        self.scene.add_actor(self.point_actor)
-
-def get_all_parents(grid):
-    parents = []
-    if len(grid.Parents) == 0: return grid
-    for parent in grid.Parents: parents.append(get_all_parents(parent))
-    return list(set(parents))
-
-def run_vtk():
-    gui = pyface.GUI()
-    importer = HierarchyImporter()
-    importer.edit_traits(handler = HierarchyImportHandler(
-            importer = importer))
-    #ehds.edit_traits()
-    gui.start_event_loop()
-
-
-if __name__=="__main__":
-    print "This code probably won't work.  But if you want to give it a try,"
-    print "you need:"
-    print
-    print "VTK (CVS)"
-    print "Mayavi2 (from Enthought)"
-    print
-    print "If you have 'em, give it a try!"
-    print
-    run_vtk()


--- a/yt/utilities/_amr_utils/VolumeIntegrator.pyx	Wed Mar 16 18:42:08 2011 -0400
+++ b/yt/utilities/_amr_utils/VolumeIntegrator.pyx	Wed Mar 16 18:42:57 2011 -0400
@@ -63,6 +63,7 @@
     double fmod(double x, double y)
     double log2(double x)
     long int lrint(double x)
+    double fabs(double x)
 
 cdef extern from "FixedInterpolator.h":
     np.float64_t fast_interpolate(int ds[3], int ci[3], np.float64_t dp[3],
@@ -596,6 +597,7 @@
             if tdelta[i] < 0: tdelta[i] *= -1
         # We have to jumpstart our calculation
         enter_t = intersect_t
+        hit = 0
         while 1:
             # dims here is one less than the dimensions of the data,
             # but we are tracing on the grid, not on the data...
@@ -634,7 +636,7 @@
                     cur_ind[2] += step[2]
                     enter_t = tmax[2]
                     tmax[2] += tdelta[2]
-            if enter_t > 1.0: break
+            if enter_t >= 1.0: break
         if return_t != NULL: return_t[0] = exit_t
         return hit
 
@@ -913,14 +915,14 @@
     cdef AdaptiveRayPacket **lpacket_pointers
 
     def __cinit__(self, center, rays_per_cell, initial_nside,
-                  np.float64_t normalization, brick_list, int max_nside = 8096):
+                  np.float64_t normalization, brick_list, int max_nside = 8192):
         cdef int i
         self.max_nside = max_nside
         self.center[0] = center[0]
         self.center[1] = center[1]
         self.center[2] = center[2]
         self.rays_per_cell = rays_per_cell
-        cdef AdaptiveRayPacket *ray = self.first
+        cdef AdaptiveRayPacket *ray
         cdef AdaptiveRayPacket *last = NULL
         cdef PartitionedGrid pg
         cdef double v_dir[3]
@@ -942,7 +944,7 @@
             ray.prev = last
             ray.ipix = i
             ray.nside = initial_nside
-            ray.t = 0.0 # Start in the first brick
+            ray.t = 0.0 # We assume we are not on a brick boundary
             healpix_interface.pix2vec_nest(initial_nside, i, v_dir)
             ray.v_dir[0] = v_dir[0] * normalization
             ray.v_dir[1] = v_dir[1] * normalization
@@ -1006,29 +1008,34 @@
         cdef AdaptiveRayPacket *ray = self.packet_pointers[pgi]
         cdef AdaptiveRayPacket *next
         cdef int *grid_neighbors = self.find_neighbors(pgi, pg.dds[0], ledges, redges)
+        cdef np.float64_t enter_t, dt, offpos[3]
+        cdef int found_a_home, hit
+        #print "Grid: ", pgi, "has", grid_neighbors[0], "neighbors"
         while ray != NULL:
             # Note that we may end up splitting a ray such that it ends up
-            # outside the brick!
+            # outside the brick!  This will likely cause them to get lost.
             #print count
             count +=1
-            #if count > 10+self.nrays or ray.cgi != pgi:
-            #    raise RuntimeError
             # We don't need to check for intersection anymore, as we are the
             # Ruler of the planet Omicron Persei 8
             #if self.intersects(ray, pg):
             ray = self.refine_ray(ray, domega, pg.dds[0],
                                   pg.left_edge, pg.right_edge)
-            pg.integrate_ray(self.center, ray.v_dir, ray.value,
-                             tf, &ray.t)
+            enter_t = ray.t
+            hit = pg.integrate_ray(self.center, ray.v_dir, ray.value, tf, &ray.t)
+            if hit == 0: dt = 0.0
+            else: dt = (ray.t - enter_t)/hit
             for i in range(3):
-                ray.pos[i] = ray.v_dir[i] * (ray.t + 1e-8) + self.center[i]
+                ray.pos[i] = ray.v_dir[i] * ray.t + self.center[i]
+                offpos[i] = ray.pos[i] + ray.v_dir[i] * 1e-5*dt
             # We set 'next' after the refinement has occurred
             next = ray.brick_next
+            found_a_home = 0
             for j in range(grid_neighbors[0]):
                 i = grid_neighbors[j+1]
-                if ((ledges[i, 0] <= ray.pos[0] <= redges[i, 0]) and
-                    (ledges[i, 1] <= ray.pos[1] <= redges[i, 1]) and
-                    (ledges[i, 2] <= ray.pos[2] <= redges[i, 2])):
+                if ((ledges[i, 0] <= offpos[0] <= redges[i, 0]) and
+                    (ledges[i, 1] <= offpos[1] <= redges[i, 1]) and
+                    (ledges[i, 2] <= offpos[2] <= redges[i, 2])):
                     if self.lpacket_pointers[i] == NULL:
                         self.packet_pointers[i] = \
                         self.lpacket_pointers[i] = ray
@@ -1038,6 +1045,7 @@
                         self.lpacket_pointers[i] = ray
                         ray.brick_next = NULL
                     #ray.cgi = i
+                    found_a_home = 1
                     break
             ray = next
         free(grid_neighbors)
@@ -1062,18 +1070,18 @@
         gre[2] = redges[this_grid, 2] + dds
         for i in range(this_grid+1, ledges.shape[0]):
             # Check for overlap
-            if ((gle[0] < redges[i, 0] and gre[0] > ledges[i, 0]) and
-                (gle[1] < redges[i, 1] and gre[1] > ledges[i, 1]) and
-                (gle[2] < redges[i, 2] and gre[2] > ledges[i, 2])):
+            if ((gle[0] <= redges[i, 0] and gre[0] >= ledges[i, 0]) and
+                (gle[1] <= redges[i, 1] and gre[1] >= ledges[i, 1]) and
+                (gle[2] <= redges[i, 2] and gre[2] >= ledges[i, 2])):
                 count += 1
-        cdef int *tr = <int *> malloc(sizeof(int) * count + 1)
+        cdef int *tr = <int *> malloc(sizeof(int) * (count + 1))
         tr[0] = count
         count = 0
         for i in range(this_grid+1, ledges.shape[0]):
             # Check for overlap
-            if ((gle[0] < redges[i, 0] and gre[0] > ledges[i, 0]) and
-                (gle[1] < redges[i, 1] and gre[1] > ledges[i, 1]) and
-                (gle[2] < redges[i, 2] and gre[2] > ledges[i, 2])):
+            if ((gle[0] <= redges[i, 0] and gre[0] >= ledges[i, 0]) and
+                (gle[1] <= redges[i, 1] and gre[1] >= ledges[i, 1]) and
+                (gle[2] <= redges[i, 2] and gre[2] >= ledges[i, 2])):
                 tr[count + 1] = i
                 count += 1
         return tr


--- a/yt/utilities/command_line.py	Wed Mar 16 18:42:08 2011 -0400
+++ b/yt/utilities/command_line.py	Wed Mar 16 18:42:57 2011 -0400
@@ -591,6 +591,25 @@
                             weight="CellMassMsun")
         ph.modify["line"](pr.data["Density"], pr.data["Temperature"])
         pc.save()
+
+    @cmdln.option("-d", "--desc", action="store",
+                  default = False, dest="desc",
+                  help="Description for this pasteboard entry")
+    def do_pasteboard(self, subcmd, opts, arg):
+        """
+        Place a file into the user's pasteboard
+        """
+        if opts.desc is None: raise RuntimeError
+        from yt.utilities.pasteboard import PostInventory
+        pp = PostInventory()
+        pp.add_post(arg, desc=opts.desc)
+
+    @cmdln.option("-o", "--output", action="store",
+                  default = None, dest="output_fn",
+                  help="File to output to; else, print.")
+    def do_pastegrab(self, subcmd, opts, username, paste_id):
+        from yt.utilities.pasteboard import retrieve_pastefile
+        retrieve_pastefile(username, paste_id, opts.output_fn)
     
 def run_main():
     for co in ["--parallel", "--paste"]:


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/utilities/parallel_tools/task_queue.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,149 @@
+"""
+A task queue for distributing work to worker agents
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import threading
+from yt.funcs import *
+
+# The idea here is that we have a set of tasks, which we want to distribute.
+# We'll try to make this forward-compatible.  To do so, we want to support the
+# idea that there's a single, global set of tasks, as well as consumers that
+# receive tasks from the main controller.  These consumers then pass them out
+# to executors.
+#
+# The middle level, the "Consumer," is only really distinct from the executor
+# in the case that there is an MPI subcommunicator.  The reason for the
+# separation is so that the controller only communicates with a single member
+# of each subcommunicator, which then passes that info back out.
+
+def locked(func):
+    @wraps(func)
+    def exclusive(self, *args, **kwargs):
+        with self.lock:
+            return func(self, *args, **kwargs)
+    return exclusive
+
+class YTTaskCommunicator(object):
+    # This should carefully be checked for a race condition, particularly in
+    # the wait() function
+    def __init__(self, interval = 2.0):
+        self.interval = interval
+        self.task_id = None
+        self.waiting = False
+        self.lock = threading.Lock()
+
+    @locked
+    def send_task(self, task_id):
+        self.task_id = task_id
+
+    @locked
+    def query(self):
+        return (self.waiting and self.task_id is None)
+
+    def wait(self):
+        self.waiting = True
+        while self.task_id is None:
+            time.sleep(self.interval)
+        with self.lock:
+            self.waiting = False
+            new_task_id = self.task_id
+            self.task_id = None
+        return new_task_id
+
+class YTTaskQueueController(threading.Thread):
+    # There's only one of these for every instance of yt -- whether than
+    # instance be spread across processors or not.
+    # We assume that this will exist in the process space of a consumer, so it
+    # will be threading based.
+    def __init__(self, tasks, interval = 2.0, communicators = None):
+        self.assignments = []
+        self.interval = interval
+        # Communicators can be anything but they have to implement a mechanism
+        # for saying, "I'm ready" and "I'm done"
+        self.tasks = tasks
+        self.communicators = communicators
+        threading.Thread.__init__(self)
+
+    def run(self):
+        # Now we bootstrap
+        for i,c in enumerate(self.communicators):
+            self.assignments.append(i)
+            if i == len(self.tasks): break
+            c.send_task(i)
+        while len(self.assignments) < len(self.tasks):
+            time.sleep(self.interval)
+            for i,c in enumerate(self.communicators):
+                if not c.query(): continue
+                print "Sending assignment %s to %s" % (
+                    len(self.assignments), i)
+                c.send_task(len(self.assignments))
+                self.assignments.append(i)
+                if len(self.assignments) >= len(self.tasks): break
+        terminated = 0
+        while terminated != len(self.communicators):
+            for i,c in enumerate(self.communicators):
+                if not c.query(): continue
+                c.send_task(-1)
+                terminated += 1
+                print "Terminated %s" % (i)
+
+class YTTaskQueueConsumer(object):
+    # One of these will exist per individual MPI task or one per MPI
+    # subcommunicator, depending on the level of parallelism.  They serve to
+    # talk to the YTTaskQueueController on one side and possibly several
+    # YTTaskExecutors on the other.
+    #
+    # One potential setup for this, when using MPI, would be to have the
+    # Executors each have one of these, but only the head process of that
+    # subcommunicator possess an external communicator.  Then in next_task,
+    # if the external communicator exists, one would probe that; otherwise,
+    # accept a broadcast from the internal communicator's 0th task.
+    def __init__(self, external_communicator, internal_communicator):
+        self.external_communicator = external_communicator
+        self.internal_communicator = internal_communicator
+
+    def next_task(self):
+        next_task = self.external_communicator.wait()
+        #self.internal_communicator.notify(next_task)
+        return next_task
+
+class YTTaskExecutor(object):
+    _count = 0
+    # One of these will exist per computational actor
+    def __init__(self, tasks, communicator):
+        self.communicator = communicator
+        self.tasks = tasks
+        self.name = "Runner%03s" % (self.__class__._count)
+        self.__class__._count += 1
+
+    def run(self):
+        # Note that right now this only works for a 1:1 mapping of
+        # YTTaskQueueConsumer to YTTaskExecutor
+        next_task = None
+        while 1:
+            next_task = self.communicator.next_task()
+            if next_task == -1: break
+            print "Executing on %s" % (self.name),
+            self.tasks[next_task]()
+        print "Concluded on %s" % (self.name)


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/utilities/pasteboard.py	Wed Mar 16 18:42:57 2011 -0400
@@ -0,0 +1,160 @@
+"""
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from mercurial import ui, repo, commands, hg
+import json
+import os
+import time
+import uuid
+import urllib
+
+from yt.config import ytcfg
+
+def _get_last_mod(filectx):
+    rev = filectx.filectx(filectx.filerev())
+    return rev
+
+class PostInventory(object):
+    def __init__(self, uu = None, repo_fn = None):
+        if uu is None: uu = ui.ui()
+        if repo_fn is None: repo_fn = ytcfg.get("yt","pasteboard_repo")
+        if repo_fn == '':
+            raise KeyError("~/.yt/config:[yt]pasteboard_repo")
+        self.repo_fn = repo_fn
+        self.bbrepo = hg.repository(uu, repo_fn)
+        config_fn = os.path.join(repo_fn, ".hg", "hgrc")
+        uu.readconfig(config_fn)
+        commands.pull(uu, self.bbrepo)
+        commands.update(uu, self.bbrepo, clean=True)
+        if not os.path.exists(os.path.join(repo_fn, "posts")):
+            os.makedirs(os.path.join(repo_fn, "posts"))
+        if not os.path.exists(os.path.join(repo_fn, "html")):
+            os.makedirs(os.path.join(repo_fn, "html"))
+        self.uu = uu
+
+    def regenerate_posts(self):
+        self.posts = []
+        for file in self.bbrepo["tip"]:
+            if file.startswith("posts/") and file.count("/") == 1 \
+               and not file.endswith(".desc"):
+                filectx = self.bbrepo["tip"][file]
+                last_mod = _get_last_mod(filectx).date()
+                self.posts.append((last_mod[0] + last_mod[1], file))
+        self.posts.sort()
+        self.posts = self.posts[::-1]
+
+    def add_post(self, filename, desc = None,
+                 uu = None, highlight = True, push = True):
+        # We assume the post filename exists in the current space
+        self.regenerate_posts()
+        if uu is None: uu = self.uu
+        prefix = uuid.uuid4()
+        name = "%s-%s" % (prefix, os.path.basename(filename))
+        name_noext = name.replace(".","-")
+        hfn = "html/%s.html" % (name_noext)
+        pfn = "posts/%s" % (name)
+        abs_pfn = os.path.join(self.repo_fn, pfn)
+        abs_hfn = os.path.join(self.repo_fn, hfn)
+        if desc is not None:
+            open(abs_pfn + ".desc", "w").write(desc)
+        self.posts.insert(0, (int(time.time()), "posts/%s" % name))
+        if not os.path.exists(abs_pfn):
+            open(abs_pfn,"w").write(open(filename).read())
+        inv_fname = self.update_inventory()
+        if highlight and not name.endswith(".html"):
+            from pygments.cmdline import main as pygmain
+            rv = pygmain(["pygmentize", "-o", abs_hfn,
+                          "-O", "full", abs_pfn])
+        if not highlight or rv:
+            content = open(abs_pfn).read()
+            open(abs_hfn, "w").write(
+                "<HTML><BODY><PRE>" + content + "</PRE></BODY></HTML>")
+        to_manage = [abs_pfn, abs_hfn]
+        if desc is not None: to_manage.append(abs_pfn + ".desc")
+        commands.add(uu, self.bbrepo, *to_manage)
+        commands.commit(uu, self.bbrepo, *(to_manage + [inv_fname]),
+                        message="Adding %s" % name)
+        if push: commands.push(uu, self.bbrepo)
+
+    def update_inventory(self):
+        tip = self.bbrepo["tip"]
+        vals = []
+        for t, pfn in self.posts:
+            dfn = pfn + ".desc"
+            if dfn in tip:
+                d = tip[dfn].data()
+                uname = _get_last_mod(tip[dfn]).user()
+            elif pfn not in tip:
+                abs_pfn = os.path.join(self.repo_fn, pfn)
+                uname = self.uu.config("ui","username")
+                if os.path.exists(abs_pfn + ".desc"):
+                    d = open(abs_pfn + ".desc").read()
+                else:
+                    d = open(abs_pfn).read()
+            else:
+                d = tip[pfn].data()
+                uname = _get_last_mod(tip[pfn]).user()
+            if len(d) > 80: d = d[:77] + "..."
+            name_noext = pfn[6:].replace(".","-")
+            vals.append(dict(modified = time.ctime(t),
+                             modtime = t,
+                             fullname = pfn,
+                             htmlname = "html/%s.html" % name_noext,
+                             name = pfn[43:], # 6 for posts/ then 36 for UUID
+                             username = uname,
+                             descr = d)) 
+        fn = os.path.join(self.repo_fn, "inventory.json")
+        f = open(fn, "w")
+        f.write("var inventory_data = ")
+        json.dump(vals, f, indent = 1)
+        f.write(";")
+        return fn
+
+def retrieve_pastefile(username, paste_id, output_fn = None):
+    # First we get the username's inventory.json
+    s = urllib.urlopen("http://%s.bitbucket.org/inventory.json" % (username))
+    data = s.read()
+    # This is an ugly, ugly hack for my lack of understanding of how best to
+    # handle this JSON stuff.
+    data = data[data.find("=")+1:data.rfind(";")] 
+    #import pdb;pdb.set_trace()
+    inv = json.loads(data)
+    k = None
+    if len(paste_id) == 36:
+        # Then this is a UUID
+        for k in inv:
+            if k['fullname'][6:42] == paste_id: break
+    elif len(paste_id) == 10:
+        pp = int(paste_id)
+        for k in inv:
+            if k['modtime'] == pp: break
+    if k is None: raise KeyError(k)
+    # k is our key
+    url = "http://%s.bitbucket.org/%s" % (username, k['fullname'])
+    s = urllib.urlopen(url)
+    data = s.read()
+    if output_fn is not None:
+        if os.path.exists(output_fn): raise IOError(output_fn)
+        open(output_fn, "w").write(data)
+    else:
+        print data


--- a/yt/visualization/volume_rendering/camera.py	Wed Mar 16 18:42:08 2011 -0400
+++ b/yt/visualization/volume_rendering/camera.py	Wed Mar 16 18:42:57 2011 -0400
@@ -606,7 +606,7 @@
                  transfer_function = None, fields = None,
                  sub_samples = 5, log_fields = None, volume = None,
                  pf = None, use_kd=True, no_ghost=False,
-                 rays_per_cell = 0.1):
+                 rays_per_cell = 0.1, max_nside = 8192):
         if pf is not None: self.pf = pf
         self.center = na.array(center, dtype='float64')
         self.radius = radius
@@ -625,6 +625,7 @@
         self.volume = volume
         self.initial_nside = nside
         self.rays_per_cell = rays_per_cell
+        self.max_nside = max_nside
 
     def snapshot(self, fn = None):
         tfp = TransferFunctionProxy(self.transfer_function)
@@ -637,9 +638,17 @@
         bricks = [b for b in self.volume.traverse(None, self.center, None)][::-1]
         left_edges = na.array([b.LeftEdge for b in bricks])
         right_edges = na.array([b.RightEdge for b in bricks])
+        min_dx = min(((b.RightEdge[0] - b.LeftEdge[0])/b.my_data[0].shape[0]
+                     for b in bricks))
+        # We jitter a bit if we're on a boundary of our initial grid
+        for i in range(3):
+            if bricks[0].LeftEdge[i] == self.center[i]:
+                self.center += 1e-2 * min_dx
+            elif bricks[0].RightEdge[i] == self.center[i]:
+                self.center -= 1e-2 * min_dx
         ray_source = AdaptiveRaySource(self.center, self.rays_per_cell,
                                        self.initial_nside, self.radius,
-                                       bricks)
+                                       bricks, self.max_nside)
         for i,brick in enumerate(bricks):
             ray_source.integrate_brick(brick, tfp, i, left_edges, right_edges)
             total_cells += na.prod(brick.my_data[0].shape)

Repository URL: https://bitbucket.org/yt_analysis/yt/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.



More information about the yt-svn mailing list