1bc531c0495e

Merge the big Flask rewrite.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Fri, 11 Jun 2010 21:25:17 -0400
parents a909c2ba47f0 (current diff) 05de33c5b8fe (diff)
children 5c05c7aced32
branches/tags (none)
files bundled/cherrypy/MANIFEST.in bundled/cherrypy/README.txt bundled/cherrypy/cherrypy/LICENSE.txt bundled/cherrypy/cherrypy/__init__.py bundled/cherrypy/cherrypy/_cpchecker.py bundled/cherrypy/cherrypy/_cpconfig.py bundled/cherrypy/cherrypy/_cpdispatch.py bundled/cherrypy/cherrypy/_cperror.py bundled/cherrypy/cherrypy/_cplogging.py bundled/cherrypy/cherrypy/_cpmodpy.py bundled/cherrypy/cherrypy/_cpnative_server.py bundled/cherrypy/cherrypy/_cpreqbody.py bundled/cherrypy/cherrypy/_cprequest.py bundled/cherrypy/cherrypy/_cpserver.py bundled/cherrypy/cherrypy/_cpthreadinglocal.py bundled/cherrypy/cherrypy/_cptools.py bundled/cherrypy/cherrypy/_cptree.py bundled/cherrypy/cherrypy/_cpwsgi.py bundled/cherrypy/cherrypy/_cpwsgi_server.py bundled/cherrypy/cherrypy/cherryd bundled/cherrypy/cherrypy/favicon.ico bundled/cherrypy/cherrypy/lib/__init__.py bundled/cherrypy/cherrypy/lib/auth.py bundled/cherrypy/cherrypy/lib/auth_basic.py bundled/cherrypy/cherrypy/lib/auth_digest.py bundled/cherrypy/cherrypy/lib/caching.py bundled/cherrypy/cherrypy/lib/covercp.py bundled/cherrypy/cherrypy/lib/cptools.py bundled/cherrypy/cherrypy/lib/encoding.py bundled/cherrypy/cherrypy/lib/http.py bundled/cherrypy/cherrypy/lib/httpauth.py bundled/cherrypy/cherrypy/lib/httputil.py bundled/cherrypy/cherrypy/lib/jsontools.py bundled/cherrypy/cherrypy/lib/profiler.py bundled/cherrypy/cherrypy/lib/reprconf.py bundled/cherrypy/cherrypy/lib/sessions.py bundled/cherrypy/cherrypy/lib/static.py bundled/cherrypy/cherrypy/lib/xmlrpc.py bundled/cherrypy/cherrypy/process/__init__.py bundled/cherrypy/cherrypy/process/plugins.py bundled/cherrypy/cherrypy/process/servers.py bundled/cherrypy/cherrypy/process/win32.py bundled/cherrypy/cherrypy/process/wspbus.py bundled/cherrypy/cherrypy/scaffold/__init__.py bundled/cherrypy/cherrypy/scaffold/apache-fcgi.conf bundled/cherrypy/cherrypy/scaffold/example.conf bundled/cherrypy/cherrypy/scaffold/site.conf bundled/cherrypy/cherrypy/scaffold/static/made_with_cherrypy_small.png bundled/cherrypy/cherrypy/test/__init__.py bundled/cherrypy/cherrypy/test/benchmark.py bundled/cherrypy/cherrypy/test/checkerdemo.py bundled/cherrypy/cherrypy/test/fcgi.conf bundled/cherrypy/cherrypy/test/helper.py bundled/cherrypy/cherrypy/test/logtest.py bundled/cherrypy/cherrypy/test/modfcgid.py bundled/cherrypy/cherrypy/test/modpy.py bundled/cherrypy/cherrypy/test/modwsgi.py bundled/cherrypy/cherrypy/test/py25.py bundled/cherrypy/cherrypy/test/sessiondemo.py bundled/cherrypy/cherrypy/test/static/dirback.jpg bundled/cherrypy/cherrypy/test/static/index.html bundled/cherrypy/cherrypy/test/style.css bundled/cherrypy/cherrypy/test/test.pem bundled/cherrypy/cherrypy/test/test.py bundled/cherrypy/cherrypy/test/test_auth_basic.py bundled/cherrypy/cherrypy/test/test_auth_digest.py bundled/cherrypy/cherrypy/test/test_bus.py bundled/cherrypy/cherrypy/test/test_caching.py bundled/cherrypy/cherrypy/test/test_config.py bundled/cherrypy/cherrypy/test/test_config_server.py bundled/cherrypy/cherrypy/test/test_conn.py bundled/cherrypy/cherrypy/test/test_core.py bundled/cherrypy/cherrypy/test/test_dynamicobjectmapping.py bundled/cherrypy/cherrypy/test/test_encoding.py bundled/cherrypy/cherrypy/test/test_etags.py bundled/cherrypy/cherrypy/test/test_http.py bundled/cherrypy/cherrypy/test/test_httpauth.py bundled/cherrypy/cherrypy/test/test_httplib.py bundled/cherrypy/cherrypy/test/test_json.py bundled/cherrypy/cherrypy/test/test_logging.py bundled/cherrypy/cherrypy/test/test_mime.py bundled/cherrypy/cherrypy/test/test_misc_tools.py bundled/cherrypy/cherrypy/test/test_objectmapping.py bundled/cherrypy/cherrypy/test/test_proxy.py bundled/cherrypy/cherrypy/test/test_refleaks.py bundled/cherrypy/cherrypy/test/test_request_obj.py bundled/cherrypy/cherrypy/test/test_routes.py bundled/cherrypy/cherrypy/test/test_session.py bundled/cherrypy/cherrypy/test/test_sessionauthenticate.py bundled/cherrypy/cherrypy/test/test_states.py bundled/cherrypy/cherrypy/test/test_states_demo.py bundled/cherrypy/cherrypy/test/test_static.py bundled/cherrypy/cherrypy/test/test_tools.py bundled/cherrypy/cherrypy/test/test_tutorials.py bundled/cherrypy/cherrypy/test/test_virtualhost.py bundled/cherrypy/cherrypy/test/test_wsgi_ns.py bundled/cherrypy/cherrypy/test/test_wsgi_vhost.py bundled/cherrypy/cherrypy/test/test_wsgiapps.py bundled/cherrypy/cherrypy/test/test_xmlrpc.py bundled/cherrypy/cherrypy/test/webtest.py bundled/cherrypy/cherrypy/tutorial/README.txt bundled/cherrypy/cherrypy/tutorial/__init__.py bundled/cherrypy/cherrypy/tutorial/bonus-sqlobject.py bundled/cherrypy/cherrypy/tutorial/custom_error.html bundled/cherrypy/cherrypy/tutorial/pdf_file.pdf bundled/cherrypy/cherrypy/tutorial/tut01_helloworld.py bundled/cherrypy/cherrypy/tutorial/tut02_expose_methods.py bundled/cherrypy/cherrypy/tutorial/tut03_get_and_post.py bundled/cherrypy/cherrypy/tutorial/tut04_complex_site.py bundled/cherrypy/cherrypy/tutorial/tut05_derived_objects.py bundled/cherrypy/cherrypy/tutorial/tut06_default_method.py bundled/cherrypy/cherrypy/tutorial/tut07_sessions.py bundled/cherrypy/cherrypy/tutorial/tut08_generators_and_yield.py bundled/cherrypy/cherrypy/tutorial/tut09_files.py bundled/cherrypy/cherrypy/tutorial/tut10_http_errors.py bundled/cherrypy/cherrypy/tutorial/tutorial.conf bundled/cherrypy/cherrypy/wsgiserver/__init__.py bundled/cherrypy/cherrypy/wsgiserver/ssl_builtin.py bundled/cherrypy/cherrypy/wsgiserver/ssl_pyopenssl.py bundled/cherrypy/docs/cherryd.1 bundled/cherrypy/ez_setup.py bundled/cherrypy/make-sdist bundled/cherrypy/setup.py bundled/cherrypy/visuals/cherrypy_logo_big.png bundled/cherrypy/visuals/cherrypy_logo_small.jpg bundled/cherrypy/visuals/favicon.ico bundled/cherrypy/visuals/made_with_cherrypy_big.png bundled/cherrypy/visuals/made_with_cherrypy_small.png bundled/jinja2/artwork/jinjalogo.svg bundled/jinja2/docs/Makefile bundled/jinja2/docs/_build/.ignore bundled/jinja2/docs/_static/.ignore bundled/jinja2/docs/_static/darkmetal.png bundled/jinja2/docs/_static/headerbg.png bundled/jinja2/docs/_static/implementation.png bundled/jinja2/docs/_static/jinja.js bundled/jinja2/docs/_static/jinjabanner.png bundled/jinja2/docs/_static/metal.png bundled/jinja2/docs/_static/navigation.png bundled/jinja2/docs/_static/note.png bundled/jinja2/docs/_static/print.css bundled/jinja2/docs/_static/style.css bundled/jinja2/docs/_static/watermark.png bundled/jinja2/docs/_static/watermark_blur.png bundled/jinja2/docs/_templates/.ignore bundled/jinja2/docs/_templates/genindex.html bundled/jinja2/docs/_templates/layout.html bundled/jinja2/docs/_templates/opensearch.xml bundled/jinja2/docs/_templates/page.html bundled/jinja2/docs/_templates/search.html bundled/jinja2/docs/api.rst bundled/jinja2/docs/cache_extension.py bundled/jinja2/docs/changelog.rst bundled/jinja2/docs/conf.py bundled/jinja2/docs/extensions.rst bundled/jinja2/docs/faq.rst bundled/jinja2/docs/index.rst bundled/jinja2/docs/integration.rst bundled/jinja2/docs/intro.rst bundled/jinja2/docs/jinjaext.py bundled/jinja2/docs/sandbox.rst bundled/jinja2/docs/switching.rst bundled/jinja2/docs/templates.rst bundled/jinja2/docs/tricks.rst bundled/jinja2/examples/basic/cycle.py bundled/jinja2/examples/basic/debugger.py bundled/jinja2/examples/basic/inheritance.py bundled/jinja2/examples/basic/templates/broken.html bundled/jinja2/examples/basic/templates/subbroken.html bundled/jinja2/examples/basic/test.py bundled/jinja2/examples/basic/test_filter_and_linestatements.py bundled/jinja2/examples/basic/test_loop_filter.py bundled/jinja2/examples/basic/translate.py bundled/jinja2/examples/bench.py bundled/jinja2/examples/profile.py bundled/jinja2/examples/rwbench/django/_form.html bundled/jinja2/examples/rwbench/django/_input_field.html bundled/jinja2/examples/rwbench/django/_textarea.html bundled/jinja2/examples/rwbench/django/index.html bundled/jinja2/examples/rwbench/django/layout.html bundled/jinja2/examples/rwbench/djangoext.py bundled/jinja2/examples/rwbench/genshi/helpers.html bundled/jinja2/examples/rwbench/genshi/index.html bundled/jinja2/examples/rwbench/genshi/layout.html bundled/jinja2/examples/rwbench/jinja/helpers.html bundled/jinja2/examples/rwbench/jinja/index.html bundled/jinja2/examples/rwbench/jinja/layout.html bundled/jinja2/examples/rwbench/mako/helpers.html bundled/jinja2/examples/rwbench/mako/index.html bundled/jinja2/examples/rwbench/mako/layout.html bundled/jinja2/examples/rwbench/rwbench.py bundled/webpy/.gitignore bundled/webpy/ChangeLog.txt bundled/webpy/LICENSE.txt bundled/webpy/experimental/background.py bundled/webpy/experimental/migration.py bundled/webpy/experimental/pwt.py bundled/webpy/experimental/untwisted.py bundled/webpy/setup.py bundled/webpy/test/README bundled/webpy/test/__init__.py bundled/webpy/test/alltests.py bundled/webpy/test/application.py bundled/webpy/test/browser.py bundled/webpy/test/db.py bundled/webpy/test/doctests.py bundled/webpy/test/session.py bundled/webpy/test/webtest.py bundled/webpy/tools/_makedoc.py bundled/webpy/tools/makedoc.py bundled/webpy/tools/markdown.py bundled/webpy/web/__init__.py bundled/webpy/web/application.py bundled/webpy/web/browser.py bundled/webpy/web/contrib/__init__.py bundled/webpy/web/contrib/template.py bundled/webpy/web/db.py bundled/webpy/web/debugerror.py bundled/webpy/web/form.py bundled/webpy/web/http.py bundled/webpy/web/httpserver.py bundled/webpy/web/net.py bundled/webpy/web/session.py bundled/webpy/web/template.py bundled/webpy/web/test.py bundled/webpy/web/utils.py bundled/webpy/web/webapi.py bundled/webpy/web/webopenid.py bundled/webpy/web/wsgi.py bundled/webpy/web/wsgiserver/LICENSE.txt bundled/webpy/web/wsgiserver/__init__.py review/web_media/aal.css review/web_media/comments.js review/web_media/jquery-1.4.2.min.js review/web_media/style.css review/web_media/ui.js review/web_templates/base.html review/web_templates/changeset.html review/web_templates/index.html review/web_templates/macros.html

Changes

--- a/README.markdown	Fri Jun 11 20:07:20 2010 -0400
+++ b/README.markdown	Fri Jun 11 21:25:17 2010 -0400
@@ -16,17 +16,21 @@
 Installing
 ==========
 
-`hg-review` requires Mercurial (probably 1.3.1+).
+`hg-review` requires Mercurial (probably 1.3.1+) and Python 2.5+. It requires
+a few other things too, but they're bundled with the extension so you don't
+need to worry about them.
 
-Get it:
+First, get hg-review:
 
     hg clone http://bitbucket.org/sjl/hg-review/
 
-Add it to `~/.hgrc`:
+Then add it to your `~/.hgrc`:
 
     [extensions]
     review = [path to]/hg-review/review/
 
+You're all set.
+
 Try It
 ======
 
--- a/bundled/cherrypy/MANIFEST.in	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-include cherrypy/cherryd
-include cherrypy/favicon.ico
-include cherrypy/LICENSE.txt
-include cherrypy/scaffold/*.conf
-include cherrypy/scaffold/static/*.png
-include cherrypy/test/style.css
-include cherrypy/test/test.pem
-include cherrypy/test/static/*.html
-include cherrypy/test/static/*.jpg
-include cherrypy/tutorial/*.conf
-include cherrypy/tutorial/*.pdf
-include cherrypy/tutorial/*.html
-include cherrypy/tutorial/README.txt
--- a/bundled/cherrypy/README.txt	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-* To install, change to the directory where setup.py is located and type (python-2.3 or later needed):
-
-    python setup.py install
-
-* To learn how to use it, look at the examples under cherrypy/tutorial/ or go to http://www.cherrypy.org for more info.
-
-* To run the regression tests, just go to the cherrypy/test/ directory and type:
-
-    python test.py
-
-  Or to run individual tests type:
-
-    python test.py --test_foo --test_bar
--- a/bundled/cherrypy/cherrypy/LICENSE.txt	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-Copyright (c) 2004-2009, CherryPy Team (team@cherrypy.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, 
-are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice, 
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice, 
-      this list of conditions and the following disclaimer in the documentation 
-      and/or other materials provided with the distribution.
-    * Neither the name of the CherryPy Team nor the names of its contributors 
-      may be used to endorse or promote products derived from this software 
-      without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- a/bundled/cherrypy/cherrypy/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,573 +0,0 @@
-"""CherryPy is a pythonic, object-oriented HTTP framework.
-
-
-CherryPy consists of not one, but four separate API layers.
-
-The APPLICATION LAYER is the simplest. CherryPy applications are written as
-a tree of classes and methods, where each branch in the tree corresponds to
-a branch in the URL path. Each method is a 'page handler', which receives
-GET and POST params as keyword arguments, and returns or yields the (HTML)
-body of the response. The special method name 'index' is used for paths
-that end in a slash, and the special method name 'default' is used to
-handle multiple paths via a single handler. This layer also includes:
-
- * the 'exposed' attribute (and cherrypy.expose)
- * cherrypy.quickstart()
- * _cp_config attributes
- * cherrypy.tools (including cherrypy.session)
- * cherrypy.url()
-
-The ENVIRONMENT LAYER is used by developers at all levels. It provides
-information about the current request and response, plus the application
-and server environment, via a (default) set of top-level objects:
-
- * cherrypy.request
- * cherrypy.response
- * cherrypy.engine
- * cherrypy.server
- * cherrypy.tree
- * cherrypy.config
- * cherrypy.thread_data
- * cherrypy.log
- * cherrypy.HTTPError, NotFound, and HTTPRedirect
- * cherrypy.lib
-
-The EXTENSION LAYER allows advanced users to construct and share their own
-plugins. It consists of:
-
- * Hook API
- * Tool API
- * Toolbox API
- * Dispatch API
- * Config Namespace API
-
-Finally, there is the CORE LAYER, which uses the core API's to construct
-the default components which are available at higher layers. You can think
-of the default components as the 'reference implementation' for CherryPy.
-Megaframeworks (and advanced users) may replace the default components
-with customized or extended components. The core API's are:
-
- * Application API
- * Engine API
- * Request API
- * Server API
- * WSGI API
-
-These API's are described in the CherryPy specification:
-http://www.cherrypy.org/wiki/CherryPySpec
-"""
-
-__version__ = "3.2.0rc1"
-
-from urlparse import urljoin as _urljoin
-from urllib import urlencode as _urlencode
-
-
-class _AttributeDocstrings(type):
-    """Metaclass for declaring docstrings for class attributes."""
-    # The full docstring for this type is down in the __init__ method so
-    # that it doesn't show up in help() for every consumer class.
-    
-    def __init__(cls, name, bases, dct):
-        '''Metaclass for declaring docstrings for class attributes.
-        
-        Base Python doesn't provide any syntax for setting docstrings on
-        'data attributes' (non-callables). This metaclass allows class
-        definitions to follow the declaration of a data attribute with
-        a docstring for that attribute; the attribute docstring will be
-        popped from the class dict and folded into the class docstring.
-        
-        The naming convention for attribute docstrings is:
-            <attrname> + "__doc".
-        For example:
-        
-            class Thing(object):
-                """A thing and its properties."""
-                
-                __metaclass__ = cherrypy._AttributeDocstrings
-                
-                height = 50
-                height__doc = """The height of the Thing in inches."""
-        
-        In which case, help(Thing) starts like this:
-        
-            >>> help(mod.Thing)
-            Help on class Thing in module pkg.mod:
-            
-            class Thing(__builtin__.object)
-             |  A thing and its properties.
-             |  
-             |  height [= 50]:
-             |      The height of the Thing in inches.
-             | 
-        
-        The benefits of this approach over hand-edited class docstrings:
-            1. Places the docstring nearer to the attribute declaration.
-            2. Makes attribute docs more uniform ("name (default): doc").
-            3. Reduces mismatches of attribute _names_ between
-               the declaration and the documentation.
-            4. Reduces mismatches of attribute default _values_ between
-               the declaration and the documentation.
-        
-        The benefits of a metaclass approach over other approaches:
-            1. Simpler ("less magic") than interface-based solutions.
-            2. __metaclass__ can be specified at the module global level
-               for classic classes.
-        
-        For various formatting reasons, you should write multiline docs
-        with a leading newline and not a trailing one:
-            
-            response__doc = """
-            The response object for the current thread. In the main thread,
-            and any threads which are not HTTP requests, this is None."""
-        
-        The type of the attribute is intentionally not included, because
-        that's not How Python Works. Quack.
-        '''
-        
-        newdoc = [cls.__doc__ or ""]
-        
-        dctkeys = dct.keys()
-        dctkeys.sort()
-        for name in dctkeys:
-            if name.endswith("__doc"):
-                # Remove the magic doc attribute.
-                if hasattr(cls, name):
-                    delattr(cls, name)
-                
-                # Make a uniformly-indented docstring from it.
-                val = '\n'.join(['    ' + line.strip()
-                                 for line in dct[name].split('\n')])
-                
-                # Get the default value.
-                attrname = name[:-5]
-                try:
-                    attrval = getattr(cls, attrname)
-                except AttributeError:
-                    attrval = "missing"
-                
-                # Add the complete attribute docstring to our list.
-                newdoc.append("%s [= %r]:\n%s" % (attrname, attrval, val))
-        
-        # Add our list of new docstrings to the class docstring.
-        cls.__doc__ = "\n\n".join(newdoc)
-
-
-from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect
-from cherrypy._cperror import NotFound, CherryPyException, TimeoutError
-
-from cherrypy import _cpdispatch as dispatch
-
-from cherrypy import _cptools
-tools = _cptools.default_toolbox
-Tool = _cptools.Tool
-
-from cherrypy import _cprequest
-from cherrypy.lib import httputil as _httputil
-
-from cherrypy import _cptree
-tree = _cptree.Tree()
-from cherrypy._cptree import Application
-from cherrypy import _cpwsgi as wsgi
-
-from cherrypy import process
-try:
-    from cherrypy.process import win32
-    engine = win32.Win32Bus()
-    engine.console_control_handler = win32.ConsoleCtrlHandler(engine)
-    del win32
-except ImportError:
-    engine = process.bus
-
-
-# Timeout monitor
-class _TimeoutMonitor(process.plugins.Monitor):
-    
-    def __init__(self, bus):
-        self.servings = []
-        process.plugins.Monitor.__init__(self, bus, self.run)
-    
-    def acquire(self):
-        self.servings.append((serving.request, serving.response))
-    
-    def release(self):
-        try:
-            self.servings.remove((serving.request, serving.response))
-        except ValueError:
-            pass
-    
-    def run(self):
-        """Check timeout on all responses. (Internal)"""
-        for req, resp in self.servings:
-            resp.check_timeout()
-engine.timeout_monitor = _TimeoutMonitor(engine)
-engine.timeout_monitor.subscribe()
-
-engine.autoreload = process.plugins.Autoreloader(engine)
-engine.autoreload.subscribe()
-
-engine.thread_manager = process.plugins.ThreadManager(engine)
-engine.thread_manager.subscribe()
-
-engine.signal_handler = process.plugins.SignalHandler(engine)
-
-
-from cherrypy import _cpserver
-server = _cpserver.Server()
-server.subscribe()
-
-
-def quickstart(root=None, script_name="", config=None):
-    """Mount the given root, start the builtin server (and engine), then block.
-    
-    root: an instance of a "controller class" (a collection of page handler
-        methods) which represents the root of the application.
-    script_name: a string containing the "mount point" of the application.
-        This should start with a slash, and be the path portion of the URL
-        at which to mount the given root. For example, if root.index() will
-        handle requests to "http://www.example.com:8080/dept/app1/", then
-        the script_name argument would be "/dept/app1".
-        
-        It MUST NOT end in a slash. If the script_name refers to the root
-        of the URI, it MUST be an empty string (not "/").
-    config: a file or dict containing application config. If this contains
-        a [global] section, those entries will be used in the global
-        (site-wide) config.
-    """
-    if config:
-        _global_conf_alias.update(config)
-    
-    tree.mount(root, script_name, config)
-    
-    if hasattr(engine, "signal_handler"):
-        engine.signal_handler.subscribe()
-    if hasattr(engine, "console_control_handler"):
-        engine.console_control_handler.subscribe()
-    
-    engine.start()
-    engine.block()
-
-
-try:
-    from threading import local as _local
-except ImportError:
-    from cherrypy._cpthreadinglocal import local as _local
-
-class _Serving(_local):
-    """An interface for registering request and response objects.
-    
-    Rather than have a separate "thread local" object for the request and
-    the response, this class works as a single threadlocal container for
-    both objects (and any others which developers wish to define). In this
-    way, we can easily dump those objects when we stop/start a new HTTP
-    conversation, yet still refer to them as module-level globals in a
-    thread-safe way.
-    """
-    
-    __metaclass__ = _AttributeDocstrings
-    
-    request = _cprequest.Request(_httputil.Host("127.0.0.1", 80),
-                                 _httputil.Host("127.0.0.1", 1111))
-    request__doc = """
-    The request object for the current thread. In the main thread,
-    and any threads which are not receiving HTTP requests, this is None."""
-    
-    response = _cprequest.Response()
-    response__doc = """
-    The response object for the current thread. In the main thread,
-    and any threads which are not receiving HTTP requests, this is None."""
-    
-    def load(self, request, response):
-        self.request = request
-        self.response = response
-    
-    def clear(self):
-        """Remove all attributes of self."""
-        self.__dict__.clear()
-
-serving = _Serving()
-
-
-class _ThreadLocalProxy(object):
-    
-    __slots__ = ['__attrname__', '__dict__']
-    
-    def __init__(self, attrname):
-        self.__attrname__ = attrname
-    
-    def __getattr__(self, name):
-        child = getattr(serving, self.__attrname__)
-        return getattr(child, name)
-    
-    def __setattr__(self, name, value):
-        if name in ("__attrname__", ):
-            object.__setattr__(self, name, value)
-        else:
-            child = getattr(serving, self.__attrname__)
-            setattr(child, name, value)
-    
-    def __delattr__(self, name):
-        child = getattr(serving, self.__attrname__)
-        delattr(child, name)
-    
-    def _get_dict(self):
-        child = getattr(serving, self.__attrname__)
-        d = child.__class__.__dict__.copy()
-        d.update(child.__dict__)
-        return d
-    __dict__ = property(_get_dict)
-    
-    def __getitem__(self, key):
-        child = getattr(serving, self.__attrname__)
-        return child[key]
-    
-    def __setitem__(self, key, value):
-        child = getattr(serving, self.__attrname__)
-        child[key] = value
-    
-    def __delitem__(self, key):
-        child = getattr(serving, self.__attrname__)
-        del child[key]
-    
-    def __contains__(self, key):
-        child = getattr(serving, self.__attrname__)
-        return key in child
-    
-    def __len__(self):
-        child = getattr(serving, self.__attrname__)
-        return len(child)
-    
-    def __nonzero__(self):
-        child = getattr(serving, self.__attrname__)
-        return bool(child)
-
-
-# Create request and response object (the same objects will be used
-#   throughout the entire life of the webserver, but will redirect
-#   to the "serving" object)
-request = _ThreadLocalProxy('request')
-response = _ThreadLocalProxy('response')
-
-# Create thread_data object as a thread-specific all-purpose storage
-class _ThreadData(_local):
-    """A container for thread-specific data."""
-thread_data = _ThreadData()
-
-
-# Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
-# Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
-# The only other way would be to change what is returned from type(request)
-# and that's not possible in pure Python (you'd have to fake ob_type).
-def _cherrypy_pydoc_resolve(thing, forceload=0):
-    """Given an object or a path to an object, get the object and its name."""
-    if isinstance(thing, _ThreadLocalProxy):
-        thing = getattr(serving, thing.__attrname__)
-    return _pydoc._builtin_resolve(thing, forceload)
-
-try:
-    import pydoc as _pydoc
-    _pydoc._builtin_resolve = _pydoc.resolve
-    _pydoc.resolve = _cherrypy_pydoc_resolve
-except ImportError:
-    pass
-
-
-from cherrypy import _cplogging
-
-class _GlobalLogManager(_cplogging.LogManager):
-    
-    def __call__(self, *args, **kwargs):
-        # Do NOT use try/except here. See http://www.cherrypy.org/ticket/945
-        if hasattr(request, 'app') and hasattr(request.app, 'log'):
-            log = request.app.log
-        else:
-            log = self
-        return log.error(*args, **kwargs)
-    
-    def access(self):
-        try:
-            return request.app.log.access()
-        except AttributeError:
-            return _cplogging.LogManager.access(self)
-
-
-log = _GlobalLogManager()
-# Set a default screen handler on the global log.
-log.screen = True
-log.error_file = ''
-# Using an access file makes CP about 10% slower. Leave off by default.
-log.access_file = ''
-
-def _buslog(msg, level):
-    log.error(msg, 'ENGINE', severity=level)
-engine.subscribe('log', _buslog)
-
-#                       Helper functions for CP apps                       #
-
-
-def expose(func=None, alias=None):
-    """Expose the function, optionally providing an alias or set of aliases."""
-    def expose_(func):
-        func.exposed = True
-        if alias is not None:
-            if isinstance(alias, basestring):
-                parents[alias.replace(".", "_")] = func
-            else:
-                for a in alias:
-                    parents[a.replace(".", "_")] = func
-        return func
-    
-    import sys, types
-    if isinstance(func, (types.FunctionType, types.MethodType)):
-        if alias is None:
-            # @expose
-            func.exposed = True
-            return func
-        else:
-            # func = expose(func, alias)
-            parents = sys._getframe(1).f_locals
-            return expose_(func)
-    elif func is None:
-        if alias is None:
-            # @expose()
-            parents = sys._getframe(1).f_locals
-            return expose_
-        else:
-            # @expose(alias="alias") or
-            # @expose(alias=["alias1", "alias2"])
-            parents = sys._getframe(1).f_locals
-            return expose_
-    else:
-        # @expose("alias") or
-        # @expose(["alias1", "alias2"])
-        parents = sys._getframe(1).f_locals
-        alias = func
-        return expose_
-
-
-def url(path="", qs="", script_name=None, base=None, relative=None):
-    """Create an absolute URL for the given path.
-    
-    If 'path' starts with a slash ('/'), this will return
-        (base + script_name + path + qs).
-    If it does not start with a slash, this returns
-        (base + script_name [+ request.path_info] + path + qs).
-    
-    If script_name is None, cherrypy.request will be used
-    to find a script_name, if available.
-    
-    If base is None, cherrypy.request.base will be used (if available).
-    Note that you can use cherrypy.tools.proxy to change this.
-    
-    Finally, note that this function can be used to obtain an absolute URL
-    for the current request path (minus the querystring) by passing no args.
-    If you call url(qs=cherrypy.request.query_string), you should get the
-    original browser URL (assuming no internal redirections).
-    
-    If relative is None or not provided, request.app.relative_urls will
-    be used (if available, else False). If False, the output will be an
-    absolute URL (including the scheme, host, vhost, and script_name).
-    If True, the output will instead be a URL that is relative to the
-    current request path, perhaps including '..' atoms. If relative is
-    the string 'server', the output will instead be a URL that is
-    relative to the server root; i.e., it will start with a slash.
-    """
-    if isinstance(qs, (tuple, list, dict)):
-        qs = _urlencode(qs)
-    if qs:
-        qs = '?' + qs
-    
-    if request.app:
-        if not path.startswith("/"):
-            # Append/remove trailing slash from path_info as needed
-            # (this is to support mistyped URL's without redirecting;
-            # if you want to redirect, use tools.trailing_slash).
-            pi = request.path_info
-            if request.is_index is True:
-                if not pi.endswith('/'):
-                    pi = pi + '/'
-            elif request.is_index is False:
-                if pi.endswith('/') and pi != '/':
-                    pi = pi[:-1]
-            
-            if path == "":
-                path = pi
-            else:
-                path = _urljoin(pi, path)
-        
-        if script_name is None:
-            script_name = request.script_name
-        if base is None:
-            base = request.base
-        
-        newurl = base + script_name + path + qs
-    else:
-        # No request.app (we're being called outside a request).
-        # We'll have to guess the base from server.* attributes.
-        # This will produce very different results from the above
-        # if you're using vhosts or tools.proxy.
-        if base is None:
-            base = server.base()
-        
-        path = (script_name or "") + path
-        newurl = base + path + qs
-    
-    if './' in newurl:
-        # Normalize the URL by removing ./ and ../
-        atoms = []
-        for atom in newurl.split('/'):
-            if atom == '.':
-                pass
-            elif atom == '..':
-                atoms.pop()
-            else:
-                atoms.append(atom)
-        newurl = '/'.join(atoms)
-    
-    # At this point, we should have a fully-qualified absolute URL.
-    
-    if relative is None:
-        relative = getattr(request.app, "relative_urls", False)
-    
-    # See http://www.ietf.org/rfc/rfc2396.txt
-    if relative == 'server':
-        # "A relative reference beginning with a single slash character is
-        # termed an absolute-path reference, as defined by <abs_path>..."
-        # This is also sometimes called "server-relative".
-        newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
-    elif relative:
-        # "A relative reference that does not begin with a scheme name
-        # or a slash character is termed a relative-path reference."
-        old = url().split('/')[:-1]
-        new = newurl.split('/')
-        while old and new:
-            a, b = old[0], new[0]
-            if a != b:
-                break
-            old.pop(0)
-            new.pop(0)
-        new = (['..'] * len(old)) + new
-        newurl = '/'.join(new)
-    
-    return newurl
-
-
-# import _cpconfig last so it can reference other top-level objects
-from cherrypy import _cpconfig
-# Use _global_conf_alias so quickstart can use 'config' as an arg
-# without shadowing cherrypy.config.
-config = _global_conf_alias = _cpconfig.Config()
-config.defaults = {
-    'tools.log_tracebacks.on': True,
-    'tools.log_headers.on': True,
-    'tools.trailing_slash.on': True,
-    'tools.encode.on': True
-    }
-config.namespaces["log"] = lambda k, v: setattr(log, k, v)
-config.namespaces["checker"] = lambda k, v: setattr(checker, k, v)
-# Must reset to get our defaults applied.
-config.reset()
-
-from cherrypy import _cpchecker
-checker = _cpchecker.Checker()
-engine.subscribe('start', checker)
--- a/bundled/cherrypy/cherrypy/_cpchecker.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,322 +0,0 @@
-import os
-import warnings
-
-import cherrypy
-
-
-class Checker(object):
-    """A checker for CherryPy sites and their mounted applications.
-    
-    on: set this to False to turn off the checker completely.
-    
-    When this object is called at engine startup, it executes each
-    of its own methods whose names start with "check_". If you wish
-    to disable selected checks, simply add a line in your global
-    config which sets the appropriate method to False:
-    
-    [global]
-    checker.check_skipped_app_config = False
-    
-    You may also dynamically add or replace check_* methods in this way.
-    """
-    
-    on = True
-    
-    def __init__(self):
-        self._populate_known_types()
-    
-    def __call__(self):
-        """Run all check_* methods."""
-        if self.on:
-            oldformatwarning = warnings.formatwarning
-            warnings.formatwarning = self.formatwarning
-            try:
-                for name in dir(self):
-                    if name.startswith("check_"):
-                        method = getattr(self, name)
-                        if method and callable(method):
-                            method()
-            finally:
-                warnings.formatwarning = oldformatwarning
-    
-    def formatwarning(self, message, category, filename, lineno, line=None):
-        """Function to format a warning."""
-        return "CherryPy Checker:\n%s\n\n" % message
-    
-    # This value should be set inside _cpconfig.
-    global_config_contained_paths = False
-    
-    def check_app_config_entries_dont_start_with_script_name(self):
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            if not app.config:
-                continue
-            if sn == '':
-                continue
-            sn_atoms = sn.strip("/").split("/")
-            for key in app.config.keys():
-                key_atoms = key.strip("/").split("/")
-                if key_atoms[:len(sn_atoms)] == sn_atoms:
-                    warnings.warn(
-                        "The application mounted at %r has config " \
-                        "entries that start with its script name: %r" % (sn, key))
-    
-    def check_site_config_entries_in_app_config(self):
-        for sn, app in cherrypy.tree.apps.iteritems():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            
-            msg = []
-            for section, entries in app.config.iteritems():
-                if section.startswith('/'):
-                    for key, value in entries.iteritems():
-                        for n in ("engine.", "server.", "tree.", "checker."):
-                            if key.startswith(n):
-                                msg.append("[%s] %s = %s" % (section, key, value))
-            if msg:
-                msg.insert(0,
-                    "The application mounted at %r contains the following "
-                    "config entries, which are only allowed in site-wide "
-                    "config. Move them to a [global] section and pass them "
-                    "to cherrypy.config.update() instead of tree.mount()." % sn)
-                warnings.warn(os.linesep.join(msg))
-    
-    def check_skipped_app_config(self):
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            if not app.config:
-                msg = "The Application mounted at %r has an empty config." % sn
-                if self.global_config_contained_paths:
-                    msg += (" It looks like the config you passed to "
-                            "cherrypy.config.update() contains application-"
-                            "specific sections. You must explicitly pass "
-                            "application config via "
-                            "cherrypy.tree.mount(..., config=app_config)")
-                warnings.warn(msg)
-                return
-    
-    def check_app_config_brackets(self):
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            if not app.config:
-                continue
-            for key in app.config.keys():
-                if key.startswith("[") or key.endswith("]"):
-                    warnings.warn(
-                        "The application mounted at %r has config " \
-                        "section names with extraneous brackets: %r. "
-                        "Config *files* need brackets; config *dicts* "
-                        "(e.g. passed to tree.mount) do not." % (sn, key))
-    
-    def check_static_paths(self):
-        # Use the dummy Request object in the main thread.
-        request = cherrypy.request
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            request.app = app
-            for section in app.config:
-                # get_resource will populate request.config
-                request.get_resource(section + "/dummy.html")
-                conf = request.config.get
-                
-                if conf("tools.staticdir.on", False):
-                    msg = ""
-                    root = conf("tools.staticdir.root")
-                    dir = conf("tools.staticdir.dir")
-                    if dir is None:
-                        msg = "tools.staticdir.dir is not set."
-                    else:
-                        fulldir = ""
-                        if os.path.isabs(dir):
-                            fulldir = dir
-                            if root:
-                                msg = ("dir is an absolute path, even "
-                                       "though a root is provided.")
-                                testdir = os.path.join(root, dir[1:])
-                                if os.path.exists(testdir):
-                                    msg += ("\nIf you meant to serve the "
-                                            "filesystem folder at %r, remove "
-                                            "the leading slash from dir." % testdir)
-                        else:
-                            if not root:
-                                msg = "dir is a relative path and no root provided."
-                            else:
-                                fulldir = os.path.join(root, dir)
-                                if not os.path.isabs(fulldir):
-                                    msg = "%r is not an absolute path." % fulldir
-                        
-                        if fulldir and not os.path.exists(fulldir):
-                            if msg:
-                                msg += "\n"
-                            msg += ("%r (root + dir) is not an existing "
-                                    "filesystem path." % fulldir)
-                    
-                    if msg:
-                        warnings.warn("%s\nsection: [%s]\nroot: %r\ndir: %r"
-                                      % (msg, section, root, dir))
-    
-    
-    # -------------------------- Compatibility -------------------------- #
-    
-    obsolete = {
-        'server.default_content_type': 'tools.response_headers.headers',
-        'log_access_file': 'log.access_file',
-        'log_config_options': None,
-        'log_file': 'log.error_file',
-        'log_file_not_found': None,
-        'log_request_headers': 'tools.log_headers.on',
-        'log_to_screen': 'log.screen',
-        'show_tracebacks': 'request.show_tracebacks',
-        'throw_errors': 'request.throw_errors',
-        'profiler.on': ('cherrypy.tree.mount(profiler.make_app('
-                        'cherrypy.Application(Root())))'),
-        }
-    
-    deprecated = {}
-    
-    def _compat(self, config):
-        """Process config and warn on each obsolete or deprecated entry."""
-        for section, conf in config.items():
-            if isinstance(conf, dict):
-                for k, v in conf.items():
-                    if k in self.obsolete:
-                        warnings.warn("%r is obsolete. Use %r instead.\n"
-                                      "section: [%s]" %
-                                      (k, self.obsolete[k], section))
-                    elif k in self.deprecated:
-                        warnings.warn("%r is deprecated. Use %r instead.\n"
-                                      "section: [%s]" %
-                                      (k, self.deprecated[k], section))
-            else:
-                if section in self.obsolete:
-                    warnings.warn("%r is obsolete. Use %r instead."
-                                  % (section, self.obsolete[section]))
-                elif section in self.deprecated:
-                    warnings.warn("%r is deprecated. Use %r instead."
-                                  % (section, self.deprecated[section]))
-    
-    def check_compatibility(self):
-        """Process config and warn on each obsolete or deprecated entry."""
-        self._compat(cherrypy.config)
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            self._compat(app.config)
-    
-    
-    # ------------------------ Known Namespaces ------------------------ #
-    
-    extra_config_namespaces = []
-    
-    def _known_ns(self, app):
-        ns = ["wsgi"]
-        ns.extend(app.toolboxes.keys())
-        ns.extend(app.namespaces.keys())
-        ns.extend(app.request_class.namespaces.keys())
-        ns.extend(cherrypy.config.namespaces.keys())
-        ns += self.extra_config_namespaces
-        
-        for section, conf in app.config.items():
-            is_path_section = section.startswith("/")
-            if is_path_section and isinstance(conf, dict):
-                for k, v in conf.items():
-                    atoms = k.split(".")
-                    if len(atoms) > 1:
-                        if atoms[0] not in ns:
-                            # Spit out a special warning if a known
-                            # namespace is preceded by "cherrypy."
-                            if (atoms[0] == "cherrypy" and atoms[1] in ns):
-                                msg = ("The config entry %r is invalid; "
-                                       "try %r instead.\nsection: [%s]"
-                                       % (k, ".".join(atoms[1:]), section))
-                            else:
-                                msg = ("The config entry %r is invalid, because "
-                                       "the %r config namespace is unknown.\n"
-                                       "section: [%s]" % (k, atoms[0], section))
-                            warnings.warn(msg)
-                        elif atoms[0] == "tools":
-                            if atoms[1] not in dir(cherrypy.tools):
-                                msg = ("The config entry %r may be invalid, "
-                                       "because the %r tool was not found.\n"
-                                       "section: [%s]" % (k, atoms[1], section))
-                                warnings.warn(msg)
-    
-    def check_config_namespaces(self):
-        """Process config and warn on each unknown config namespace."""
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            self._known_ns(app)
-
-
-    
-    
-    # -------------------------- Config Types -------------------------- #
-    
-    known_config_types = {}
-    
-    def _populate_known_types(self):
-        import __builtin__ as builtins
-        b = [x for x in vars(builtins).values()
-             if type(x) is type(str)]
-        
-        def traverse(obj, namespace):
-            for name in dir(obj):
-                # Hack for 3.2's warning about body_params
-                if name == 'body_params':
-                    continue
-                vtype = type(getattr(obj, name, None))
-                if vtype in b:
-                    self.known_config_types[namespace + "." + name] = vtype
-        
-        traverse(cherrypy.request, "request")
-        traverse(cherrypy.response, "response")
-        traverse(cherrypy.server, "server")
-        traverse(cherrypy.engine, "engine")
-        traverse(cherrypy.log, "log")
-    
-    def _known_types(self, config):
-        msg = ("The config entry %r in section %r is of type %r, "
-               "which does not match the expected type %r.")
-        
-        for section, conf in config.items():
-            if isinstance(conf, dict):
-                for k, v in conf.items():
-                    if v is not None:
-                        expected_type = self.known_config_types.get(k, None)
-                        vtype = type(v)
-                        if expected_type and vtype != expected_type:
-                            warnings.warn(msg % (k, section, vtype.__name__,
-                                                 expected_type.__name__))
-            else:
-                k, v = section, conf
-                if v is not None:
-                    expected_type = self.known_config_types.get(k, None)
-                    vtype = type(v)
-                    if expected_type and vtype != expected_type:
-                        warnings.warn(msg % (k, section, vtype.__name__,
-                                             expected_type.__name__))
-    
-    def check_config_types(self):
-        """Assert that config values are of the same type as default values."""
-        self._known_types(cherrypy.config)
-        for sn, app in cherrypy.tree.apps.items():
-            if not isinstance(app, cherrypy.Application):
-                continue
-            self._known_types(app.config)
-    
-    
-    # -------------------- Specific config warnings -------------------- #
-    
-    def check_localhost(self):
-        """Warn if any socket_host is 'localhost'. See #711."""
-        for k, v in cherrypy.config.items():
-            if k == 'server.socket_host' and v == 'localhost':
-                warnings.warn("The use of 'localhost' as a socket host can "
-                    "cause problems on newer systems, since 'localhost' can "
-                    "map to either an IPv4 or an IPv6 address. You should "
-                    "use '127.0.0.1' or '[::1]' instead.")
--- a/bundled/cherrypy/cherrypy/_cpconfig.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-"""Configuration system for CherryPy.
-
-Configuration in CherryPy is implemented via dictionaries. Keys are strings
-which name the mapped value, which may be of any type.
-
-
-Architecture
-------------
-
-CherryPy Requests are part of an Application, which runs in a global context,
-and configuration data may apply to any of those three scopes:
-
-    Global: configuration entries which apply everywhere are stored in
-    cherrypy.config.
-    
-    Application: entries which apply to each mounted application are stored
-    on the Application object itself, as 'app.config'. This is a two-level
-    dict where each key is a path, or "relative URL" (for example, "/" or
-    "/path/to/my/page"), and each value is a config dict. Usually, this
-    data is provided in the call to tree.mount(root(), config=conf),
-    although you may also use app.merge(conf).
-    
-    Request: each Request object possesses a single 'Request.config' dict.
-    Early in the request process, this dict is populated by merging global
-    config entries, Application entries (whose path equals or is a parent
-    of Request.path_info), and any config acquired while looking up the
-    page handler (see next).
-
-
-Declaration
------------
-
-Configuration data may be supplied as a Python dictionary, as a filename,
-or as an open file object. When you supply a filename or file, CherryPy
-uses Python's builtin ConfigParser; you declare Application config by
-writing each path as a section header:
-
-    [/path/to/my/page]
-    request.stream = True
-
-To declare global configuration entries, place them in a [global] section.
-
-You may also declare config entries directly on the classes and methods
-(page handlers) that make up your CherryPy application via the '_cp_config'
-attribute. For example:
-
-    class Demo:
-        _cp_config = {'tools.gzip.on': True}
-        
-        def index(self):
-            return "Hello world"
-        index.exposed = True
-        index._cp_config = {'request.show_tracebacks': False}
-
-Note, however, that this behavior is only guaranteed for the default
-dispatcher. Other dispatchers may have different restrictions on where
-you can attach _cp_config attributes.
-
-
-Namespaces
-----------
-
-Configuration keys are separated into namespaces by the first "." in the key.
-Current namespaces:
-
-    engine:     Controls the 'application engine', including autoreload.
-                These can only be declared in the global config.
-    tree:       Grafts cherrypy.Application objects onto cherrypy.tree.
-                These can only be declared in the global config.
-    hooks:      Declares additional request-processing functions.
-    log:        Configures the logging for each application.
-                These can only be declared in the global or / config.
-    request:    Adds attributes to each Request.
-    response:   Adds attributes to each Response.
-    server:     Controls the default HTTP server via cherrypy.server.
-                These can only be declared in the global config.
-    tools:      Runs and configures additional request-processing packages.
-    wsgi:       Adds WSGI middleware to an Application's "pipeline".
-                These can only be declared in the app's root config ("/").
-    checker:    Controls the 'checker', which looks for common errors in
-                app state (including config) when the engine starts.
-                Global config only.
-
-The only key that does not exist in a namespace is the "environment" entry.
-This special entry 'imports' other config entries from a template stored in
-cherrypy._cpconfig.environments[environment]. It only applies to the global
-config, and only when you use cherrypy.config.update.
-
-You can define your own namespaces to be called at the Global, Application,
-or Request level, by adding a named handler to cherrypy.config.namespaces,
-app.namespaces, or app.request_class.namespaces. The name can
-be any string, and the handler must be either a callable or a (Python 2.5
-style) context manager.
-"""
-
-try:
-    set
-except NameError:
-    from sets import Set as set
-
-import cherrypy
-from cherrypy.lib import reprconf
-
-# Deprecated in  CherryPy 3.2--remove in 3.3
-NamespaceSet = reprconf.NamespaceSet
-
-def merge(base, other):
-    """Merge one app config (from a dict, file, or filename) into another.
-    
-    If the given config is a filename, it will be appended to
-    the list of files to monitor for "autoreload" changes.
-    """
-    if isinstance(other, basestring):
-        cherrypy.engine.autoreload.files.add(other)
-    
-    # Load other into base
-    for section, value_map in reprconf.as_dict(other).items():
-        if not isinstance(value_map, dict):
-            raise ValueError(
-                "Application config must include section headers, but the "
-                "config you tried to merge doesn't have any sections. "
-                "Wrap your config in another dict with paths as section "
-                "headers, for example: {'/': config}.")
-        base.setdefault(section, {}).update(value_map)
-
-
-class Config(reprconf.Config):
-    """The 'global' configuration data for the entire CherryPy process."""
-
-    def update(self, config):
-        """Update self from a dict, file or filename."""
-        if isinstance(config, basestring):
-            # Filename
-            cherrypy.engine.autoreload.files.add(config)
-        reprconf.Config.update(self, config)
-
-    def _apply(self, config):
-        """Update self from a dict."""
-        if isinstance(config.get("global", None), dict):
-            if len(config) > 1:
-                cherrypy.checker.global_config_contained_paths = True
-            config = config["global"]
-        if 'tools.staticdir.dir' in config:
-            config['tools.staticdir.section'] = "global"
-        reprconf.Config._apply(self, config)
-    
-    def __call__(self, *args, **kwargs):
-        """Decorator for page handlers to set _cp_config."""
-        if args:
-            raise TypeError(
-                "The cherrypy.config decorator does not accept positional "
-                "arguments; you must use keyword arguments.")
-        def tool_decorator(f):
-            if not hasattr(f, "_cp_config"):
-                f._cp_config = {}
-            for k, v in kwargs.items():
-                f._cp_config[k] = v
-            return f
-        return tool_decorator
-
-
-Config.environments = environments = {
-    "staging": {
-        'engine.autoreload_on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': False,
-        'request.show_mismatched_params': False,
-        },
-    "production": {
-        'engine.autoreload_on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': False,
-        'request.show_mismatched_params': False,
-        'log.screen': False,
-        },
-    "embedded": {
-        # For use with CherryPy embedded in another deployment stack.
-        'engine.autoreload_on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': False,
-        'request.show_mismatched_params': False,
-        'log.screen': False,
-        'engine.SIGHUP': None,
-        'engine.SIGTERM': None,
-        },
-    "test_suite": {
-        'engine.autoreload_on': False,
-        'checker.on': False,
-        'tools.log_headers.on': False,
-        'request.show_tracebacks': True,
-        'request.show_mismatched_params': True,
-        'log.screen': False,
-        },
-    }
-
-
-def _server_namespace_handler(k, v):
-    """Config handler for the "server" namespace."""
-    atoms = k.split(".", 1)
-    if len(atoms) > 1:
-        # Special-case config keys of the form 'server.servername.socket_port'
-        # to configure additional HTTP servers.
-        if not hasattr(cherrypy, "servers"):
-            cherrypy.servers = {}
-        
-        servername, k = atoms
-        if servername not in cherrypy.servers:
-            from cherrypy import _cpserver
-            cherrypy.servers[servername] = _cpserver.Server()
-            # On by default, but 'on = False' can unsubscribe it (see below).
-            cherrypy.servers[servername].subscribe()
-        
-        if k == 'on':
-            if v:
-                cherrypy.servers[servername].subscribe()
-            else:
-                cherrypy.servers[servername].unsubscribe()
-        else:
-            setattr(cherrypy.servers[servername], k, v)
-    else:
-        setattr(cherrypy.server, k, v)
-Config.namespaces["server"] = _server_namespace_handler
-
-def _engine_namespace_handler(k, v):
-    """Backward compatibility handler for the "engine" namespace."""
-    engine = cherrypy.engine
-    if k == 'autoreload_on':
-        if v:
-            engine.autoreload.subscribe()
-        else:
-            engine.autoreload.unsubscribe()
-    elif k == 'autoreload_frequency':
-        engine.autoreload.frequency = v
-    elif k == 'autoreload_match':
-        engine.autoreload.match = v
-    elif k == 'reload_files':
-        engine.autoreload.files = set(v)
-    elif k == 'deadlock_poll_freq':
-        engine.timeout_monitor.frequency = v
-    elif k == 'SIGHUP':
-        engine.listeners['SIGHUP'] = set([v])
-    elif k == 'SIGTERM':
-        engine.listeners['SIGTERM'] = set([v])
-    elif "." in k:
-        plugin, attrname = k.split(".", 1)
-        plugin = getattr(engine, plugin)
-        if attrname == 'on':
-            if v and hasattr(getattr(plugin, 'subscribe', None), '__call__'):
-                plugin.subscribe()
-                return
-            elif (not v) and hasattr(getattr(plugin, 'unsubscribe', None), '__call__'):
-                plugin.unsubscribe()
-                return
-        setattr(plugin, attrname, v)
-    else:
-        setattr(engine, k, v)
-Config.namespaces["engine"] = _engine_namespace_handler
-
-
-def _tree_namespace_handler(k, v):
-    """Namespace handler for the 'tree' config namespace."""
-    cherrypy.tree.graft(v, v.script_name)
-    cherrypy.engine.log("Mounted: %s on %s" % (v, v.script_name or "/"))
-Config.namespaces["tree"] = _tree_namespace_handler
-
-
--- a/bundled/cherrypy/cherrypy/_cpdispatch.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,568 +0,0 @@
-"""CherryPy dispatchers.
-
-A 'dispatcher' is the object which looks up the 'page handler' callable
-and collects config for the current request based on the path_info, other
-request attributes, and the application architecture. The core calls the
-dispatcher as early as possible, passing it a 'path_info' argument.
-
-The default dispatcher discovers the page handler by matching path_info
-to a hierarchical arrangement of objects, starting at request.app.root.
-"""
-
-import cherrypy
-
-
-class PageHandler(object):
-    """Callable which sets response.body."""
-    
-    def __init__(self, callable, *args, **kwargs):
-        self.callable = callable
-        self.args = args
-        self.kwargs = kwargs
-    
-    def __call__(self):
-        try:
-            return self.callable(*self.args, **self.kwargs)
-        except TypeError, x:
-            try:
-                test_callable_spec(self.callable, self.args, self.kwargs)
-            except cherrypy.HTTPError, error:
-                raise error
-            except:
-                raise x
-            raise
-
-
-def test_callable_spec(callable, callable_args, callable_kwargs):
-    """
-    Inspect callable and test to see if the given args are suitable for it.
-
-    When an error occurs during the handler's invoking stage there are 2
-    erroneous cases:
-    1.  Too many parameters passed to a function which doesn't define
-        one of *args or **kwargs.
-    2.  Too little parameters are passed to the function.
-
-    There are 3 sources of parameters to a cherrypy handler.
-    1.  query string parameters are passed as keyword parameters to the handler.
-    2.  body parameters are also passed as keyword parameters.
-    3.  when partial matching occurs, the final path atoms are passed as
-        positional args.
-    Both the query string and path atoms are part of the URI.  If they are
-    incorrect, then a 404 Not Found should be raised. Conversely the body
-    parameters are part of the request; if they are invalid a 400 Bad Request.
-    """
-    show_mismatched_params = getattr(
-        cherrypy.serving.request, 'show_mismatched_params', False)
-    try:
-        (args, varargs, varkw, defaults) = inspect.getargspec(callable)
-    except TypeError:
-        if isinstance(callable, object) and hasattr(callable, '__call__'):
-            (args, varargs, varkw, defaults) = inspect.getargspec(callable.__call__)
-        else:
-            # If it wasn't one of our own types, re-raise 
-            # the original error
-            raise
-
-    if args and args[0] == 'self':
-        args = args[1:]
-
-    arg_usage = dict([(arg, 0,) for arg in args])
-    vararg_usage = 0
-    varkw_usage = 0
-    extra_kwargs = set()
-
-    for i, value in enumerate(callable_args):
-        try:
-            arg_usage[args[i]] += 1
-        except IndexError:
-            vararg_usage += 1
-
-    for key in callable_kwargs.keys():
-        try:
-            arg_usage[key] += 1
-        except KeyError:
-            varkw_usage += 1
-            extra_kwargs.add(key)
-
-    # figure out which args have defaults.
-    args_with_defaults = args[-len(defaults or []):]
-    for i, val in enumerate(defaults or []):
-        # Defaults take effect only when the arg hasn't been used yet.
-        if arg_usage[args_with_defaults[i]] == 0:
-            arg_usage[args_with_defaults[i]] += 1
-
-    missing_args = []
-    multiple_args = []
-    for key, usage in arg_usage.items():
-        if usage == 0:
-            missing_args.append(key)
-        elif usage > 1:
-            multiple_args.append(key)
-
-    if missing_args:
-        # In the case where the method allows body arguments
-        # there are 3 potential errors:
-        # 1. not enough query string parameters -> 404
-        # 2. not enough body parameters -> 400
-        # 3. not enough path parts (partial matches) -> 404
-        #
-        # We can't actually tell which case it is, 
-        # so I'm raising a 404 because that covers 2/3 of the
-        # possibilities
-        # 
-        # In the case where the method does not allow body
-        # arguments it's definitely a 404.
-        message = None
-        if show_mismatched_params:
-            message="Missing parameters: %s" % ",".join(missing_args)
-        raise cherrypy.HTTPError(404, message=message)
-
-    # the extra positional arguments come from the path - 404 Not Found
-    if not varargs and vararg_usage > 0:
-        raise cherrypy.HTTPError(404)
-
-    body_params = cherrypy.serving.request.body.params or {}
-    body_params = set(body_params.keys())
-    qs_params = set(callable_kwargs.keys()) - body_params
-
-    if multiple_args:
-        if qs_params.intersection(set(multiple_args)):
-            # If any of the multiple parameters came from the query string then
-            # it's a 404 Not Found
-            error = 404
-        else:
-            # Otherwise it's a 400 Bad Request
-            error = 400
-
-        message = None
-        if show_mismatched_params:
-            message="Multiple values for parameters: "\
-                    "%s" % ",".join(multiple_args)
-        raise cherrypy.HTTPError(error, message=message)
-
-    if not varkw and varkw_usage > 0:
-
-        # If there were extra query string parameters, it's a 404 Not Found
-        extra_qs_params = set(qs_params).intersection(extra_kwargs)
-        if extra_qs_params:
-            message = None
-            if show_mismatched_params:
-                message="Unexpected query string "\
-                        "parameters: %s" % ", ".join(extra_qs_params)
-            raise cherrypy.HTTPError(404, message=message)
-
-        # If there were any extra body parameters, it's a 400 Not Found
-        extra_body_params = set(body_params).intersection(extra_kwargs)
-        if extra_body_params:
-            message = None
-            if show_mismatched_params:
-                message="Unexpected body parameters: "\
-                        "%s" % ", ".join(extra_body_params)
-            raise cherrypy.HTTPError(400, message=message)
-
-
-try:
-    import inspect
-except ImportError:
-    test_callable_spec = lambda callable, args, kwargs: None
-
-
-
-class LateParamPageHandler(PageHandler):
-    """When passing cherrypy.request.params to the page handler, we do not
-    want to capture that dict too early; we want to give tools like the
-    decoding tool a chance to modify the params dict in-between the lookup
-    of the handler and the actual calling of the handler. This subclass
-    takes that into account, and allows request.params to be 'bound late'
-    (it's more complicated than that, but that's the effect).
-    """
-    
-    def _get_kwargs(self):
-        kwargs = cherrypy.serving.request.params.copy()
-        if self._kwargs:
-            kwargs.update(self._kwargs)
-        return kwargs
-    
-    def _set_kwargs(self, kwargs):
-        self._kwargs = kwargs
-    
-    kwargs = property(_get_kwargs, _set_kwargs,
-                      doc='page handler kwargs (with '
-                      'cherrypy.request.params copied in)')
-
-
-class Dispatcher(object):
-    """CherryPy Dispatcher which walks a tree of objects to find a handler.
-    
-    The tree is rooted at cherrypy.request.app.root, and each hierarchical
-    component in the path_info argument is matched to a corresponding nested
-    attribute of the root object. Matching handlers must have an 'exposed'
-    attribute which evaluates to True. The special method name "index"
-    matches a URI which ends in a slash ("/"). The special method name
-    "default" may match a portion of the path_info (but only when no longer
-    substring of the path_info matches some other object).
-    
-    This is the default, built-in dispatcher for CherryPy.
-    """
-    __metaclass__ = cherrypy._AttributeDocstrings
-
-    dispatch_method_name = '_cp_dispatch'
-    dispatch_method_name__doc = """
-    The name of the dispatch method that nodes may optionally implement
-    to provide their own dynamic dispatch algorithm.
-    """
-    
-    def __init__(self, dispatch_method_name = None):
-        if dispatch_method_name:
-            self.dispatch_method_name = dispatch_method_name
-
-    def __call__(self, path_info):
-        """Set handler and config for the current request."""
-        request = cherrypy.serving.request
-        func, vpath = self.find_handler(path_info)
-        
-        if func:
-            # Decode any leftover %2F in the virtual_path atoms.
-            vpath = [x.replace("%2F", "/") for x in vpath]
-            request.handler = LateParamPageHandler(func, *vpath)
-        else:
-            request.handler = cherrypy.NotFound()
-    
-    def find_handler(self, path):
-        """Return the appropriate page handler, plus any virtual path.
-        
-        This will return two objects. The first will be a callable,
-        which can be used to generate page output. Any parameters from
-        the query string or request body will be sent to that callable
-        as keyword arguments.
-        
-        The callable is found by traversing the application's tree,
-        starting from cherrypy.request.app.root, and matching path
-        components to successive objects in the tree. For example, the
-        URL "/path/to/handler" might return root.path.to.handler.
-        
-        The second object returned will be a list of names which are
-        'virtual path' components: parts of the URL which are dynamic,
-        and were not used when looking up the handler.
-        These virtual path components are passed to the handler as
-        positional arguments.
-        """
-        request = cherrypy.serving.request
-        app = request.app
-        root = app.root
-        dispatch_name = self.dispatch_method_name
-        
-        # Get config for the root object/path.
-        curpath = ""
-        nodeconf = {}
-        if hasattr(root, "_cp_config"):
-            nodeconf.update(root._cp_config)
-        if "/" in app.config:
-            nodeconf.update(app.config["/"])
-        object_trail = [['root', root, nodeconf, curpath]]
-        
-        node = root
-        names = [x for x in path.strip('/').split('/') if x] + ['index']
-        iternames = names[:]
-        while iternames:
-            name = iternames[0]
-            # map to legal Python identifiers (replace '.' with '_')
-            objname = name.replace('.', '_')
-            
-            nodeconf = {}
-            subnode = getattr(node, objname, None)
-            if subnode is None:
-                dispatch = getattr(node, dispatch_name, None)
-                if dispatch and callable(dispatch) and not \
-                        getattr(dispatch, 'exposed', False):
-                    subnode = dispatch(vpath=iternames)
-            name = iternames.pop(0)
-            node = subnode
-
-            if node is not None:
-                # Get _cp_config attached to this node.
-                if hasattr(node, "_cp_config"):
-                    nodeconf.update(node._cp_config)
-            
-            # Mix in values from app.config for this path.
-            curpath = "/".join((curpath, name))
-            if curpath in app.config:
-                nodeconf.update(app.config[curpath])
-            
-            object_trail.append([name, node, nodeconf, curpath])
-        
-        def set_conf():
-            """Collapse all object_trail config into cherrypy.request.config."""
-            base = cherrypy.config.copy()
-            # Note that we merge the config from each node
-            # even if that node was None.
-            for name, obj, conf, curpath in object_trail:
-                base.update(conf)
-                if 'tools.staticdir.dir' in conf:
-                    base['tools.staticdir.section'] = curpath
-            return base
-        
-        # Try successive objects (reverse order)
-        num_candidates = len(object_trail) - 1
-        for i in range(num_candidates, -1, -1):
-            
-            name, candidate, nodeconf, curpath = object_trail[i]
-            if candidate is None:
-                continue
-            
-            # Try a "default" method on the current leaf.
-            if hasattr(candidate, "default"):
-                defhandler = candidate.default
-                if getattr(defhandler, 'exposed', False):
-                    # Insert any extra _cp_config from the default handler.
-                    conf = getattr(defhandler, "_cp_config", {})
-                    object_trail.insert(i+1, ["default", defhandler, conf, curpath])
-                    request.config = set_conf()
-                    # See http://www.cherrypy.org/ticket/613
-                    request.is_index = path.endswith("/")
-                    return defhandler, names[i:-1]
-            
-            # Uncomment the next line to restrict positional params to "default".
-            # if i < num_candidates - 2: continue
-            
-            # Try the current leaf.
-            if getattr(candidate, 'exposed', False):
-                request.config = set_conf()
-                if i == num_candidates:
-                    # We found the extra ".index". Mark request so tools
-                    # can redirect if path_info has no trailing slash.
-                    request.is_index = True
-                else:
-                    # We're not at an 'index' handler. Mark request so tools
-                    # can redirect if path_info has NO trailing slash.
-                    # Note that this also includes handlers which take
-                    # positional parameters (virtual paths).
-                    request.is_index = False
-                return candidate, names[i:-1]
-        
-        # We didn't find anything
-        request.config = set_conf()
-        return None, []
-
-
-class MethodDispatcher(Dispatcher):
-    """Additional dispatch based on cherrypy.request.method.upper().
-    
-    Methods named GET, POST, etc will be called on an exposed class.
-    The method names must be all caps; the appropriate Allow header
-    will be output showing all capitalized method names as allowable
-    HTTP verbs.
-    
-    Note that the containing class must be exposed, not the methods.
-    """
-    
-    def __call__(self, path_info):
-        """Set handler and config for the current request."""
-        request = cherrypy.serving.request
-        resource, vpath = self.find_handler(path_info)
-        
-        if resource:
-            # Set Allow header
-            avail = [m for m in dir(resource) if m.isupper()]
-            if "GET" in avail and "HEAD" not in avail:
-                avail.append("HEAD")
-            avail.sort()
-            cherrypy.serving.response.headers['Allow'] = ", ".join(avail)
-            
-            # Find the subhandler
-            meth = request.method.upper()
-            func = getattr(resource, meth, None)
-            if func is None and meth == "HEAD":
-                func = getattr(resource, "GET", None)
-            if func:
-                # Grab any _cp_config on the subhandler.
-                if hasattr(func, "_cp_config"):
-                    request.config.update(func._cp_config)
-                
-                # Decode any leftover %2F in the virtual_path atoms.
-                vpath = [x.replace("%2F", "/") for x in vpath]
-                request.handler = LateParamPageHandler(func, *vpath)
-            else:
-                request.handler = cherrypy.HTTPError(405)
-        else:
-            request.handler = cherrypy.NotFound()
-
-
-class RoutesDispatcher(object):
-    """A Routes based dispatcher for CherryPy."""
-    
-    def __init__(self, full_result=False):
-        """
-        Routes dispatcher
-
-        Set full_result to True if you wish the controller
-        and the action to be passed on to the page handler
-        parameters. By default they won't be.
-        """
-        import routes
-        self.full_result = full_result
-        self.controllers = {}
-        self.mapper = routes.Mapper()
-        self.mapper.controller_scan = self.controllers.keys
-        
-    def connect(self, name, route, controller, **kwargs):
-        self.controllers[name] = controller
-        self.mapper.connect(name, route, controller=name, **kwargs)
-    
-    def redirect(self, url):
-        raise cherrypy.HTTPRedirect(url)
-    
-    def __call__(self, path_info):
-        """Set handler and config for the current request."""
-        func = self.find_handler(path_info)
-        if func:
-            cherrypy.serving.request.handler = LateParamPageHandler(func)
-        else:
-            cherrypy.serving.request.handler = cherrypy.NotFound()
-    
-    def find_handler(self, path_info):
-        """Find the right page handler, and set request.config."""
-        import routes
-        
-        request = cherrypy.serving.request
-        
-        config = routes.request_config()
-        config.mapper = self.mapper
-        if hasattr(request, 'wsgi_environ'):
-            config.environ = request.wsgi_environ
-        config.host = request.headers.get('Host', None)
-        config.protocol = request.scheme
-        config.redirect = self.redirect
-        
-        result = self.mapper.match(path_info)
-        
-        config.mapper_dict = result
-        params = {}
-        if result:
-            params = result.copy()
-        if not self.full_result:
-            params.pop('controller', None)
-            params.pop('action', None)
-        request.params.update(params)
-        
-        # Get config for the root object/path.
-        request.config = base = cherrypy.config.copy()
-        curpath = ""
-        
-        def merge(nodeconf):
-            if 'tools.staticdir.dir' in nodeconf:
-                nodeconf['tools.staticdir.section'] = curpath or "/"
-            base.update(nodeconf)
-        
-        app = request.app
-        root = app.root
-        if hasattr(root, "_cp_config"):
-            merge(root._cp_config)
-        if "/" in app.config:
-            merge(app.config["/"])
-        
-        # Mix in values from app.config.
-        atoms = [x for x in path_info.split("/") if x]
-        if atoms:
-            last = atoms.pop()
-        else:
-            last = None
-        for atom in atoms:
-            curpath = "/".join((curpath, atom))
-            if curpath in app.config:
-                merge(app.config[curpath])
-        
-        handler = None
-        if result:
-            controller = result.get('controller', None)
-            controller = self.controllers.get(controller)
-            if controller:
-                # Get config from the controller.
-                if hasattr(controller, "_cp_config"):
-                    merge(controller._cp_config)
-            
-            action = result.get('action', None)
-            if action is not None:
-                handler = getattr(controller, action, None)
-                # Get config from the handler 
-                if hasattr(handler, "_cp_config"): 
-                    merge(handler._cp_config)
-                    
-        # Do the last path atom here so it can
-        # override the controller's _cp_config.
-        if last:
-            curpath = "/".join((curpath, last))
-            if curpath in app.config:
-                merge(app.config[curpath])
-        
-        return handler
-
-
-def XMLRPCDispatcher(next_dispatcher=Dispatcher()):
-    from cherrypy.lib import xmlrpc
-    def xmlrpc_dispatch(path_info):
-        path_info = xmlrpc.patched_path(path_info)
-        return next_dispatcher(path_info)
-    return xmlrpc_dispatch
-
-
-def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True, **domains):
-    """Select a different handler based on the Host header.
-    
-    This can be useful when running multiple sites within one CP server.
-    It allows several domains to point to different parts of a single
-    website structure. For example:
-    
-        http://www.domain.example  ->  root
-        http://www.domain2.example  ->  root/domain2/
-        http://www.domain2.example:443  ->  root/secure
-    
-    can be accomplished via the following config:
-    
-        [/]
-        request.dispatch = cherrypy.dispatch.VirtualHost(
-            **{'www.domain2.example': '/domain2',
-               'www.domain2.example:443': '/secure',
-              })
-    
-    next_dispatcher: the next dispatcher object in the dispatch chain.
-        The VirtualHost dispatcher adds a prefix to the URL and calls
-        another dispatcher. Defaults to cherrypy.dispatch.Dispatcher().
-    
-    use_x_forwarded_host: if True (the default), any "X-Forwarded-Host"
-        request header will be used instead of the "Host" header. This
-        is commonly added by HTTP servers (such as Apache) when proxying.
-    
-    **domains: a dict of {host header value: virtual prefix} pairs.
-        The incoming "Host" request header is looked up in this dict,
-        and, if a match is found, the corresponding "virtual prefix"
-        value will be prepended to the URL path before calling the
-        next dispatcher. Note that you often need separate entries
-        for "example.com" and "www.example.com". In addition, "Host"
-        headers may contain the port number.
-    """
-    from cherrypy.lib import httputil
-    def vhost_dispatch(path_info):
-        request = cherrypy.serving.request
-        header = request.headers.get
-        
-        domain = header('Host', '')
-        if use_x_forwarded_host:
-            domain = header("X-Forwarded-Host", domain)
-        
-        prefix = domains.get(domain, "")
-        if prefix:
-            path_info = httputil.urljoin(prefix, path_info)
-        
-        result = next_dispatcher(path_info)
-        
-        # Touch up staticdir config. See http://www.cherrypy.org/ticket/614.
-        section = request.config.get('tools.staticdir.section')
-        if section:
-            section = section[len(prefix):]
-            request.config['tools.staticdir.section'] = section
-        
-        return result
-    return vhost_dispatch
-
--- a/bundled/cherrypy/cherrypy/_cperror.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,396 +0,0 @@
-"""Error classes for CherryPy."""
-
-from cgi import escape as _escape
-from sys import exc_info as _exc_info
-from traceback import format_exception as _format_exception
-from urlparse import urljoin as _urljoin
-from cherrypy.lib import httputil as _httputil
-
-
-class CherryPyException(Exception):
-    pass
-
-
-class TimeoutError(CherryPyException):
-    """Exception raised when Response.timed_out is detected."""
-    pass
-
-
-class InternalRedirect(CherryPyException):
-    """Exception raised to switch to the handler for a different URL.
-    
-    Any request.params must be supplied in a query string.
-    """
-    
-    def __init__(self, path, query_string=""):
-        import cherrypy
-        self.request = cherrypy.serving.request
-        
-        self.query_string = query_string
-        if "?" in path:
-            # Separate any params included in the path
-            path, self.query_string = path.split("?", 1)
-        
-        # Note that urljoin will "do the right thing" whether url is:
-        #  1. a URL relative to root (e.g. "/dummy")
-        #  2. a URL relative to the current path
-        # Note that any query string will be discarded.
-        path = _urljoin(self.request.path_info, path)
-        
-        # Set a 'path' member attribute so that code which traps this
-        # error can have access to it.
-        self.path = path
-        
-        CherryPyException.__init__(self, path, self.query_string)
-
-
-class HTTPRedirect(CherryPyException):
-    """Exception raised when the request should be redirected.
-    
-    The new URL must be passed as the first argument to the Exception,
-    e.g., HTTPRedirect(newUrl). Multiple URLs are allowed. If a URL is
-    absolute, it will be used as-is. If it is relative, it is assumed
-    to be relative to the current cherrypy.request.path_info.
-    """
-    
-    def __init__(self, urls, status=None):
-        import cherrypy
-        request = cherrypy.serving.request
-        
-        if isinstance(urls, basestring):
-            urls = [urls]
-        
-        abs_urls = []
-        for url in urls:
-            # Note that urljoin will "do the right thing" whether url is:
-            #  1. a complete URL with host (e.g. "http://www.example.com/test")
-            #  2. a URL relative to root (e.g. "/dummy")
-            #  3. a URL relative to the current path
-            # Note that any query string in cherrypy.request is discarded.
-            url = _urljoin(cherrypy.url(), url)
-            abs_urls.append(url)
-        self.urls = abs_urls
-        
-        # RFC 2616 indicates a 301 response code fits our goal; however,
-        # browser support for 301 is quite messy. Do 302/303 instead. See
-        # http://www.alanflavell.org.uk/www/post-redirect.html
-        if status is None:
-            if request.protocol >= (1, 1):
-                status = 303
-            else:
-                status = 302
-        else:
-            status = int(status)
-            if status < 300 or status > 399:
-                raise ValueError("status must be between 300 and 399.")
-        
-        self.status = status
-        CherryPyException.__init__(self, abs_urls, status)
-    
-    def set_response(self):
-        """Modify cherrypy.response status, headers, and body to represent self.
-        
-        CherryPy uses this internally, but you can also use it to create an
-        HTTPRedirect object and set its output without *raising* the exception.
-        """
-        import cherrypy
-        response = cherrypy.serving.response
-        response.status = status = self.status
-        
-        if status in (300, 301, 302, 303, 307):
-            response.headers['Content-Type'] = "text/html;charset=utf-8"
-            # "The ... URI SHOULD be given by the Location field
-            # in the response."
-            response.headers['Location'] = self.urls[0]
-            
-            # "Unless the request method was HEAD, the entity of the response
-            # SHOULD contain a short hypertext note with a hyperlink to the
-            # new URI(s)."
-            msg = {300: "This resource can be found at <a href='%s'>%s</a>.",
-                   301: "This resource has permanently moved to <a href='%s'>%s</a>.",
-                   302: "This resource resides temporarily at <a href='%s'>%s</a>.",
-                   303: "This resource can be found at <a href='%s'>%s</a>.",
-                   307: "This resource has moved temporarily to <a href='%s'>%s</a>.",
-                   }[status]
-            msgs = [msg % (u, u) for u in self.urls]
-            response.body = "<br />\n".join(msgs)
-            # Previous code may have set C-L, so we have to reset it
-            # (allow finalize to set it).
-            response.headers.pop('Content-Length', None)
-        elif status == 304:
-            # Not Modified.
-            # "The response MUST include the following header fields:
-            # Date, unless its omission is required by section 14.18.1"
-            # The "Date" header should have been set in Response.__init__
-            
-            # "...the response SHOULD NOT include other entity-headers."
-            for key in ('Allow', 'Content-Encoding', 'Content-Language',
-                        'Content-Length', 'Content-Location', 'Content-MD5',
-                        'Content-Range', 'Content-Type', 'Expires',
-                        'Last-Modified'):
-                if key in response.headers:
-                    del response.headers[key]
-            
-            # "The 304 response MUST NOT contain a message-body."
-            response.body = None
-            # Previous code may have set C-L, so we have to reset it.
-            response.headers.pop('Content-Length', None)
-        elif status == 305:
-            # Use Proxy.
-            # self.urls[0] should be the URI of the proxy.
-            response.headers['Location'] = self.urls[0]
-            response.body = None
-            # Previous code may have set C-L, so we have to reset it.
-            response.headers.pop('Content-Length', None)
-        else:
-            raise ValueError("The %s status code is unknown." % status)
-    
-    def __call__(self):
-        """Use this exception as a request.handler (raise self)."""
-        raise self
-
-
-def clean_headers(status):
-    """Remove any headers which should not apply to an error response."""
-    import cherrypy
-    
-    response = cherrypy.serving.response
-    
-    # Remove headers which applied to the original content,
-    # but do not apply to the error page.
-    respheaders = response.headers
-    for key in ["Accept-Ranges", "Age", "ETag", "Location", "Retry-After",
-                "Vary", "Content-Encoding", "Content-Length", "Expires",
-                "Content-Location", "Content-MD5", "Last-Modified"]:
-        if key in respheaders:
-            del respheaders[key]
-    
-    if status != 416:
-        # A server sending a response with status code 416 (Requested
-        # range not satisfiable) SHOULD include a Content-Range field
-        # with a byte-range-resp-spec of "*". The instance-length
-        # specifies the current length of the selected resource.
-        # A response with status code 206 (Partial Content) MUST NOT
-        # include a Content-Range field with a byte-range- resp-spec of "*".
-        if "Content-Range" in respheaders:
-            del respheaders["Content-Range"]
-
-
-class HTTPError(CherryPyException):
-    """ Exception used to return an HTTP error code (4xx-5xx) to the client.
-        This exception will automatically set the response status and body.
-        
-        A custom message (a long description to display in the browser)
-        can be provided in place of the default.
-    """
-    
-    def __init__(self, status=500, message=None):
-        self.status = status
-        try:
-            self.code, self.reason, defaultmsg = _httputil.valid_status(status)
-        except ValueError, x:
-            raise self.__class__(500, x.args[0])
-        
-        if self.code < 400 or self.code > 599:
-            raise ValueError("status must be between 400 and 599.")
-        
-        # See http://www.python.org/dev/peps/pep-0352/
-        # self.message = message
-        self._message = message or defaultmsg
-        CherryPyException.__init__(self, status, message)
-    
-    def set_response(self):
-        """Modify cherrypy.response status, headers, and body to represent self.
-        
-        CherryPy uses this internally, but you can also use it to create an
-        HTTPError object and set its output without *raising* the exception.
-        """
-        import cherrypy
-        
-        response = cherrypy.serving.response
-        
-        clean_headers(self.code)
-        
-        # In all cases, finalize will be called after this method,
-        # so don't bother cleaning up response values here.
-        response.status = self.status
-        tb = None
-        if cherrypy.serving.request.show_tracebacks:
-            tb = format_exc()
-        response.headers['Content-Type'] = "text/html;charset=utf-8"
-        response.headers.pop('Content-Length', None)
-        
-        content = self.get_error_page(self.status, traceback=tb,
-                                      message=self._message)
-        response.body = content
-        
-        _be_ie_unfriendly(self.code)
-    
-    def get_error_page(self, *args, **kwargs):
-        return get_error_page(*args, **kwargs)
-    
-    def __call__(self):
-        """Use this exception as a request.handler (raise self)."""
-        raise self
-
-
-class NotFound(HTTPError):
-    """Exception raised when a URL could not be mapped to any handler (404)."""
-    
-    def __init__(self, path=None):
-        if path is None:
-            import cherrypy
-            request = cherrypy.serving.request
-            path = request.script_name + request.path_info
-        self.args = (path,)
-        HTTPError.__init__(self, 404, "The path '%s' was not found." % path)
-
-
-_HTTPErrorTemplate = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
-    <title>%(status)s</title>
-    <style type="text/css">
-    #powered_by {
-        margin-top: 20px;
-        border-top: 2px solid black;
-        font-style: italic;
-    }
-
-    #traceback {
-        color: red;
-    }
-    </style>
-</head>
-    <body>
-        <h2>%(status)s</h2>
-        <p>%(message)s</p>
-        <pre id="traceback">%(traceback)s</pre>
-    <div id="powered_by">
-    <span>Powered by <a href="http://www.cherrypy.org">CherryPy %(version)s</a></span>
-    </div>
-    </body>
-</html>
-'''
-
-def get_error_page(status, **kwargs):
-    """Return an HTML page, containing a pretty error response.
-    
-    status should be an int or a str.
-    kwargs will be interpolated into the page template.
-    """
-    import cherrypy
-    
-    try:
-        code, reason, message = _httputil.valid_status(status)
-    except ValueError, x:
-        raise cherrypy.HTTPError(500, x.args[0])
-    
-    # We can't use setdefault here, because some
-    # callers send None for kwarg values.
-    if kwargs.get('status') is None:
-        kwargs['status'] = "%s %s" % (code, reason)
-    if kwargs.get('message') is None:
-        kwargs['message'] = message
-    if kwargs.get('traceback') is None:
-        kwargs['traceback'] = ''
-    if kwargs.get('version') is None:
-        kwargs['version'] = cherrypy.__version__
-    
-    for k, v in kwargs.iteritems():
-        if v is None:
-            kwargs[k] = ""
-        else:
-            kwargs[k] = _escape(kwargs[k])
-    
-    # Use a custom template or callable for the error page?
-    pages = cherrypy.serving.request.error_page
-    error_page = pages.get(code) or pages.get('default')
-    if error_page:
-        try:
-            if callable(error_page):
-                return error_page(**kwargs)
-            else:
-                return open(error_page, 'rb').read() % kwargs
-        except:
-            e = _format_exception(*_exc_info())[-1]
-            m = kwargs['message']
-            if m:
-                m += "<br />"
-            m += "In addition, the custom error page failed:\n<br />%s" % e
-            kwargs['message'] = m
-    
-    return _HTTPErrorTemplate % kwargs
-
-
-_ie_friendly_error_sizes = {
-    400: 512, 403: 256, 404: 512, 405: 256,
-    406: 512, 408: 512, 409: 512, 410: 256,
-    500: 512, 501: 512, 505: 512,
-    }
-
-
-def _be_ie_unfriendly(status):
-    import cherrypy
-    response = cherrypy.serving.response
-    
-    # For some statuses, Internet Explorer 5+ shows "friendly error
-    # messages" instead of our response.body if the body is smaller
-    # than a given size. Fix this by returning a body over that size
-    # (by adding whitespace).
-    # See http://support.microsoft.com/kb/q218155/
-    s = _ie_friendly_error_sizes.get(status, 0)
-    if s:
-        s += 1
-        # Since we are issuing an HTTP error status, we assume that
-        # the entity is short, and we should just collapse it.
-        content = response.collapse_body()
-        l = len(content)
-        if l and l < s:
-            # IN ADDITION: the response must be written to IE
-            # in one chunk or it will still get replaced! Bah.
-            content = content + (" " * (s - l))
-        response.body = content
-        response.headers[u'Content-Length'] = str(len(content))
-
-
-def format_exc(exc=None):
-    """Return exc (or sys.exc_info if None), formatted."""
-    if exc is None:
-        exc = _exc_info()
-    if exc == (None, None, None):
-        return ""
-    import traceback
-    return "".join(traceback.format_exception(*exc))
-
-def bare_error(extrabody=None):
-    """Produce status, headers, body for a critical error.
-    
-    Returns a triple without calling any other questionable functions,
-    so it should be as error-free as possible. Call it from an HTTP server
-    if you get errors outside of the request.
-    
-    If extrabody is None, a friendly but rather unhelpful error message
-    is set in the body. If extrabody is a string, it will be appended
-    as-is to the body.
-    """
-    
-    # The whole point of this function is to be a last line-of-defense
-    # in handling errors. That is, it must not raise any errors itself;
-    # it cannot be allowed to fail. Therefore, don't add to it!
-    # In particular, don't call any other CP functions.
-    
-    body = "Unrecoverable error in the server."
-    if extrabody is not None:
-        if not isinstance(extrabody, str): 
-            extrabody = extrabody.encode('utf-8')
-        body += "\n" + extrabody
-    
-    return ("500 Internal Server Error",
-            [('Content-Type', 'text/plain'),
-             ('Content-Length', str(len(body)))],
-            [body])
-
-
--- a/bundled/cherrypy/cherrypy/_cplogging.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-"""CherryPy logging."""
-
-import datetime
-import logging
-# Silence the no-handlers "warning" (stderr write!) in stdlib logging
-logging.Logger.manager.emittedNoHandlerWarning = 1
-logfmt = logging.Formatter("%(message)s")
-import os
-import sys
-
-import cherrypy
-from cherrypy import _cperror
-
-
-class LogManager(object):
-    
-    appid = None
-    error_log = None
-    access_log = None
-    access_log_format = \
-        '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
-    
-    def __init__(self, appid=None, logger_root="cherrypy"):
-        self.logger_root = logger_root
-        self.appid = appid
-        if appid is None:
-            self.error_log = logging.getLogger("%s.error" % logger_root)
-            self.access_log = logging.getLogger("%s.access" % logger_root)
-        else:
-            self.error_log = logging.getLogger("%s.error.%s" % (logger_root, appid))
-            self.access_log = logging.getLogger("%s.access.%s" % (logger_root, appid))
-        self.error_log.setLevel(logging.INFO)
-        self.access_log.setLevel(logging.INFO)
-        cherrypy.engine.subscribe('graceful', self.reopen_files)
-    
-    def reopen_files(self):
-        """Close and reopen all file handlers."""
-        for log in (self.error_log, self.access_log):
-            for h in log.handlers:
-                if isinstance(h, logging.FileHandler):
-                    h.acquire()
-                    h.stream.close()
-                    h.stream = open(h.baseFilename, h.mode)
-                    h.release()
-    
-    def error(self, msg='', context='', severity=logging.INFO, traceback=False):
-        """Write to the error log.
-        
-        This is not just for errors! Applications may call this at any time
-        to log application-specific information.
-        """
-        if traceback:
-            msg += _cperror.format_exc()
-        self.error_log.log(severity, ' '.join((self.time(), context, msg)))
-    
-    def __call__(self, *args, **kwargs):
-        """Write to the error log.
-        
-        This is not just for errors! Applications may call this at any time
-        to log application-specific information.
-        """
-        return self.error(*args, **kwargs)
-    
-    def access(self):
-        """Write to the access log (in Apache/NCSA Combined Log format).
-        
-        Like Apache started doing in 2.0.46, non-printable and other special
-        characters in %r (and we expand that to all parts) are escaped using
-        \\xhh sequences, where hh stands for the hexadecimal representation
-        of the raw byte. Exceptions from this rule are " and \\, which are
-        escaped by prepending a backslash, and all whitespace characters,
-        which are written in their C-style notation (\\n, \\t, etc).
-        """
-        request = cherrypy.serving.request
-        remote = request.remote
-        response = cherrypy.serving.response
-        outheaders = response.headers
-        inheaders = request.headers
-        if response.output_status is None:
-            status = "-"
-        else:
-            status = response.output_status.split(" ", 1)[0]
-        
-        atoms = {'h': remote.name or remote.ip,
-                 'l': '-',
-                 'u': getattr(request, "login", None) or "-",
-                 't': self.time(),
-                 'r': request.request_line,
-                 's': status,
-                 'b': dict.get(outheaders, 'Content-Length', '') or "-",
-                 'f': dict.get(inheaders, 'Referer', ''),
-                 'a': dict.get(inheaders, 'User-Agent', ''),
-                 }
-        for k, v in atoms.items():
-            if isinstance(v, unicode):
-                v = v.encode('utf8')
-            elif not isinstance(v, str):
-                v = str(v)
-            # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
-            # and backslash for us. All we have to do is strip the quotes.
-            v = repr(v)[1:-1]
-            # Escape double-quote.
-            atoms[k] = v.replace('"', '\\"')
-        
-        try:
-            self.access_log.log(logging.INFO, self.access_log_format % atoms)
-        except:
-            self(traceback=True)
-    
-    def time(self):
-        """Return now() in Apache Common Log Format (no timezone)."""
-        now = datetime.datetime.now()
-        monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
-                      'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
-        month = monthnames[now.month - 1].capitalize()
-        return ('[%02d/%s/%04d:%02d:%02d:%02d]' %
-                (now.day, month, now.year, now.hour, now.minute, now.second))
-    
-    def _get_builtin_handler(self, log, key):
-        for h in log.handlers:
-            if getattr(h, "_cpbuiltin", None) == key:
-                return h
-    
-    
-    # ------------------------- Screen handlers ------------------------- #
-    
-    def _set_screen_handler(self, log, enable, stream=None):
-        h = self._get_builtin_handler(log, "screen")
-        if enable:
-            if not h:
-                if stream is None:
-                    stream=sys.stderr
-                h = logging.StreamHandler(stream)
-                h.setFormatter(logfmt)
-                h._cpbuiltin = "screen"
-                log.addHandler(h)
-        elif h:
-            log.handlers.remove(h)
-    
-    def _get_screen(self):
-        h = self._get_builtin_handler
-        has_h = h(self.error_log, "screen") or h(self.access_log, "screen")
-        return bool(has_h)
-    
-    def _set_screen(self, newvalue):
-        self._set_screen_handler(self.error_log, newvalue, stream=sys.stderr)
-        self._set_screen_handler(self.access_log, newvalue, stream=sys.stdout)
-    screen = property(_get_screen, _set_screen,
-                      doc="If True, error and access will print to stderr.")
-    
-    
-    # -------------------------- File handlers -------------------------- #
-    
-    def _add_builtin_file_handler(self, log, fname):
-        h = logging.FileHandler(fname)
-        h.setFormatter(logfmt)
-        h._cpbuiltin = "file"
-        log.addHandler(h)
-    
-    def _set_file_handler(self, log, filename):
-        h = self._get_builtin_handler(log, "file")
-        if filename:
-            if h:
-                if h.baseFilename != os.path.abspath(filename):
-                    h.close()
-                    log.handlers.remove(h)
-                    self._add_builtin_file_handler(log, filename)
-            else:
-                self._add_builtin_file_handler(log, filename)
-        else:
-            if h:
-                h.close()
-                log.handlers.remove(h)
-    
-    def _get_error_file(self):
-        h = self._get_builtin_handler(self.error_log, "file")
-        if h:
-            return h.baseFilename
-        return ''
-    def _set_error_file(self, newvalue):
-        self._set_file_handler(self.error_log, newvalue)
-    error_file = property(_get_error_file, _set_error_file,
-                          doc="The filename for self.error_log.")
-    
-    def _get_access_file(self):
-        h = self._get_builtin_handler(self.access_log, "file")
-        if h:
-            return h.baseFilename
-        return ''
-    def _set_access_file(self, newvalue):
-        self._set_file_handler(self.access_log, newvalue)
-    access_file = property(_get_access_file, _set_access_file,
-                           doc="The filename for self.access_log.")
-    
-    
-    # ------------------------- WSGI handlers ------------------------- #
-    
-    def _set_wsgi_handler(self, log, enable):
-        h = self._get_builtin_handler(log, "wsgi")
-        if enable:
-            if not h:
-                h = WSGIErrorHandler()
-                h.setFormatter(logfmt)
-                h._cpbuiltin = "wsgi"
-                log.addHandler(h)
-        elif h:
-            log.handlers.remove(h)
-    
-    def _get_wsgi(self):
-        return bool(self._get_builtin_handler(self.error_log, "wsgi"))
-    
-    def _set_wsgi(self, newvalue):
-        self._set_wsgi_handler(self.error_log, newvalue)
-    wsgi = property(_get_wsgi, _set_wsgi,
-                      doc="If True, error messages will be sent to wsgi.errors.")
-
-
-class WSGIErrorHandler(logging.Handler):
-    "A handler class which writes logging records to environ['wsgi.errors']."
-    
-    def flush(self):
-        """Flushes the stream."""
-        try:
-            stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
-        except (AttributeError, KeyError):
-            pass
-        else:
-            stream.flush()
-    
-    def emit(self, record):
-        """Emit a record."""
-        try:
-            stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
-        except (AttributeError, KeyError):
-            pass
-        else:
-            try:
-                msg = self.format(record)
-                fs = "%s\n"
-                import types
-                if not hasattr(types, "UnicodeType"): #if no unicode support...
-                    stream.write(fs % msg)
-                else:
-                    try:
-                        stream.write(fs % msg)
-                    except UnicodeError:
-                        stream.write(fs % msg.encode("UTF-8"))
-                self.flush()
-            except:
-                self.handleError(record)
--- a/bundled/cherrypy/cherrypy/_cpmodpy.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,333 +0,0 @@
-"""Native adapter for serving CherryPy via mod_python
-
-Basic usage:
-
-##########################################
-# Application in a module called myapp.py
-##########################################
-
-import cherrypy
-
-class Root:
-    @cherrypy.expose
-    def index(self):
-        return 'Hi there, Ho there, Hey there'
-
-
-# We will use this method from the mod_python configuration
-# as the entry point to our application
-def setup_server():
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({'environment': 'production',
-                            'log.screen': False,
-                            'show_tracebacks': False})
-
-##########################################
-# mod_python settings for apache2
-# This should reside in your httpd.conf
-# or a file that will be loaded at
-# apache startup
-##########################################
-
-# Start
-DocumentRoot "/"
-Listen 8080
-LoadModule python_module /usr/lib/apache2/modules/mod_python.so
-
-<Location "/">
-	PythonPath "sys.path+['/path/to/my/application']" 
-	SetHandler python-program
-	PythonHandler cherrypy._cpmodpy::handler
-	PythonOption cherrypy.setup myapp::setup_server
-	PythonDebug On
-</Location> 
-# End
-
-The actual path to your mod_python.so is dependent on your
-environment. In this case we suppose a global mod_python
-installation on a Linux distribution such as Ubuntu.
-
-We do set the PythonPath configuration setting so that
-your application can be found by from the user running
-the apache2 instance. Of course if your application
-resides in the global site-package this won't be needed.
-
-Then restart apache2 and access http://127.0.0.1:8080
-"""
-
-import logging
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-import cherrypy
-from cherrypy._cperror import format_exc, bare_error
-from cherrypy.lib import httputil
-
-
-# ------------------------------ Request-handling
-
-
-
-def setup(req):
-    from mod_python import apache
-    
-    # Run any setup functions defined by a "PythonOption cherrypy.setup" directive.
-    options = req.get_options()
-    if 'cherrypy.setup' in options:
-        for function in options['cherrypy.setup'].split():
-            atoms = function.split('::', 1)
-            if len(atoms) == 1:
-                mod = __import__(atoms[0], globals(), locals())
-            else:
-                modname, fname = atoms
-                mod = __import__(modname, globals(), locals(), [fname])
-                func = getattr(mod, fname)
-                func()
-    
-    cherrypy.config.update({'log.screen': False,
-                            "tools.ignore_headers.on": True,
-                            "tools.ignore_headers.headers": ['Range'],
-                            })
-    
-    engine = cherrypy.engine
-    if hasattr(engine, "signal_handler"):
-        engine.signal_handler.unsubscribe()
-    if hasattr(engine, "console_control_handler"):
-        engine.console_control_handler.unsubscribe()
-    engine.autoreload.unsubscribe()
-    cherrypy.server.unsubscribe()
-    
-    def _log(msg, level):
-        newlevel = apache.APLOG_ERR
-        if logging.DEBUG >= level:
-            newlevel = apache.APLOG_DEBUG
-        elif logging.INFO >= level:
-            newlevel = apache.APLOG_INFO
-        elif logging.WARNING >= level:
-            newlevel = apache.APLOG_WARNING
-        # On Windows, req.server is required or the msg will vanish. See
-        # http://www.modpython.org/pipermail/mod_python/2003-October/014291.html.
-        # Also, "When server is not specified...LogLevel does not apply..."
-        apache.log_error(msg, newlevel, req.server)
-    engine.subscribe('log', _log)
-    
-    engine.start()
-    
-    def cherrypy_cleanup(data):
-        engine.exit()
-    try:
-        # apache.register_cleanup wasn't available until 3.1.4.
-        apache.register_cleanup(cherrypy_cleanup)
-    except AttributeError:
-        req.server.register_cleanup(req, cherrypy_cleanup)
-
-
-class _ReadOnlyRequest:
-    expose = ('read', 'readline', 'readlines')
-    def __init__(self, req):
-        for method in self.expose:
-            self.__dict__[method] = getattr(req, method)
-
-
-recursive = False
-
-_isSetUp = False
-def handler(req):
-    from mod_python import apache
-    try:
-        global _isSetUp
-        if not _isSetUp:
-            setup(req)
-            _isSetUp = True
-        
-        # Obtain a Request object from CherryPy
-        local = req.connection.local_addr
-        local = httputil.Host(local[0], local[1], req.connection.local_host or "")
-        remote = req.connection.remote_addr
-        remote = httputil.Host(remote[0], remote[1], req.connection.remote_host or "")
-        
-        scheme = req.parsed_uri[0] or 'http'
-        req.get_basic_auth_pw()
-        
-        try:
-            # apache.mpm_query only became available in mod_python 3.1
-            q = apache.mpm_query
-            threaded = q(apache.AP_MPMQ_IS_THREADED)
-            forked = q(apache.AP_MPMQ_IS_FORKED)
-        except AttributeError:
-            bad_value = ("You must provide a PythonOption '%s', "
-                         "either 'on' or 'off', when running a version "
-                         "of mod_python < 3.1")
-            
-            threaded = options.get('multithread', '').lower()
-            if threaded == 'on':
-                threaded = True
-            elif threaded == 'off':
-                threaded = False
-            else:
-                raise ValueError(bad_value % "multithread")
-            
-            forked = options.get('multiprocess', '').lower()
-            if forked == 'on':
-                forked = True
-            elif forked == 'off':
-                forked = False
-            else:
-                raise ValueError(bad_value % "multiprocess")
-        
-        sn = cherrypy.tree.script_name(req.uri or "/")
-        if sn is None:
-            send_response(req, '404 Not Found', [], '')
-        else:
-            app = cherrypy.tree.apps[sn]
-            method = req.method
-            path = req.uri
-            qs = req.args or ""
-            reqproto = req.protocol
-            headers = req.headers_in.items()
-            rfile = _ReadOnlyRequest(req)
-            prev = None
-            
-            try:
-                redirections = []
-                while True:
-                    request, response = app.get_serving(local, remote, scheme,
-                                                        "HTTP/1.1")
-                    request.login = req.user
-                    request.multithread = bool(threaded)
-                    request.multiprocess = bool(forked)
-                    request.app = app
-                    request.prev = prev
-                    
-                    # Run the CherryPy Request object and obtain the response
-                    try:
-                        request.run(method, path, qs, reqproto, headers, rfile)
-                        break
-                    except cherrypy.InternalRedirect, ir:
-                        app.release_serving()
-                        prev = request
-                        
-                        if not recursive:
-                            if ir.path in redirections:
-                                raise RuntimeError("InternalRedirector visited the "
-                                                   "same URL twice: %r" % ir.path)
-                            else:
-                                # Add the *previous* path_info + qs to redirections.
-                                if qs:
-                                    qs = "?" + qs
-                                redirections.append(sn + path + qs)
-                        
-                        # Munge environment and try again.
-                        method = "GET"
-                        path = ir.path
-                        qs = ir.query_string
-                        rfile = StringIO()
-                
-                send_response(req, response.status, response.header_list,
-                              response.body, response.stream)
-            finally:
-                app.release_serving()
-    except:
-        tb = format_exc()
-        cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
-        s, h, b = bare_error()
-        send_response(req, s, h, b)
-    return apache.OK
-
-
-def send_response(req, status, headers, body, stream=False):
-    # Set response status
-    req.status = int(status[:3])
-    
-    # Set response headers
-    req.content_type = "text/plain"
-    for header, value in headers:
-        if header.lower() == 'content-type':
-            req.content_type = value
-            continue
-        req.headers_out.add(header, value)
-    
-    if stream:
-        # Flush now so the status and headers are sent immediately.
-        req.flush()
-    
-    # Set response body
-    if isinstance(body, basestring):
-        req.write(body)
-    else:
-        for seg in body:
-            req.write(seg)
-
-
-
-# --------------- Startup tools for CherryPy + mod_python --------------- #
-
-
-import os
-import re
-
-
-def read_process(cmd, args=""):
-    pipein, pipeout = os.popen4("%s %s" % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r"(not recognized|No such file|not found)", firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-class ModPythonServer(object):
-    
-    template = """
-# Apache2 server configuration file for running CherryPy with mod_python.
-
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-<Location %(loc)s>
-    SetHandler python-program
-    PythonHandler %(handler)s
-    PythonDebug On
-%(opts)s
-</Location>
-"""
-    
-    def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
-                 handler="cherrypy._cpmodpy::handler"):
-        self.loc = loc
-        self.port = port
-        self.opts = opts
-        self.apache_path = apache_path
-        self.handler = handler
-    
-    def start(self):
-        opts = "".join(["    PythonOption %s %s\n" % (k, v)
-                        for k, v in self.opts])
-        conf_data = self.template % {"port": self.port,
-                                     "loc": self.loc,
-                                     "opts": opts,
-                                     "handler": self.handler,
-                                     }
-        
-        mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
-        f = open(mpconf, 'wb')
-        try:
-            f.write(conf_data)
-        finally:
-            f.close()
-        
-        response = read_process(self.apache_path, "-k start -f %s" % mpconf)
-        self.ready = True
-        return response
-    
-    def stop(self):
-        os.popen("apache -k stop")
-        self.ready = False
-
--- a/bundled/cherrypy/cherrypy/_cpnative_server.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-"""Native adapter for serving CherryPy via its builtin server."""
-
-import logging
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-import cherrypy
-from cherrypy._cperror import format_exc, bare_error
-from cherrypy.lib import httputil
-from cherrypy import wsgiserver
-
-
-class NativeGateway(wsgiserver.Gateway):
-    
-    recursive = False
-    
-    def respond(self):
-        req = self.req
-        try:
-            # Obtain a Request object from CherryPy
-            local = req.server.bind_addr
-            local = httputil.Host(local[0], local[1], "")
-            remote = req.conn.remote_addr, req.conn.remote_port
-            remote = httputil.Host(remote[0], remote[1], "")
-            
-            scheme = req.scheme
-            sn = cherrypy.tree.script_name(req.uri or "/")
-            if sn is None:
-                self.send_response('404 Not Found', [], [''])
-            else:
-                app = cherrypy.tree.apps[sn]
-                method = req.method
-                path = req.path
-                qs = req.qs or ""
-                headers = req.inheaders.items()
-                rfile = req.rfile
-                prev = None
-                
-                try:
-                    redirections = []
-                    while True:
-                        request, response = app.get_serving(
-                            local, remote, scheme, "HTTP/1.1")
-                        request.multithread = True
-                        request.multiprocess = False
-                        request.app = app
-                        request.prev = prev
-                        
-                        # Run the CherryPy Request object and obtain the response
-                        try:
-                            request.run(method, path, qs, req.request_protocol, headers, rfile)
-                            break
-                        except cherrypy.InternalRedirect, ir:
-                            app.release_serving()
-                            prev = request
-                            
-                            if not self.recursive:
-                                if ir.path in redirections:
-                                    raise RuntimeError("InternalRedirector visited the "
-                                                       "same URL twice: %r" % ir.path)
-                                else:
-                                    # Add the *previous* path_info + qs to redirections.
-                                    if qs:
-                                        qs = "?" + qs
-                                    redirections.append(sn + path + qs)
-                            
-                            # Munge environment and try again.
-                            method = "GET"
-                            path = ir.path
-                            qs = ir.query_string
-                            rfile = StringIO()
-                    
-                    self.send_response(
-                        response.output_status, response.header_list,
-                        response.body)
-                finally:
-                    app.release_serving()
-        except:
-            tb = format_exc()
-            #print tb
-            cherrypy.log(tb, 'NATIVE_ADAPTER', severity=logging.ERROR)
-            s, h, b = bare_error()
-            self.send_response(s, h, b)
-    
-    def send_response(self, status, headers, body):
-        req = self.req
-        
-        # Set response status
-        req.status = str(status or "500 Server Error")
-        
-        # Set response headers
-        for header, value in headers:
-            req.outheaders.append((header, value))
-        if (req.ready and not req.sent_headers):
-            req.sent_headers = True
-            req.send_headers()
-        
-        # Set response body
-        for seg in body:
-            req.write(seg)
-
-
-class CPHTTPServer(wsgiserver.HTTPServer):
-    """Wrapper for wsgiserver.HTTPServer.
-    
-    wsgiserver has been designed to not reference CherryPy in any way,
-    so that it can be used in other frameworks and applications.
-    Therefore, we wrap it here, so we can apply some attributes
-    from config -> cherrypy.server -> HTTPServer.
-    """
-    
-    def __init__(self, server_adapter=cherrypy.server):
-        self.server_adapter = server_adapter
-        
-        server_name = (self.server_adapter.socket_host or
-                       self.server_adapter.socket_file or
-                       None)
-        
-        wsgiserver.HTTPServer.__init__(
-            self, server_adapter.bind_addr, NativeGateway,
-            minthreads=server_adapter.thread_pool,
-            maxthreads=server_adapter.thread_pool_max,
-            server_name=server_name)
-        
-        self.max_request_header_size = self.server_adapter.max_request_header_size or 0
-        self.max_request_body_size = self.server_adapter.max_request_body_size or 0
-        self.request_queue_size = self.server_adapter.socket_queue_size
-        self.timeout = self.server_adapter.socket_timeout
-        self.shutdown_timeout = self.server_adapter.shutdown_timeout
-        self.protocol = self.server_adapter.protocol_version
-        self.nodelay = self.server_adapter.nodelay
-        
-        ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
-        if self.server_adapter.ssl_context:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain)
-            self.ssl_adapter.context = self.server_adapter.ssl_context
-        elif self.server_adapter.ssl_certificate:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain)
-
-
--- a/bundled/cherrypy/cherrypy/_cpreqbody.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,723 +0,0 @@
-"""Request body processing for CherryPy.
-
-When an HTTP request includes an entity body, it is often desirable to
-provide that information to applications in a form other than the raw bytes.
-Different content types demand different approaches. Examples:
-
- * For a GIF file, we want the raw bytes in a stream.
- * An HTML form is better parsed into its component fields, and each text field
-    decoded from bytes to unicode.
- * A JSON body should be deserialized into a Python dict or list.
-
-When the request contains a Content-Type header, the media type is used as a
-key to look up a value in the 'request.body.processors' dict. If the full media
-type is not found, then the major type is tried; for example, if no processor
-is found for the 'image/jpeg' type, then we look for a processor for the 'image'
-types altogether. If neither the full type nor the major type has a matching
-processor, then a default processor is used (self.default_proc). For most
-types, this means no processing is done, and the body is left unread as a
-raw byte stream. Processors are configurable in an 'on_start_resource' hook.
-
-Some processors, especially those for the 'text' types, attempt to decode bytes
-to unicode. If the Content-Type request header includes a 'charset' parameter,
-this is used to decode the entity. Otherwise, one or more default charsets may
-be attempted, although this decision is up to each processor. If a processor
-successfully decodes an Entity or Part, it should set the 'charset' attribute
-on the Entity or Part to the name of the successful charset, so that
-applications can easily re-encode or transcode the value if they wish.
-
-If the Content-Type of the request entity is of major type 'multipart', then
-the above parsing process, and possibly a decoding process, is performed for
-each part.
-
-For both the full entity and multipart parts, a Content-Disposition header may
-be used to fill .name and .filename attributes on the request.body or the Part.
-"""
-
-import re
-import tempfile
-from urllib import unquote_plus
-
-import cherrypy
-from cherrypy.lib import httputil
-
-
-# -------------------------------- Processors -------------------------------- #
-
-def process_urlencoded(entity):
-    """Read application/x-www-form-urlencoded data into entity.params."""
-    qs = entity.fp.read()
-    for charset in entity.attempt_charsets:
-        try:
-            params = {}
-            for aparam in qs.split('&'):
-                for pair in aparam.split(';'):
-                    if not pair:
-                        continue
-                    
-                    atoms = pair.split('=', 1)
-                    if len(atoms) == 1:
-                        atoms.append('')
-                    
-                    key = unquote_plus(atoms[0]).decode(charset)
-                    value = unquote_plus(atoms[1]).decode(charset)
-                    
-                    if key in params:
-                        if not isinstance(params[key], list):
-                            params[key] = [params[key]]
-                        params[key].append(value)
-                    else:
-                        params[key] = value
-        except UnicodeDecodeError:
-            pass
-        else:
-            entity.charset = charset
-            break
-    else:
-        raise cherrypy.HTTPError(
-            400, "The request entity could not be decoded. The following "
-            "charsets were attempted: %s" % repr(entity.attempt_charsets))
-        
-    # Now that all values have been successfully parsed and decoded,
-    # apply them to the entity.params dict.
-    for key, value in params.items():
-        if key in entity.params:
-            if not isinstance(entity.params[key], list):
-                entity.params[key] = [entity.params[key]]
-            entity.params[key].append(value)
-        else:
-            entity.params[key] = value
-
-
-def process_multipart(entity):
-    """Read all multipart parts into entity.parts."""
-    ib = u""
-    if u'boundary' in entity.content_type.params:
-        # http://tools.ietf.org/html/rfc2046#section-5.1.1
-        # "The grammar for parameters on the Content-type field is such that it
-        # is often necessary to enclose the boundary parameter values in quotes
-        # on the Content-type line"
-        ib = entity.content_type.params['boundary'].strip(u'"')
-    
-    if not re.match(u"^[ -~]{0,200}[!-~]$", ib):
-        raise ValueError(u'Invalid boundary in multipart form: %r' % (ib,))
-    
-    ib = (u'--' + ib).encode('ascii')
-    
-    # Find the first marker
-    while True:
-        b = entity.readline()
-        if not b:
-            return
-        
-        b = b.strip()
-        if b == ib:
-            break
-    
-    # Read all parts
-    while True:
-        part = entity.part_class.from_fp(entity.fp, ib)
-        entity.parts.append(part)
-        part.process()
-        if part.fp.done:
-            break
-
-def process_multipart_form_data(entity):
-    """Read all multipart/form-data parts into entity.parts or entity.params."""
-    process_multipart(entity)
-    
-    kept_parts = []
-    for part in entity.parts:
-        if part.name is None:
-            kept_parts.append(part)
-        else:
-            if part.filename is None:
-                # It's a regular field
-                entity.params[part.name] = part.fullvalue()
-            else:
-                # It's a file upload. Retain the whole part so consumer code
-                # has access to its .file and .filename attributes.
-                entity.params[part.name] = part
-    
-    entity.parts = kept_parts
-
-def _old_process_multipart(entity):
-    """The behavior of 3.2 and lower. Deprecated and will be changed in 3.3."""
-    process_multipart(entity)
-    
-    params = entity.params
-    
-    for part in entity.parts:
-        if part.name is None:
-            key = u'parts'
-        else:
-            key = part.name
-        
-        if part.filename is None:
-            # It's a regular field
-            value = part.fullvalue()
-        else:
-            # It's a file upload. Retain the whole part so consumer code
-            # has access to its .file and .filename attributes.
-            value = part
-        
-        if key in params:
-            if not isinstance(params[key], list):
-                params[key] = [params[key]]
-            params[key].append(value)
-        else:
-            params[key] = value
-
-
-
-# --------------------------------- Entities --------------------------------- #
-
-
-class Entity(object):
-    """An HTTP request body, or MIME multipart body."""
-    
-    __metaclass__ = cherrypy._AttributeDocstrings
-    
-    params = None
-    params__doc = u"""
-    If the request Content-Type is 'application/x-www-form-urlencoded' or
-    multipart, this will be a dict of the params pulled from the entity
-    body; that is, it will be the portion of request.params that come
-    from the message body (sometimes called "POST params", although they
-    can be sent with various HTTP method verbs). This value is set between
-    the 'before_request_body' and 'before_handler' hooks (assuming that
-    process_request_body is True)."""
-    
-    default_content_type = u'application/x-www-form-urlencoded'
-    # http://tools.ietf.org/html/rfc2046#section-4.1.2:
-    # "The default character set, which must be assumed in the
-    # absence of a charset parameter, is US-ASCII."
-    # However, many browsers send data in utf-8 with no charset.
-    attempt_charsets = [u'utf-8']
-    processors = {u'application/x-www-form-urlencoded': process_urlencoded,
-                  u'multipart/form-data': process_multipart_form_data,
-                  u'multipart': process_multipart,
-                  }
-    
-    def __init__(self, fp, headers, params=None, parts=None):
-        # Make an instance-specific copy of the class processors
-        # so Tools, etc. can replace them per-request.
-        self.processors = self.processors.copy()
-        
-        self.fp = fp
-        self.headers = headers
-        
-        if params is None:
-            params = {}
-        self.params = params
-        
-        if parts is None:
-            parts = []
-        self.parts = parts
-        
-        # Content-Type
-        self.content_type = headers.elements(u'Content-Type')
-        if self.content_type:
-            self.content_type = self.content_type[0]
-        else:
-            self.content_type = httputil.HeaderElement.from_str(
-                self.default_content_type)
-        
-        # Copy the class 'attempt_charsets', prepending any Content-Type charset
-        dec = self.content_type.params.get(u"charset", None)
-        if dec:
-            dec = dec.decode('ISO-8859-1')
-            self.attempt_charsets = [dec] + [c for c in self.attempt_charsets
-                                             if c != dec]
-        else:
-            self.attempt_charsets = self.attempt_charsets[:]
-        
-        # Length
-        self.length = None
-        clen = headers.get(u'Content-Length', None)
-        # If Transfer-Encoding is 'chunked', ignore any Content-Length.
-        if clen is not None and 'chunked' not in headers.get(u'Transfer-Encoding', ''):
-            try:
-                self.length = int(clen)
-            except ValueError:
-                pass
-        
-        # Content-Disposition
-        self.name = None
-        self.filename = None
-        disp = headers.elements(u'Content-Disposition')
-        if disp:
-            disp = disp[0]
-            if 'name' in disp.params:
-                self.name = disp.params['name']
-                if self.name.startswith(u'"') and self.name.endswith(u'"'):
-                    self.name = self.name[1:-1]
-            if 'filename' in disp.params:
-                self.filename = disp.params['filename']
-                if self.filename.startswith(u'"') and self.filename.endswith(u'"'):
-                    self.filename = self.filename[1:-1]
-    
-    # The 'type' attribute is deprecated in 3.2; remove it in 3.3.
-    type = property(lambda self: self.content_type)
-    
-    def read(self, size=None, fp_out=None):
-        return self.fp.read(size, fp_out)
-    
-    def readline(self, size=None):
-        return self.fp.readline(size)
-    
-    def readlines(self, sizehint=None):
-        return self.fp.readlines(sizehint)
-    
-    def __iter__(self):
-        return self
-    
-    def next(self):
-        line = self.readline()
-        if not line:
-            raise StopIteration
-        return line
-    
-    def read_into_file(self, fp_out=None):
-        """Read the request body into fp_out (or make_file() if None). Return fp_out."""
-        if fp_out is None:
-            fp_out = self.make_file()
-        self.read(fp_out=fp_out)
-        return fp_out
-    
-    def make_file(self):
-        """Return a file into which the request body will be read.
-        
-        By default, this will return a TemporaryFile. Override as needed."""
-        return tempfile.TemporaryFile()
-    
-    def fullvalue(self):
-        """Return this entity as a string, whether stored in a file or not."""
-        if self.file:
-            # It was stored in a tempfile. Read it.
-            self.file.seek(0)
-            value = self.file.read()
-            self.file.seek(0)
-        else:
-            value = self.value
-        return value
-    
-    def process(self):
-        """Execute the best-match processor for the given media type."""
-        proc = None
-        ct = self.content_type.value
-        try:
-            proc = self.processors[ct]
-        except KeyError:
-            toptype = ct.split(u'/', 1)[0]
-            try:
-                proc = self.processors[toptype]
-            except KeyError:
-                pass
-        if proc is None:
-            self.default_proc()
-        else:
-            proc(self)
-    
-    def default_proc(self):
-        # Leave the fp alone for someone else to read. This works fine
-        # for request.body, but the Part subclasses need to override this
-        # so they can move on to the next part.
-        pass
-
-
-class Part(Entity):
-    """A MIME part entity, part of a multipart entity."""
-    
-    default_content_type = u'text/plain'
-    # "The default character set, which must be assumed in the absence of a
-    # charset parameter, is US-ASCII."
-    attempt_charsets = [u'us-ascii', u'utf-8']
-    # This is the default in stdlib cgi. We may want to increase it.
-    maxrambytes = 1000
-    
-    def __init__(self, fp, headers, boundary):
-        Entity.__init__(self, fp, headers)
-        self.boundary = boundary
-        self.file = None
-        self.value = None
-    
-    def from_fp(cls, fp, boundary):
-        headers = cls.read_headers(fp)
-        return cls(fp, headers, boundary)
-    from_fp = classmethod(from_fp)
-    
-    def read_headers(cls, fp):
-        headers = httputil.HeaderMap()
-        while True:
-            line = fp.readline()
-            if not line:
-                # No more data--illegal end of headers
-                raise EOFError(u"Illegal end of headers.")
-            
-            if line == '\r\n':
-                # Normal end of headers
-                break
-            if not line.endswith('\r\n'):
-                raise ValueError(u"MIME requires CRLF terminators: %r" % line)
-            
-            if line[0] in ' \t':
-                # It's a continuation line.
-                v = line.strip().decode(u'ISO-8859-1')
-            else:
-                k, v = line.split(":", 1)
-                k = k.strip().decode(u'ISO-8859-1')
-                v = v.strip().decode(u'ISO-8859-1')
-            
-            existing = headers.get(k)
-            if existing:
-                v = u", ".join((existing, v))
-            headers[k] = v
-        
-        return headers
-    read_headers = classmethod(read_headers)
-    
-    def read_lines_to_boundary(self, fp_out=None):
-        """Read bytes from self.fp and return or write them to a file.
-        
-        If the 'fp_out' argument is None (the default), all bytes read are
-        returned in a single byte string.
-        
-        If the 'fp_out' argument is not None, it must be a file-like object that
-        supports the 'write' method; all bytes read will be written to the fp,
-        and that fp is returned.
-        """
-        endmarker = self.boundary + "--"
-        delim = ""
-        prev_lf = True
-        lines = []
-        seen = 0
-        while True:
-            line = self.fp.readline(1<<16)
-            if not line:
-                raise EOFError(u"Illegal end of multipart body.")
-            if line.startswith("--") and prev_lf:
-                strippedline = line.strip()
-                if strippedline == self.boundary:
-                    break
-                if strippedline == endmarker:
-                    self.fp.finish()
-                    break
-            
-            line = delim + line
-            
-            if line.endswith("\r\n"):
-                delim = "\r\n"
-                line = line[:-2]
-                prev_lf = True
-            elif line.endswith("\n"):
-                delim = "\n"
-                line = line[:-1]
-                prev_lf = True
-            else:
-                delim = ""
-                prev_lf = False
-            
-            if fp_out is None:
-                lines.append(line)
-                seen += len(line)
-                if seen > self.maxrambytes:
-                    fp_out = self.make_file()
-                    for line in lines:
-                        fp_out.write(line)
-            else:
-                fp_out.write(line)
-        
-        if fp_out is None:
-            result = ''.join(lines)
-            for charset in self.attempt_charsets:
-                try:
-                    result = result.decode(charset)
-                except UnicodeDecodeError:
-                    pass
-                else:
-                    self.charset = charset
-                    return result
-            else:
-                raise cherrypy.HTTPError(
-                    400, "The request entity could not be decoded. The following "
-                    "charsets were attempted: %s" % repr(self.attempt_charsets))
-        else:
-            fp_out.seek(0)
-            return fp_out
-    
-    def default_proc(self):
-        if self.filename:
-            # Always read into a file if a .filename was given.
-            self.file = self.read_into_file()
-        else:
-            result = self.read_lines_to_boundary()
-            if isinstance(result, basestring):
-                self.value = result
-            else:
-                self.file = result
-    
-    def read_into_file(self, fp_out=None):
-        """Read the request body into fp_out (or make_file() if None). Return fp_out."""
-        if fp_out is None:
-            fp_out = self.make_file()
-        self.read_lines_to_boundary(fp_out=fp_out)
-        return fp_out
-
-Entity.part_class = Part
-
-
-class Infinity(object):
-    def __cmp__(self, other):
-        return 1
-    def __sub__(self, other):
-        return self
-inf = Infinity()
-
-
-comma_separated_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding',
-    'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control', 'Connection',
-    'Content-Encoding', 'Content-Language', 'Expect', 'If-Match',
-    'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'Te', 'Trailer',
-    'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning', 'Www-Authenticate']
-
-
-class SizedReader:
-    
-    def __init__(self, fp, length, maxbytes, bufsize=8192, has_trailers=False):
-        # Wrap our fp in a buffer so peek() works
-        self.fp = fp
-        self.length = length
-        self.maxbytes = maxbytes
-        self.buffer = ''
-        self.bufsize = bufsize
-        self.bytes_read = 0
-        self.done = False
-        self.has_trailers = has_trailers
-    
-    def read(self, size=None, fp_out=None):
-        """Read bytes from the request body and return or write them to a file.
-        
-        A number of bytes less than or equal to the 'size' argument are read
-        off the socket. The actual number of bytes read are tracked in
-        self.bytes_read. The number may be smaller than 'size' when 1) the
-        client sends fewer bytes, 2) the 'Content-Length' request header
-        specifies fewer bytes than requested, or 3) the number of bytes read
-        exceeds self.maxbytes (in which case, 413 is raised).
-        
-        If the 'fp_out' argument is None (the default), all bytes read are
-        returned in a single byte string.
-        
-        If the 'fp_out' argument is not None, it must be a file-like object that
-        supports the 'write' method; all bytes read will be written to the fp,
-        and None is returned.
-        """
-        
-        if self.length is None:
-            if size is None:
-                remaining = inf
-            else:
-                remaining = size
-        else:
-            remaining = self.length - self.bytes_read
-            if size and size < remaining:
-                remaining = size
-        if remaining == 0:
-            self.finish()
-            if fp_out is None:
-                return ''
-            else:
-                return None
-        
-        chunks = []
-        
-        # Read bytes from the buffer.
-        if self.buffer:
-            if remaining is inf:
-                data = self.buffer
-                self.buffer = ''
-            else:
-                data = self.buffer[:remaining]
-                self.buffer = self.buffer[remaining:]
-            datalen = len(data)
-            remaining -= datalen
-            
-            # Check lengths.
-            self.bytes_read += datalen
-            if self.maxbytes and self.bytes_read > self.maxbytes:
-                raise cherrypy.HTTPError(413)
-            
-            # Store the data.
-            if fp_out is None:
-                chunks.append(data)
-            else:
-                fp_out.write(data)
-        
-        # Read bytes from the socket.
-        while remaining > 0:
-            chunksize = min(remaining, self.bufsize)
-            try:
-                data = self.fp.read(chunksize)
-            except Exception, e:
-                if e.__class__.__name__ == 'MaxSizeExceeded':
-                    # Post data is too big
-                    raise cherrypy.HTTPError(
-                        413, "Maximum request length: %r" % e.args[1])
-                else:
-                    raise
-            if not data:
-                self.finish()
-                break
-            datalen = len(data)
-            remaining -= datalen
-            
-            # Check lengths.
-            self.bytes_read += datalen
-            if self.maxbytes and self.bytes_read > self.maxbytes:
-                raise cherrypy.HTTPError(413)
-            
-            # Store the data.
-            if fp_out is None:
-                chunks.append(data)
-            else:
-                fp_out.write(data)
-        
-        if fp_out is None:
-            return ''.join(chunks)
-    
-    def readline(self, size=None):
-        """Read a line from the request body and return it."""
-        chunks = []
-        while size is None or size > 0:
-            chunksize = self.bufsize
-            if size is not None and size < self.bufsize:
-                chunksize = size
-            data = self.read(chunksize)
-            if not data:
-                break
-            pos = data.find('\n') + 1
-            if pos:
-                chunks.append(data[:pos])
-                remainder = data[pos:]
-                self.buffer += remainder
-                self.bytes_read -= len(remainder)
-                break
-            else:
-                chunks.append(data)
-        return ''.join(chunks)
-    
-    def readlines(self, sizehint=None):
-        """Read lines from the request body and return them."""
-        if self.length is not None:
-            if sizehint is None:
-                sizehint = self.length - self.bytes_read
-            else:
-                sizehint = min(sizehint, self.length - self.bytes_read)
-        
-        lines = []
-        seen = 0
-        while True:
-            line = self.readline()
-            if not line:
-                break
-            lines.append(line)
-            seen += len(line)
-            if seen >= sizehint:
-                break
-        return lines
-    
-    def finish(self):
-        self.done = True
-        if self.has_trailers and hasattr(self.fp, 'read_trailer_lines'):
-            self.trailers = {}
-            
-            try:
-                for line in self.fp.read_trailer_lines():
-                    if line[0] in ' \t':
-                        # It's a continuation line.
-                        v = line.strip()
-                    else:
-                        try:
-                            k, v = line.split(":", 1)
-                        except ValueError:
-                            raise ValueError("Illegal header line.")
-                        k = k.strip().title()
-                        v = v.strip()
-                    
-                    if k in comma_separated_headers:
-                        existing = self.trailers.get(envname)
-                        if existing:
-                            v = ", ".join((existing, v))
-                    self.trailers[k] = v
-            except Exception, e:
-                if e.__class__.__name__ == 'MaxSizeExceeded':
-                    # Post data is too big
-                    raise cherrypy.HTTPError(
-                        413, "Maximum request length: %r" % e.args[1])
-                else:
-                    raise
-
-
-class RequestBody(Entity):
-    
-    # Don't parse the request body at all if the client didn't provide
-    # a Content-Type header. See http://www.cherrypy.org/ticket/790
-    default_content_type = u''
-    
-    bufsize = 8 * 1024
-    maxbytes = None
-    
-    def __init__(self, fp, headers, params=None, request_params=None):
-        Entity.__init__(self, fp, headers, params)
-        
-        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
-        # When no explicit charset parameter is provided by the
-        # sender, media subtypes of the "text" type are defined
-        # to have a default charset value of "ISO-8859-1" when
-        # received via HTTP.
-        if self.content_type.value.startswith('text/'):
-            for c in (u'ISO-8859-1', u'iso-8859-1', u'Latin-1', u'latin-1'):
-                if c in self.attempt_charsets:
-                    break
-            else:
-                self.attempt_charsets.append(u'ISO-8859-1')
-        
-        # Temporary fix while deprecating passing .parts as .params.
-        self.processors[u'multipart'] = _old_process_multipart
-        
-        if request_params is None:
-            request_params = {}
-        self.request_params = request_params
-    
-    def process(self):
-        """Include body params in request params."""
-        # "The presence of a message-body in a request is signaled by the
-        # inclusion of a Content-Length or Transfer-Encoding header field in
-        # the request's message-headers."
-        # It is possible to send a POST request with no body, for example;
-        # however, app developers are responsible in that case to set
-        # cherrypy.request.process_body to False so this method isn't called.
-        h = cherrypy.serving.request.headers
-        if u'Content-Length' not in h and u'Transfer-Encoding' not in h:
-            raise cherrypy.HTTPError(411)
-        
-        self.fp = SizedReader(self.fp, self.length,
-                              self.maxbytes, bufsize=self.bufsize,
-                              has_trailers='Trailer' in h)
-        super(RequestBody, self).process()
-        
-        # Body params should also be a part of the request_params
-        # add them in here.
-        request_params = self.request_params
-        for key, value in self.params.items():
-            # Python 2 only: keyword arguments must be byte strings (type 'str').
-            if isinstance(key, unicode):
-                key = key.encode('ISO-8859-1')
-            
-            if key in request_params:
-                if not isinstance(request_params[key], list):
-                    request_params[key] = [request_params[key]]
-                request_params[key].append(value)
-            else:
-                request_params[key] = value
-
--- a/bundled/cherrypy/cherrypy/_cprequest.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,940 +0,0 @@
-
-from Cookie import SimpleCookie, CookieError
-import os
-import sys
-import time
-import types
-import warnings
-
-import cherrypy
-from cherrypy import _cpreqbody, _cpconfig
-from cherrypy._cperror import format_exc, bare_error
-from cherrypy.lib import httputil, file_generator
-
-
-class Hook(object):
-    """A callback and its metadata: failsafe, priority, and kwargs."""
-    
-    __metaclass__ = cherrypy._AttributeDocstrings
-    
-    callback = None
-    callback__doc = """
-    The bare callable that this Hook object is wrapping, which will
-    be called when the Hook is called."""
-    
-    failsafe = False
-    failsafe__doc = """
-    If True, the callback is guaranteed to run even if other callbacks
-    from the same call point raise exceptions."""
-    
-    priority = 50
-    priority__doc = """
-    Defines the order of execution for a list of Hooks. Priority numbers
-    should be limited to the closed interval [0, 100], but values outside
-    this range are acceptable, as are fractional values."""
-    
-    kwargs = {}
-    kwargs__doc = """
-    A set of keyword arguments that will be passed to the
-    callable on each call."""
-    
-    def __init__(self, callback, failsafe=None, priority=None, **kwargs):
-        self.callback = callback
-        
-        if failsafe is None:
-            failsafe = getattr(callback, "failsafe", False)
-        self.failsafe = failsafe
-        
-        if priority is None:
-            priority = getattr(callback, "priority", 50)
-        self.priority = priority
-        
-        self.kwargs = kwargs
-    
-    def __cmp__(self, other):
-        return cmp(self.priority, other.priority)
-    
-    def __call__(self):
-        """Run self.callback(**self.kwargs)."""
-        return self.callback(**self.kwargs)
-    
-    def __repr__(self):
-        cls = self.__class__
-        return ("%s.%s(callback=%r, failsafe=%r, priority=%r, %s)"
-                % (cls.__module__, cls.__name__, self.callback,
-                   self.failsafe, self.priority,
-                   ", ".join(['%s=%r' % (k, v)
-                              for k, v in self.kwargs.items()])))
-
-
-class HookMap(dict):
-    """A map of call points to lists of callbacks (Hook objects)."""
-    
-    def __new__(cls, points=None):
-        d = dict.__new__(cls)
-        for p in points or []:
-            d[p] = []
-        return d
-    
-    def __init__(self, *a, **kw):
-        pass
-    
-    def attach(self, point, callback, failsafe=None, priority=None, **kwargs):
-        """Append a new Hook made from the supplied arguments."""
-        self[point].append(Hook(callback, failsafe, priority, **kwargs))
-    
-    def run(self, point):
-        """Execute all registered Hooks (callbacks) for the given point."""
-        exc = None
-        hooks = self[point]
-        hooks.sort()
-        for hook in hooks:
-            # Some hooks are guaranteed to run even if others at
-            # the same hookpoint fail. We will still log the failure,
-            # but proceed on to the next hook. The only way
-            # to stop all processing from one of these hooks is
-            # to raise SystemExit and stop the whole server.
-            if exc is None or hook.failsafe:
-                try:
-                    hook()
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except (cherrypy.HTTPError, cherrypy.HTTPRedirect,
-                        cherrypy.InternalRedirect):
-                    exc = sys.exc_info()[1]
-                except:
-                    exc = sys.exc_info()[1]
-                    cherrypy.log(traceback=True, severity=40)
-        if exc:
-            raise
-    
-    def __copy__(self):
-        newmap = self.__class__()
-        # We can't just use 'update' because we want copies of the
-        # mutable values (each is a list) as well.
-        for k, v in self.items():
-            newmap[k] = v[:]
-        return newmap
-    copy = __copy__
-    
-    def __repr__(self):
-        cls = self.__class__
-        return "%s.%s(points=%r)" % (cls.__module__, cls.__name__, self.keys())
-
-
-# Config namespace handlers
-
-def hooks_namespace(k, v):
-    """Attach bare hooks declared in config."""
-    # Use split again to allow multiple hooks for a single
-    # hookpoint per path (e.g. "hooks.before_handler.1").
-    # Little-known fact you only get from reading source ;)
-    hookpoint = k.split(".", 1)[0]
-    if isinstance(v, basestring):
-        v = cherrypy.lib.attributes(v)
-    if not isinstance(v, Hook):
-        v = Hook(v)
-    cherrypy.serving.request.hooks[hookpoint].append(v)
-
-def request_namespace(k, v):
-    """Attach request attributes declared in config."""
-    # Provides config entries to set request.body attrs (like attempt_charsets).
-    if k[:5] == 'body.':
-        setattr(cherrypy.serving.request.body, k[5:], v)
-    else:
-        setattr(cherrypy.serving.request, k, v)
-
-def response_namespace(k, v):
-    """Attach response attributes declared in config."""
-    # Provides config entries to set default response headers
-    # http://cherrypy.org/ticket/889
-    if k[:8] == 'headers.':
-        cherrypy.serving.response.headers[k.split('.', 1)[1]] = v
-    else:
-        setattr(cherrypy.serving.response, k, v)
-
-def error_page_namespace(k, v):
-    """Attach error pages declared in config."""
-    if k != 'default':
-        k = int(k)
-    cherrypy.serving.request.error_page[k] = v
-
-
-hookpoints = ['on_start_resource', 'before_request_body',
-              'before_handler', 'before_finalize',
-              'on_end_resource', 'on_end_request',
-              'before_error_response', 'after_error_response']
-
-
-class Request(object):
-    """An HTTP request.
-    
-    This object represents the metadata of an HTTP request message;
-    that is, it contains attributes which describe the environment
-    in which the request URL, headers, and body were sent (if you
-    want tools to interpret the headers and body, those are elsewhere,
-    mostly in Tools). This 'metadata' consists of socket data,
-    transport characteristics, and the Request-Line. This object
-    also contains data regarding the configuration in effect for
-    the given URL, and the execution plan for generating a response.
-    """
-    
-    __metaclass__ = cherrypy._AttributeDocstrings
-    
-    prev = None
-    prev__doc = """
-    The previous Request object (if any). This should be None
-    unless we are processing an InternalRedirect."""
-    
-    # Conversation/connection attributes
-    local = httputil.Host("127.0.0.1", 80)
-    local__doc = \
-        "An httputil.Host(ip, port, hostname) object for the server socket."
-    
-    remote = httputil.Host("127.0.0.1", 1111)
-    remote__doc = \
-        "An httputil.Host(ip, port, hostname) object for the client socket."
-    
-    scheme = "http"
-    scheme__doc = """
-    The protocol used between client and server. In most cases,
-    this will be either 'http' or 'https'."""
-    
-    server_protocol = "HTTP/1.1"
-    server_protocol__doc = """
-    The HTTP version for which the HTTP server is at least
-    conditionally compliant."""
-    
-    base = ""
-    base__doc = """The (scheme://host) portion of the requested URL.
-    In some cases (e.g. when proxying via mod_rewrite), this may contain
-    path segments which cherrypy.url uses when constructing url's, but
-    which otherwise are ignored by CherryPy. Regardless, this value
-    MUST NOT end in a slash."""
-    
-    # Request-Line attributes
-    request_line = ""
-    request_line__doc = """
-    The complete Request-Line received from the client. This is a
-    single string consisting of the request method, URI, and protocol
-    version (joined by spaces). Any final CRLF is removed."""
-    
-    method = "GET"
-    method__doc = """
-    Indicates the HTTP method to be performed on the resource identified
-    by the Request-URI. Common methods include GET, HEAD, POST, PUT, and
-    DELETE. CherryPy allows any extension method; however, various HTTP
-    servers and gateways may restrict the set of allowable methods.
-    CherryPy applications SHOULD restrict the set (on a per-URI basis)."""
-    
-    query_string = ""
-    query_string__doc = """
-    The query component of the Request-URI, a string of information to be
-    interpreted by the resource. The query portion of a URI follows the
-    path component, and is separated by a '?'. For example, the URI
-    'http://www.cherrypy.org/wiki?a=3&b=4' has the query component,
-    'a=3&b=4'."""
-    
-    query_string_encoding = 'utf8'
-    query_string_encoding__doc = """
-    The encoding expected for query string arguments after % HEX HEX decoding).
-    If a query string is provided that cannot be decoded with this encoding,
-    404 is raised (since technically it's a different URI). If you want
-    arbitrary encodings to not error, set this to 'Latin-1'; you can then
-    encode back to bytes and re-decode to whatever encoding you like later.
-    """
-    
-    protocol = (1, 1)
-    protocol__doc = """The HTTP protocol version corresponding to the set
-        of features which should be allowed in the response. If BOTH
-        the client's request message AND the server's level of HTTP
-        compliance is HTTP/1.1, this attribute will be the tuple (1, 1).
-        If either is 1.0, this attribute will be the tuple (1, 0).
-        Lower HTTP protocol versions are not explicitly supported."""
-    
-    params = {}
-    params__doc = """
-    A dict which combines query string (GET) and request entity (POST)
-    variables. This is populated in two stages: GET params are added
-    before the 'on_start_resource' hook, and POST params are added
-    between the 'before_request_body' and 'before_handler' hooks."""
-    
-    # Message attributes
-    header_list = []
-    header_list__doc = """
-    A list of the HTTP request headers as (name, value) tuples.
-    In general, you should use request.headers (a dict) instead."""
-    
-    headers = httputil.HeaderMap()
-    headers__doc = """
-    A dict-like object containing the request headers. Keys are header
-    names (in Title-Case format); however, you may get and set them in
-    a case-insensitive manner. That is, headers['Content-Type'] and
-    headers['content-type'] refer to the same value. Values are header
-    values (decoded according to RFC 2047 if necessary). See also:
-    httputil.HeaderMap, httputil.HeaderElement."""
-    
-    cookie = SimpleCookie()
-    cookie__doc = """See help(Cookie)."""
-    
-    body = None
-    body__doc = """See help(cherrypy.request.body)"""
-    
-    rfile = None
-    rfile__doc = """
-    If the request included an entity (body), it will be available
-    as a stream in this attribute. However, the rfile will normally
-    be read for you between the 'before_request_body' hook and the
-    'before_handler' hook, and the resulting string is placed into
-    either request.params or the request.body attribute.
-    
-    You may disable the automatic consumption of the rfile by setting
-    request.process_request_body to False, either in config for the desired
-    path, or in an 'on_start_resource' or 'before_request_body' hook.
-    
-    WARNING: In almost every case, you should not attempt to read from the
-    rfile stream after CherryPy's automatic mechanism has read it. If you
-    turn off the automatic parsing of rfile, you should read exactly the
-    number of bytes specified in request.headers['Content-Length'].
-    Ignoring either of these warnings may result in a hung request thread
-    or in corruption of the next (pipelined) request.
-    """
-    
-    process_request_body = True
-    process_request_body__doc = """
-    If True, the rfile (if any) is automatically read and parsed,
-    and the result placed into request.params or request.body."""
-    
-    methods_with_bodies = ("POST", "PUT")
-    methods_with_bodies__doc = """
-    A sequence of HTTP methods for which CherryPy will automatically
-    attempt to read a body from the rfile."""
-    
-    body = None
-    body__doc = """
-    If the request Content-Type is 'application/x-www-form-urlencoded'
-    or multipart, this will be None. Otherwise, this will contain the
-    request entity body as an open file object (which you can .read());
-    this value is set between the 'before_request_body' and 'before_handler'
-    hooks (assuming that process_request_body is True)."""
-    
-    body_params = None
-    body_params__doc = """
-    If the request Content-Type is 'application/x-www-form-urlencoded' or
-    multipart, this will be a dict of the params pulled from the entity
-    body; that is, it will be the portion of request.params that come
-    from the message body (sometimes called "POST params", although they
-    can be sent with various HTTP method verbs). This value is set between
-    the 'before_request_body' and 'before_handler' hooks (assuming that
-    process_request_body is True)."""
-    
-    # Dispatch attributes
-    dispatch = cherrypy.dispatch.Dispatcher()
-    dispatch__doc = """
-    The object which looks up the 'page handler' callable and collects
-    config for the current request based on the path_info, other
-    request attributes, and the application architecture. The core
-    calls the dispatcher as early as possible, passing it a 'path_info'
-    argument.
-    
-    The default dispatcher discovers the page handler by matching path_info
-    to a hierarchical arrangement of objects, starting at request.app.root.
-    See help(cherrypy.dispatch) for more information."""
-    
-    script_name = ""
-    script_name__doc = """
-    The 'mount point' of the application which is handling this request.
-    
-    This attribute MUST NOT end in a slash. If the script_name refers to
-    the root of the URI, it MUST be an empty string (not "/").
-    """
-    
-    path_info = "/"
-    path_info__doc = """
-    The 'relative path' portion of the Request-URI. This is relative
-    to the script_name ('mount point') of the application which is
-    handling this request."""
-
-    login = None
-    login__doc = """
-    When authentication is used during the request processing this is
-    set to 'False' if it failed and to the 'username' value if it succeeded.
-    The default 'None' implies that no authentication happened."""
-    
-    # Note that cherrypy.url uses "if request.app:" to determine whether
-    # the call is during a real HTTP request or not. So leave this None.
-    app = None
-    app__doc = \
-        """The cherrypy.Application object which is handling this request."""
-    
-    handler = None
-    handler__doc = """
-    The function, method, or other callable which CherryPy will call to
-    produce the response. The discovery of the handler and the arguments
-    it will receive are determined by the request.dispatch object.
-    By default, the handler is discovered by walking a tree of objects
-    starting at request.app.root, and is then passed all HTTP params
-    (from the query string and POST body) as keyword arguments."""
-    
-    toolmaps = {}
-    toolmaps__doc = """
-    A nested dict of all Toolboxes and Tools in effect for this request,
-    of the form: {Toolbox.namespace: {Tool.name: config dict}}."""
-    
-    config = None
-    config__doc = """
-    A flat dict of all configuration entries which apply to the
-    current request. These entries are collected from global config,
-    application config (based on request.path_info), and from handler
-    config (exactly how is governed by the request.dispatch object in
-    effect for this request; by default, handler config can be attached
-    anywhere in the tree between request.app.root and the final handler,
-    and inherits downward)."""
-    
-    is_index = None
-    is_index__doc = """
-    This will be True if the current request is mapped to an 'index'
-    resource handler (also, a 'default' handler if path_info ends with
-    a slash). The value may be used to automatically redirect the
-    user-agent to a 'more canonical' URL which either adds or removes
-    the trailing slash. See cherrypy.tools.trailing_slash."""
-    
-    hooks = HookMap(hookpoints)
-    hooks__doc = """
-    A HookMap (dict-like object) of the form: {hookpoint: [hook, ...]}.
-    Each key is a str naming the hook point, and each value is a list
-    of hooks which will be called at that hook point during this request.
-    The list of hooks is generally populated as early as possible (mostly
-    from Tools specified in config), but may be extended at any time.
-    See also: _cprequest.Hook, _cprequest.HookMap, and cherrypy.tools."""
-    
-    error_response = cherrypy.HTTPError(500).set_response
-    error_response__doc = """
-    The no-arg callable which will handle unexpected, untrapped errors
-    during request processing. This is not used for expected exceptions
-    (like NotFound, HTTPError, or HTTPRedirect) which are raised in
-    response to expected conditions (those should be customized either
-    via request.error_page or by overriding HTTPError.set_response).
-    By default, error_response uses HTTPError(500) to return a generic
-    error response to the user-agent."""
-    
-    error_page = {}
-    error_page__doc = """
-    A dict of {error code: response filename or callable} pairs.
-    
-    The error code must be an int representing a given HTTP error code,
-    or the string 'default', which will be used if no matching entry
-    is found for a given numeric code.
-    
-    If a filename is provided, the file should contain a Python string-
-    formatting template, and can expect by default to receive format 
-    values with the mapping keys %(status)s, %(message)s, %(traceback)s,
-    and %(version)s. The set of format mappings can be extended by
-    overriding HTTPError.set_response.
-    
-    If a callable is provided, it will be called by default with keyword
-    arguments 'status', 'message', 'traceback', and 'version', as for a
-    string-formatting template. The callable must return a string or iterable of
-    strings which will be set to response.body. It may also override headers or
-    perform any other processing.
-    
-    If no entry is given for an error code, and no 'default' entry exists,
-    a default template will be used.
-    """
-    
-    show_tracebacks = True
-    show_tracebacks__doc = """
-    If True, unexpected errors encountered during request processing will
-    include a traceback in the response body."""
-
-    show_mismatched_params = True
-    show_mismatched_params__doc = """
-    If True, mismatched parameters encountered during PageHandler invocation
-    processing will be included in the response body."""
-    
-    throws = (KeyboardInterrupt, SystemExit, cherrypy.InternalRedirect)
-    throws__doc = \
-        """The sequence of exceptions which Request.run does not trap."""
-    
-    throw_errors = False
-    throw_errors__doc = """
-    If True, Request.run will not trap any errors (except HTTPRedirect and
-    HTTPError, which are more properly called 'exceptions', not errors)."""
-    
-    closed = False
-    closed__doc = """
-    True once the close method has been called, False otherwise."""
-    
-    stage = None
-    stage__doc = """
-    A string containing the stage reached in the request-handling process.
-    This is useful when debugging a live server with hung requests."""
-    
-    namespaces = _cpconfig.NamespaceSet(
-        **{"hooks": hooks_namespace,
-           "request": request_namespace,
-           "response": response_namespace,
-           "error_page": error_page_namespace,
-           "tools": cherrypy.tools,
-           })
-    
-    def __init__(self, local_host, remote_host, scheme="http",
-                 server_protocol="HTTP/1.1"):
-        """Populate a new Request object.
-        
-        local_host should be an httputil.Host object with the server info.
-        remote_host should be an httputil.Host object with the client info.
-        scheme should be a string, either "http" or "https".
-        """
-        self.local = local_host
-        self.remote = remote_host
-        self.scheme = scheme
-        self.server_protocol = server_protocol
-        
-        self.closed = False
-        
-        # Put a *copy* of the class error_page into self.
-        self.error_page = self.error_page.copy()
-        
-        # Put a *copy* of the class namespaces into self.
-        self.namespaces = self.namespaces.copy()
-        
-        self.stage = None
-    
-    def close(self):
-        """Run cleanup code. (Core)"""
-        if not self.closed:
-            self.closed = True
-            self.stage = 'on_end_request'
-            self.hooks.run('on_end_request')
-            self.stage = 'close'
-    
-    def run(self, method, path, query_string, req_protocol, headers, rfile):
-        """Process the Request. (Core)
-        
-        method, path, query_string, and req_protocol should be pulled directly
-            from the Request-Line (e.g. "GET /path?key=val HTTP/1.0").
-        path should be %XX-unquoted, but query_string should not be.
-            They both MUST be byte strings, not unicode strings.
-        headers should be a list of (name, value) tuples.
-        rfile should be a file-like object containing the HTTP request entity.
-        
-        When run() is done, the returned object should have 3 attributes:
-          status, e.g. "200 OK"
-          header_list, a list of (name, value) tuples
-          body, an iterable yielding strings
-        
-        Consumer code (HTTP servers) should then access these response
-        attributes to build the outbound stream.
-        
-        """
-        response = cherrypy.serving.response
-        self.stage = 'run'
-        try:
-            self.error_response = cherrypy.HTTPError(500).set_response
-            
-            self.method = method
-            path = path or "/"
-            self.query_string = query_string or ''
-            self.params = {}
-            
-            # Compare request and server HTTP protocol versions, in case our
-            # server does not support the requested protocol. Limit our output
-            # to min(req, server). We want the following output:
-            #     request    server     actual written   supported response
-            #     protocol   protocol  response protocol    feature set
-            # a     1.0        1.0           1.0                1.0
-            # b     1.0        1.1           1.1                1.0
-            # c     1.1        1.0           1.0                1.0
-            # d     1.1        1.1           1.1                1.1
-            # Notice that, in (b), the response will be "HTTP/1.1" even though
-            # the client only understands 1.0. RFC 2616 10.5.6 says we should
-            # only return 505 if the _major_ version is different.
-            rp = int(req_protocol[5]), int(req_protocol[7])
-            sp = int(self.server_protocol[5]), int(self.server_protocol[7])
-            self.protocol = min(rp, sp)
-            response.headers.protocol = self.protocol
-            
-            # Rebuild first line of the request (e.g. "GET /path HTTP/1.0").
-            url = path
-            if query_string:
-                url += '?' + query_string
-            self.request_line = '%s %s %s' % (method, url, req_protocol)
-            
-            self.header_list = list(headers)
-            self.headers = httputil.HeaderMap()
-            
-            self.rfile = rfile
-            self.body = None
-            
-            self.cookie = SimpleCookie()
-            self.handler = None
-            
-            # path_info should be the path from the
-            # app root (script_name) to the handler.
-            self.script_name = self.app.script_name
-            self.path_info = pi = path[len(self.script_name):]
-            
-            self.stage = 'respond'
-            self.respond(pi)
-            
-        except self.throws:
-            raise
-        except:
-            if self.throw_errors:
-                raise
-            else:
-                # Failure in setup, error handler or finalize. Bypass them.
-                # Can't use handle_error because we may not have hooks yet.
-                cherrypy.log(traceback=True, severity=40)
-                if self.show_tracebacks:
-                    body = format_exc()
-                else:
-                    body = ""
-                r = bare_error(body)
-                response.output_status, response.header_list, response.body = r
-        
-        if self.method == "HEAD":
-            # HEAD requests MUST NOT return a message-body in the response.
-            response.body = []
-        
-        try:
-            cherrypy.log.access()
-        except:
-            cherrypy.log.error(traceback=True)
-        
-        if response.timed_out:
-            raise cherrypy.TimeoutError()
-        
-        return response
-    
-    # Uncomment for stage debugging
-    # stage = property(lambda self: self._stage, lambda self, v: print(v))
-    
-    def respond(self, path_info):
-        """Generate a response for the resource at self.path_info. (Core)"""
-        response = cherrypy.serving.response
-        try:
-            try:
-                try:
-                    if self.app is None:
-                        raise cherrypy.NotFound()
-                    
-                    # Get the 'Host' header, so we can HTTPRedirect properly.
-                    self.stage = 'process_headers'
-                    self.process_headers()
-                    
-                    # Make a copy of the class hooks
-                    self.hooks = self.__class__.hooks.copy()
-                    self.toolmaps = {}
-                    
-                    self.stage = 'get_resource'
-                    self.get_resource(path_info)
-                    
-                    self.body = _cpreqbody.RequestBody(
-                        self.rfile, self.headers, request_params=self.params)
-                    
-                    self.namespaces(self.config)
-                    
-                    self.stage = 'on_start_resource'
-                    self.hooks.run('on_start_resource')
-                    
-                    # Parse the querystring
-                    self.stage = 'process_query_string'
-                    self.process_query_string()
-                    
-                    # Process the body
-                    if self.process_request_body:
-                        if self.method not in self.methods_with_bodies:
-                            self.process_request_body = False
-                    self.stage = 'before_request_body'
-                    self.hooks.run('before_request_body')
-                    if self.process_request_body:
-                        self.body.process()
-                    
-                    # Run the handler
-                    self.stage = 'before_handler'
-                    self.hooks.run('before_handler')
-                    if self.handler:
-                        self.stage = 'handler'
-                        response.body = self.handler()
-                    
-                    # Finalize
-                    self.stage = 'before_finalize'
-                    self.hooks.run('before_finalize')
-                    response.finalize()
-                except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst:
-                    inst.set_response()
-                    self.stage = 'before_finalize (HTTPError)'
-                    self.hooks.run('before_finalize')
-                    response.finalize()
-            finally:
-                self.stage = 'on_end_resource'
-                self.hooks.run('on_end_resource')
-        except self.throws:
-            raise
-        except:
-            if self.throw_errors:
-                raise
-            self.handle_error()
-    
-    def process_query_string(self):
-        """Parse the query string into Python structures. (Core)"""
-        try:
-            p = httputil.parse_query_string(
-                self.query_string, encoding=self.query_string_encoding)
-        except UnicodeDecodeError:
-            raise cherrypy.HTTPError(
-                404, "The given query string could not be processed. Query "
-                "strings for this resource must be encoded with %r." %
-                self.query_string_encoding)
-        
-        # Python 2 only: keyword arguments must be byte strings (type 'str').
-        for key, value in p.items():
-            if isinstance(key, unicode):
-                del p[key]
-                p[key.encode(self.query_string_encoding)] = value
-        self.params.update(p)
-    
-    def process_headers(self):
-        """Parse HTTP header data into Python structures. (Core)"""
-        # Process the headers into self.headers
-        headers = self.headers
-        for name, value in self.header_list:
-            # Call title() now (and use dict.__method__(headers))
-            # so title doesn't have to be called twice.
-            name = name.title()
-            value = value.strip()
-            
-            # Warning: if there is more than one header entry for cookies (AFAIK,
-            # only Konqueror does that), only the last one will remain in headers
-            # (but they will be correctly stored in request.cookie).
-            if "=?" in value:
-                dict.__setitem__(headers, name, httputil.decode_TEXT(value))
-            else:
-                dict.__setitem__(headers, name, value)
-            
-            # Handle cookies differently because on Konqueror, multiple
-            # cookies come on different lines with the same key
-            if name == 'Cookie':
-                try:
-                    self.cookie.load(value)
-                except CookieError:
-                    msg = "Illegal cookie name %s" % value.split('=')[0]
-                    raise cherrypy.HTTPError(400, msg)
-        
-        if not dict.__contains__(headers, 'Host'):
-            # All Internet-based HTTP/1.1 servers MUST respond with a 400
-            # (Bad Request) status code to any HTTP/1.1 request message
-            # which lacks a Host header field.
-            if self.protocol >= (1, 1):
-                msg = "HTTP/1.1 requires a 'Host' request header."
-                raise cherrypy.HTTPError(400, msg)
-        host = dict.get(headers, 'Host')
-        if not host:
-            host = self.local.name or self.local.ip
-        self.base = "%s://%s" % (self.scheme, host)
-    
-    def get_resource(self, path):
-        """Call a dispatcher (which sets self.handler and .config). (Core)"""
-        # First, see if there is a custom dispatch at this URI. Custom
-        # dispatchers can only be specified in app.config, not in _cp_config
-        # (since custom dispatchers may not even have an app.root).
-        dispatch = self.app.find_config(path, "request.dispatch", self.dispatch)
-        
-        # dispatch() should set self.handler and self.config
-        dispatch(path)
-    
-    def handle_error(self):
-        """Handle the last unanticipated exception. (Core)"""
-        try:
-            self.hooks.run("before_error_response")
-            if self.error_response:
-                self.error_response()
-            self.hooks.run("after_error_response")
-            cherrypy.serving.response.finalize()
-        except cherrypy.HTTPRedirect, inst:
-            inst.set_response()
-            cherrypy.serving.response.finalize()
-    
-    # ------------------------- Properties ------------------------- #
-    
-    def _get_body_params(self):
-        warnings.warn(
-                "body_params is deprecated in CherryPy 3.2, will be removed in "
-                "CherryPy 3.3.",
-                DeprecationWarning
-            )
-        return self.body.params
-    body_params = property(_get_body_params,
-                      doc= """
-    If the request Content-Type is 'application/x-www-form-urlencoded' or
-    multipart, this will be a dict of the params pulled from the entity
-    body; that is, it will be the portion of request.params that come
-    from the message body (sometimes called "POST params", although they
-    can be sent with various HTTP method verbs). This value is set between
-    the 'before_request_body' and 'before_handler' hooks (assuming that
-    process_request_body is True).
-    
-    Deprecated in 3.2, will be removed for 3.3""")
-
-
-class ResponseBody(object):
-    """The body of the HTTP response (the response entity)."""
-    
-    def __get__(self, obj, objclass=None):
-        if obj is None:
-            # When calling on the class instead of an instance...
-            return self
-        else:
-            return obj._body
-    
-    def __set__(self, obj, value):
-        # Convert the given value to an iterable object.
-        if isinstance(value, basestring):
-            # strings get wrapped in a list because iterating over a single
-            # item list is much faster than iterating over every character
-            # in a long string.
-            if value:
-                value = [value]
-            else:
-                # [''] doesn't evaluate to False, so replace it with [].
-                value = []
-        elif isinstance(value, types.FileType):
-            value = file_generator(value)
-        elif value is None:
-            value = []
-        obj._body = value
-
-
-class Response(object):
-    """An HTTP Response, including status, headers, and body.
-    
-    Application developers should use Response.headers (a dict) to
-    set or modify HTTP response headers. When the response is finalized,
-    Response.headers is transformed into Response.header_list as
-    (key, value) tuples.
-    """
-    
-    __metaclass__ = cherrypy._AttributeDocstrings
-    
-    # Class attributes for dev-time introspection.
-    status = ""
-    status__doc = """The HTTP Status-Code and Reason-Phrase."""
-    
-    header_list = []
-    header_list__doc = """
-    A list of the HTTP response headers as (name, value) tuples.
-    In general, you should use response.headers (a dict) instead."""
-    
-    headers = httputil.HeaderMap()
-    headers__doc = """
-    A dict-like object containing the response headers. Keys are header
-    names (in Title-Case format); however, you may get and set them in
-    a case-insensitive manner. That is, headers['Content-Type'] and
-    headers['content-type'] refer to the same value. Values are header
-    values (decoded according to RFC 2047 if necessary). See also:
-    httputil.HeaderMap, httputil.HeaderElement."""
-    
-    cookie = SimpleCookie()
-    cookie__doc = """See help(Cookie)."""
-    
-    body = ResponseBody()
-    body__doc = """The body (entity) of the HTTP response."""
-    
-    time = None
-    time__doc = """The value of time.time() when created. Use in HTTP dates."""
-    
-    timeout = 300
-    timeout__doc = """Seconds after which the response will be aborted."""
-    
-    timed_out = False
-    timed_out__doc = """
-    Flag to indicate the response should be aborted, because it has
-    exceeded its timeout."""
-    
-    stream = False
-    stream__doc = """If False, buffer the response body."""
-    
-    def __init__(self):
-        self.status = None
-        self.header_list = None
-        self._body = []
-        self.time = time.time()
-        
-        self.headers = httputil.HeaderMap()
-        # Since we know all our keys are titled strings, we can
-        # bypass HeaderMap.update and get a big speed boost.
-        dict.update(self.headers, {
-            "Content-Type": 'text/html',
-            "Server": "CherryPy/" + cherrypy.__version__,
-            "Date": httputil.HTTPDate(self.time),
-        })
-        self.cookie = SimpleCookie()
-    
-    def collapse_body(self):
-        """Collapse self.body to a single string; replace it and return it."""
-        if isinstance(self.body, basestring):
-            return self.body
-
-        newbody = ''.join([chunk for chunk in self.body])
-        self.body = newbody
-        return newbody
-    
-    def finalize(self):
-        """Transform headers (and cookies) into self.header_list. (Core)"""
-        try:
-            code, reason, _ = httputil.valid_status(self.status)
-        except ValueError, x:
-            raise cherrypy.HTTPError(500, x.args[0])
-        
-        headers = self.headers
-        
-        self.output_status = str(code) + " " + headers.encode(reason)
-        
-        if self.stream:
-            # The upshot: wsgiserver will chunk the response if
-            # you pop Content-Length (or set it explicitly to None).
-            # Note that lib.static sets C-L to the file's st_size.
-            if dict.get(headers, 'Content-Length') is None:
-                dict.pop(headers, 'Content-Length', None)
-        elif code < 200 or code in (204, 205, 304):
-            # "All 1xx (informational), 204 (no content),
-            # and 304 (not modified) responses MUST NOT
-            # include a message-body."
-            dict.pop(headers, 'Content-Length', None)
-            self.body = ""
-        else:
-            # Responses which are not streamed should have a Content-Length,
-            # but allow user code to set Content-Length if desired.
-            if dict.get(headers, 'Content-Length') is None:
-                content = self.collapse_body()
-                dict.__setitem__(headers, 'Content-Length', len(content))
-        
-        # Transform our header dict into a list of tuples.
-        self.header_list = h = headers.output()
-        
-        cookie = self.cookie.output()
-        if cookie:
-            for line in cookie.split("\n"):
-                if line.endswith("\r"):
-                    # Python 2.4 emits cookies joined by LF but 2.5+ by CRLF.
-                    line = line[:-1]
-                name, value = line.split(": ", 1)
-                if isinstance(name, unicode):
-                    name = name.encode("ISO-8859-1")
-                if isinstance(value, unicode):
-                    value = headers.encode(value)
-                h.append((name, value))
-    
-    def check_timeout(self):
-        """If now > self.time + self.timeout, set self.timed_out.
-        
-        This purposefully sets a flag, rather than raising an error,
-        so that a monitor thread can interrupt the Response thread.
-        """
-        if time.time() > self.time + self.timeout:
-            self.timed_out = True
-
-
-
--- a/bundled/cherrypy/cherrypy/_cpserver.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-"""Manage HTTP servers with CherryPy."""
-
-import warnings
-
-import cherrypy
-from cherrypy.lib import attributes
-
-# We import * because we want to export check_port
-# et al as attributes of this module.
-from cherrypy.process.servers import *
-
-
-class Server(ServerAdapter):
-    """An adapter for an HTTP server.
-    
-    You can set attributes (like socket_host and socket_port)
-    on *this* object (which is probably cherrypy.server), and call
-    quickstart. For example:
-    
-        cherrypy.server.socket_port = 80
-        cherrypy.quickstart()
-    """
-    
-    socket_port = 8080
-    
-    _socket_host = '127.0.0.1'
-    def _get_socket_host(self):
-        return self._socket_host
-    def _set_socket_host(self, value):
-        if value == '':
-            raise ValueError("The empty string ('') is not an allowed value. "
-                             "Use '0.0.0.0' instead to listen on all active "
-                             "interfaces (INADDR_ANY).")
-        self._socket_host = value
-    socket_host = property(_get_socket_host, _set_socket_host,
-        doc="""The hostname or IP address on which to listen for connections.
-        
-        Host values may be any IPv4 or IPv6 address, or any valid hostname.
-        The string 'localhost' is a synonym for '127.0.0.1' (or '::1', if
-        your hosts file prefers IPv6). The string '0.0.0.0' is a special
-        IPv4 entry meaning "any active interface" (INADDR_ANY), and '::'
-        is the similar IN6ADDR_ANY for IPv6. The empty string or None are
-        not allowed.""")
-    
-    socket_file = None
-    socket_queue_size = 5
-    socket_timeout = 10
-    shutdown_timeout = 5
-    protocol_version = 'HTTP/1.1'
-    reverse_dns = False
-    thread_pool = 10
-    thread_pool_max = -1
-    max_request_header_size = 500 * 1024
-    max_request_body_size = 100 * 1024 * 1024
-    instance = None
-    ssl_context = None
-    ssl_certificate = None
-    ssl_certificate_chain = None
-    ssl_private_key = None
-    ssl_module = 'pyopenssl'
-    nodelay = True
-    wsgi_version = (1, 1)
-    
-    def __init__(self):
-        self.bus = cherrypy.engine
-        self.httpserver = None
-        self.interrupt = None
-        self.running = False
-    
-    def httpserver_from_self(self, httpserver=None):
-        """Return a (httpserver, bind_addr) pair based on self attributes."""
-        if httpserver is None:
-            httpserver = self.instance
-        if httpserver is None:
-            from cherrypy import _cpwsgi_server
-            httpserver = _cpwsgi_server.CPWSGIServer(self)
-        if isinstance(httpserver, basestring):
-            # Is anyone using this? Can I add an arg?
-            httpserver = attributes(httpserver)(self)
-        return httpserver, self.bind_addr
-    
-    def start(self):
-        """Start the HTTP server."""
-        if not self.httpserver:
-            self.httpserver, self.bind_addr = self.httpserver_from_self()
-        ServerAdapter.start(self)
-    start.priority = 75
-    
-    def _get_bind_addr(self):
-        if self.socket_file:
-            return self.socket_file
-        if self.socket_host is None and self.socket_port is None:
-            return None
-        return (self.socket_host, self.socket_port)
-    def _set_bind_addr(self, value):
-        if value is None:
-            self.socket_file = None
-            self.socket_host = None
-            self.socket_port = None
-        elif isinstance(value, basestring):
-            self.socket_file = value
-            self.socket_host = None
-            self.socket_port = None
-        else:
-            try:
-                self.socket_host, self.socket_port = value
-                self.socket_file = None
-            except ValueError:
-                raise ValueError("bind_addr must be a (host, port) tuple "
-                                 "(for TCP sockets) or a string (for Unix "
-                                 "domain sockets), not %r" % value)
-    bind_addr = property(_get_bind_addr, _set_bind_addr)
-    
-    def base(self):
-        """Return the base (scheme://host[:port] or sock file) for this server."""
-        if self.socket_file:
-            return self.socket_file
-        
-        host = self.socket_host
-        if host in ('0.0.0.0', '::'):
-            # 0.0.0.0 is INADDR_ANY and :: is IN6ADDR_ANY.
-            # Look up the host name, which should be the
-            # safest thing to spit out in a URL.
-            import socket
-            host = socket.gethostname()
-        
-        port = self.socket_port
-        
-        if self.ssl_certificate:
-            scheme = "https"
-            if port != 443:
-                host += ":%s" % port
-        else:
-            scheme = "http"
-            if port != 80:
-                host += ":%s" % port
-        
-        return "%s://%s" % (scheme, host)
-
--- a/bundled/cherrypy/cherrypy/_cpthreadinglocal.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,239 +0,0 @@
-# This is a backport of Python-2.4's threading.local() implementation
-
-"""Thread-local objects
-
-(Note that this module provides a Python version of thread
- threading.local class.  Depending on the version of Python you're
- using, there may be a faster one available.  You should always import
- the local class from threading.)
-
-Thread-local objects support the management of thread-local data.
-If you have data that you want to be local to a thread, simply create
-a thread-local object and use its attributes:
-
-  >>> mydata = local()
-  >>> mydata.number = 42
-  >>> mydata.number
-  42
-
-You can also access the local-object's dictionary:
-
-  >>> mydata.__dict__
-  {'number': 42}
-  >>> mydata.__dict__.setdefault('widgets', [])
-  []
-  >>> mydata.widgets
-  []
-
-What's important about thread-local objects is that their data are
-local to a thread. If we access the data in a different thread:
-
-  >>> log = []
-  >>> def f():
-  ...     items = mydata.__dict__.items()
-  ...     items.sort()
-  ...     log.append(items)
-  ...     mydata.number = 11
-  ...     log.append(mydata.number)
-
-  >>> import threading
-  >>> thread = threading.Thread(target=f)
-  >>> thread.start()
-  >>> thread.join()
-  >>> log
-  [[], 11]
-
-we get different data.  Furthermore, changes made in the other thread
-don't affect data seen in this thread:
-
-  >>> mydata.number
-  42
-
-Of course, values you get from a local object, including a __dict__
-attribute, are for whatever thread was current at the time the
-attribute was read.  For that reason, you generally don't want to save
-these values across threads, as they apply only to the thread they
-came from.
-
-You can create custom local objects by subclassing the local class:
-
-  >>> class MyLocal(local):
-  ...     number = 2
-  ...     initialized = False
-  ...     def __init__(self, **kw):
-  ...         if self.initialized:
-  ...             raise SystemError('__init__ called too many times')
-  ...         self.initialized = True
-  ...         self.__dict__.update(kw)
-  ...     def squared(self):
-  ...         return self.number ** 2
-
-This can be useful to support default values, methods and
-initialization.  Note that if you define an __init__ method, it will be
-called each time the local object is used in a separate thread.  This
-is necessary to initialize each thread's dictionary.
-
-Now if we create a local object:
-
-  >>> mydata = MyLocal(color='red')
-
-Now we have a default number:
-
-  >>> mydata.number
-  2
-
-an initial color:
-
-  >>> mydata.color
-  'red'
-  >>> del mydata.color
-
-And a method that operates on the data:
-
-  >>> mydata.squared()
-  4
-
-As before, we can access the data in a separate thread:
-
-  >>> log = []
-  >>> thread = threading.Thread(target=f)
-  >>> thread.start()
-  >>> thread.join()
-  >>> log
-  [[('color', 'red'), ('initialized', True)], 11]
-
-without affecting this thread's data:
-
-  >>> mydata.number
-  2
-  >>> mydata.color
-  Traceback (most recent call last):
-  ...
-  AttributeError: 'MyLocal' object has no attribute 'color'
-
-Note that subclasses can define slots, but they are not thread
-local. They are shared across threads:
-
-  >>> class MyLocal(local):
-  ...     __slots__ = 'number'
-
-  >>> mydata = MyLocal()
-  >>> mydata.number = 42
-  >>> mydata.color = 'red'
-
-So, the separate thread:
-
-  >>> thread = threading.Thread(target=f)
-  >>> thread.start()
-  >>> thread.join()
-
-affects what we see:
-
-  >>> mydata.number
-  11
-
->>> del mydata
-"""
-
-# Threading import is at end
-
-class _localbase(object):
-    __slots__ = '_local__key', '_local__args', '_local__lock'
-
-    def __new__(cls, *args, **kw):
-        self = object.__new__(cls)
-        key = 'thread.local.' + str(id(self))
-        object.__setattr__(self, '_local__key', key)
-        object.__setattr__(self, '_local__args', (args, kw))
-        object.__setattr__(self, '_local__lock', RLock())
-
-        if args or kw and (cls.__init__ is object.__init__):
-            raise TypeError("Initialization arguments are not supported")
-
-        # We need to create the thread dict in anticipation of
-        # __init__ being called, to make sure we don't call it
-        # again ourselves.
-        dict = object.__getattribute__(self, '__dict__')
-        currentThread().__dict__[key] = dict
-
-        return self
-
-def _patch(self):
-    key = object.__getattribute__(self, '_local__key')
-    d = currentThread().__dict__.get(key)
-    if d is None:
-        d = {}
-        currentThread().__dict__[key] = d
-        object.__setattr__(self, '__dict__', d)
-
-        # we have a new instance dict, so call out __init__ if we have
-        # one
-        cls = type(self)
-        if cls.__init__ is not object.__init__:
-            args, kw = object.__getattribute__(self, '_local__args')
-            cls.__init__(self, *args, **kw)
-    else:
-        object.__setattr__(self, '__dict__', d)
-
-class local(_localbase):
-
-    def __getattribute__(self, name):
-        lock = object.__getattribute__(self, '_local__lock')
-        lock.acquire()
-        try:
-            _patch(self)
-            return object.__getattribute__(self, name)
-        finally:
-            lock.release()
-
-    def __setattr__(self, name, value):
-        lock = object.__getattribute__(self, '_local__lock')
-        lock.acquire()
-        try:
-            _patch(self)
-            return object.__setattr__(self, name, value)
-        finally:
-            lock.release()
-
-    def __delattr__(self, name):
-        lock = object.__getattribute__(self, '_local__lock')
-        lock.acquire()
-        try:
-            _patch(self)
-            return object.__delattr__(self, name)
-        finally:
-            lock.release()
-
-
-    def __del__():
-        threading_enumerate = enumerate
-        __getattribute__ = object.__getattribute__
-
-        def __del__(self):
-            key = __getattribute__(self, '_local__key')
-
-            try:
-                threads = list(threading_enumerate())
-            except:
-                # if enumerate fails, as it seems to do during
-                # shutdown, we'll skip cleanup under the assumption
-                # that there is nothing to clean up
-                return
-
-            for thread in threads:
-                try:
-                    __dict__ = thread.__dict__
-                except AttributeError:
-                    # Thread is dying, rest in peace
-                    continue
-
-                if key in __dict__:
-                    try:
-                        del __dict__[key]
-                    except KeyError:
-                        pass # didn't have anything in this thread
-
-        return __del__
-    __del__ = __del__()
-
-from threading import currentThread, enumerate, RLock
--- a/bundled/cherrypy/cherrypy/_cptools.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,498 +0,0 @@
-"""CherryPy tools. A "tool" is any helper, adapted to CP.
-
-Tools are usually designed to be used in a variety of ways (although some
-may only offer one if they choose):
-    
-    Library calls:
-        All tools are callables that can be used wherever needed.
-        The arguments are straightforward and should be detailed within the
-        docstring.
-    
-    Function decorators:
-        All tools, when called, may be used as decorators which configure
-        individual CherryPy page handlers (methods on the CherryPy tree).
-        That is, "@tools.anytool()" should "turn on" the tool via the
-        decorated function's _cp_config attribute.
-    
-    CherryPy config:
-        If a tool exposes a "_setup" callable, it will be called
-        once per Request (if the feature is "turned on" via config).
-
-Tools may be implemented as any object with a namespace. The builtins
-are generally either modules or instances of the tools.Tool class.
-"""
-
-import cherrypy
-import warnings
-
-
-def _getargs(func):
-    """Return the names of all static arguments to the given function."""
-    # Use this instead of importing inspect for less mem overhead.
-    import types
-    if isinstance(func, types.MethodType):
-        func = func.im_func
-    co = func.func_code
-    return co.co_varnames[:co.co_argcount]
-
-
-_attr_error = ("CherryPy Tools cannot be turned on directly. Instead, turn them "
-               "on via config, or use them as decorators on your page handlers.")
-
-class Tool(object):
-    """A registered function for use with CherryPy request-processing hooks.
-    
-    help(tool.callable) should give you more information about this Tool.
-    """
-    
-    namespace = "tools"
-    
-    def __init__(self, point, callable, name=None, priority=50):
-        self._point = point
-        self.callable = callable
-        self._name = name
-        self._priority = priority
-        self.__doc__ = self.callable.__doc__
-        self._setargs()
-    
-    def _get_on(self):
-        raise AttributeError(_attr_error)
-    def _set_on(self, value):
-        raise AttributeError(_attr_error)
-    on = property(_get_on, _set_on)
-    
-    def _setargs(self):
-        """Copy func parameter names to obj attributes."""
-        try:
-            for arg in _getargs(self.callable):
-                setattr(self, arg, None)
-        except (TypeError, AttributeError):
-            if hasattr(self.callable, "__call__"):
-                for arg in _getargs(self.callable.__call__):
-                    setattr(self, arg, None)
-        # IronPython 1.0 raises NotImplementedError because
-        # inspect.getargspec tries to access Python bytecode
-        # in co_code attribute.
-        except NotImplementedError:
-            pass
-        # IronPython 1B1 may raise IndexError in some cases,
-        # but if we trap it here it doesn't prevent CP from working.
-        except IndexError:
-            pass
-    
-    def _merged_args(self, d=None):
-        """Return a dict of configuration entries for this Tool."""
-        if d:
-            conf = d.copy()
-        else:
-            conf = {}
-        
-        tm = cherrypy.serving.request.toolmaps[self.namespace]
-        if self._name in tm:
-            conf.update(tm[self._name])
-        
-        if "on" in conf:
-            del conf["on"]
-        
-        return conf
-    
-    def __call__(self, *args, **kwargs):
-        """Compile-time decorator (turn on the tool in config).
-        
-        For example:
-        
-            @tools.proxy()
-            def whats_my_base(self):
-                return cherrypy.request.base
-            whats_my_base.exposed = True
-        """
-        if args:
-            raise TypeError("The %r Tool does not accept positional "
-                            "arguments; you must use keyword arguments."
-                            % self._name)
-        def tool_decorator(f):
-            if not hasattr(f, "_cp_config"):
-                f._cp_config = {}
-            subspace = self.namespace + "." + self._name + "."
-            f._cp_config[subspace + "on"] = True
-            for k, v in kwargs.items():
-                f._cp_config[subspace + k] = v
-            return f
-        return tool_decorator
-    
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-        
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        conf = self._merged_args()
-        p = conf.pop("priority", None)
-        if p is None:
-            p = getattr(self.callable, "priority", self._priority)
-        cherrypy.serving.request.hooks.attach(self._point, self.callable,
-                                              priority=p, **conf)
-
-
-class HandlerTool(Tool):
-    """Tool which is called 'before main', that may skip normal handlers.
-    
-    If the tool successfully handles the request (by setting response.body),
-    if should return True. This will cause CherryPy to skip any 'normal' page
-    handler. If the tool did not handle the request, it should return False
-    to tell CherryPy to continue on and call the normal page handler. If the
-    tool is declared AS a page handler (see the 'handler' method), returning
-    False will raise NotFound.
-    """
-    
-    def __init__(self, callable, name=None):
-        Tool.__init__(self, 'before_handler', callable, name)
-    
-    def handler(self, *args, **kwargs):
-        """Use this tool as a CherryPy page handler.
-        
-        For example:
-            class Root:
-                nav = tools.staticdir.handler(section="/nav", dir="nav",
-                                              root=absDir)
-        """
-        def handle_func(*a, **kw):
-            handled = self.callable(*args, **self._merged_args(kwargs))
-            if not handled:
-                raise cherrypy.NotFound()
-            return cherrypy.serving.response.body
-        handle_func.exposed = True
-        return handle_func
-    
-    def _wrapper(self, **kwargs):
-        if self.callable(**kwargs):
-            cherrypy.serving.request.handler = None
-    
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-        
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        conf = self._merged_args()
-        p = conf.pop("priority", None)
-        if p is None:
-            p = getattr(self.callable, "priority", self._priority)
-        cherrypy.serving.request.hooks.attach(self._point, self._wrapper,
-                                              priority=p, **conf)
-
-
-class HandlerWrapperTool(Tool):
-    """Tool which wraps request.handler in a provided wrapper function.
-    
-    The 'newhandler' arg must be a handler wrapper function that takes a
-    'next_handler' argument, plus *args and **kwargs. Like all page handler
-    functions, it must return an iterable for use as cherrypy.response.body.
-    
-    For example, to allow your 'inner' page handlers to return dicts
-    which then get interpolated into a template:
-    
-        def interpolator(next_handler, *args, **kwargs):
-            filename = cherrypy.request.config.get('template')
-            cherrypy.response.template = env.get_template(filename)
-            response_dict = next_handler(*args, **kwargs)
-            return cherrypy.response.template.render(**response_dict)
-        cherrypy.tools.jinja = HandlerWrapperTool(interpolator)
-    """
-    
-    def __init__(self, newhandler, point='before_handler', name=None, priority=50):
-        self.newhandler = newhandler
-        self._point = point
-        self._name = name
-        self._priority = priority
-    
-    def callable(self, debug=False):
-        innerfunc = cherrypy.serving.request.handler
-        def wrap(*args, **kwargs):
-            return self.newhandler(innerfunc, *args, **kwargs)
-        cherrypy.serving.request.handler = wrap
-
-
-class ErrorTool(Tool):
-    """Tool which is used to replace the default request.error_response."""
-    
-    def __init__(self, callable, name=None):
-        Tool.__init__(self, None, callable, name)
-    
-    def _wrapper(self):
-        self.callable(**self._merged_args())
-    
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-        
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        cherrypy.serving.request.error_response = self._wrapper
-
-
-#                              Builtin tools                              #
-
-from cherrypy.lib import cptools, encoding, auth, static, jsontools
-from cherrypy.lib import sessions as _sessions, xmlrpc as _xmlrpc
-from cherrypy.lib import caching as _caching
-from cherrypy.lib import auth_basic, auth_digest
-
-
-class SessionTool(Tool):
-    """Session Tool for CherryPy.
-    
-    sessions.locking:
-        When 'implicit' (the default), the session will be locked for you,
-            just before running the page handler.
-        When 'early', the session will be locked before reading the request
-            body. This is off by default for safety reasons; for example,
-            a large upload would block the session, denying an AJAX
-            progress meter (see http://www.cherrypy.org/ticket/630).
-        When 'explicit' (or any other value), you need to call
-            cherrypy.session.acquire_lock() yourself before using
-            session data.
-    """
-    
-    def __init__(self):
-        # _sessions.init must be bound after headers are read
-        Tool.__init__(self, 'before_request_body', _sessions.init)
-    
-    def _lock_session(self):
-        cherrypy.serving.session.acquire_lock()
-    
-    def _setup(self):
-        """Hook this tool into cherrypy.request.
-        
-        The standard CherryPy request object will automatically call this
-        method when the tool is "turned on" in config.
-        """
-        hooks = cherrypy.serving.request.hooks
-        
-        conf = self._merged_args()
-        
-        p = conf.pop("priority", None)
-        if p is None:
-            p = getattr(self.callable, "priority", self._priority)
-        
-        hooks.attach(self._point, self.callable, priority=p, **conf)
-        
-        locking = conf.pop('locking', 'implicit')
-        if locking == 'implicit':
-            hooks.attach('before_handler', self._lock_session)
-        elif locking == 'early':
-            # Lock before the request body (but after _sessions.init runs!)
-            hooks.attach('before_request_body', self._lock_session,
-                         priority=60)
-        else:
-            # Don't lock
-            pass
-        
-        hooks.attach('before_finalize', _sessions.save)
-        hooks.attach('on_end_request', _sessions.close)
-        
-    def regenerate(self):
-        """Drop the current session and make a new one (with a new id)."""
-        sess = cherrypy.serving.session
-        sess.regenerate()
-        
-        # Grab cookie-relevant tool args
-        conf = dict([(k, v) for k, v in self._merged_args().items()
-                     if k in ('path', 'path_header', 'name', 'timeout',
-                              'domain', 'secure')])
-        _sessions.set_response_cookie(**conf)
-
-
-
-
-class XMLRPCController(object):
-    """A Controller (page handler collection) for XML-RPC.
-    
-    To use it, have your controllers subclass this base class (it will
-    turn on the tool for you).
-    
-    You can also supply the following optional config entries:
-        
-        tools.xmlrpc.encoding: 'utf-8'
-        tools.xmlrpc.allow_none: 0
-    
-    XML-RPC is a rather discontinuous layer over HTTP; dispatching to the
-    appropriate handler must first be performed according to the URL, and
-    then a second dispatch step must take place according to the RPC method
-    specified in the request body. It also allows a superfluous "/RPC2"
-    prefix in the URL, supplies its own handler args in the body, and
-    requires a 200 OK "Fault" response instead of 404 when the desired
-    method is not found.
-    
-    Therefore, XML-RPC cannot be implemented for CherryPy via a Tool alone.
-    This Controller acts as the dispatch target for the first half (based
-    on the URL); it then reads the RPC method from the request body and
-    does its own second dispatch step based on that method. It also reads
-    body params, and returns a Fault on error.
-    
-    The XMLRPCDispatcher strips any /RPC2 prefix; if you aren't using /RPC2
-    in your URL's, you can safely skip turning on the XMLRPCDispatcher.
-    Otherwise, you need to use declare it in config:
-        
-        request.dispatch: cherrypy.dispatch.XMLRPCDispatcher()
-    """
-    
-    # Note we're hard-coding this into the 'tools' namespace. We could do
-    # a huge amount of work to make it relocatable, but the only reason why
-    # would be if someone actually disabled the default_toolbox. Meh.
-    _cp_config = {'tools.xmlrpc.on': True}
-    
-    def default(self, *vpath, **params):
-        rpcparams, rpcmethod = _xmlrpc.process_body()
-        
-        subhandler = self
-        for attr in str(rpcmethod).split('.'):
-            subhandler = getattr(subhandler, attr, None)
-         
-        if subhandler and getattr(subhandler, "exposed", False):
-            body = subhandler(*(vpath + rpcparams), **params)
-        
-        else:
-            # http://www.cherrypy.org/ticket/533
-            # if a method is not found, an xmlrpclib.Fault should be returned
-            # raising an exception here will do that; see
-            # cherrypy.lib.xmlrpc.on_error
-            raise Exception('method "%s" is not supported' % attr)
-        
-        conf = cherrypy.serving.request.toolmaps['tools'].get("xmlrpc", {})
-        _xmlrpc.respond(body,
-                        conf.get('encoding', 'utf-8'),
-                        conf.get('allow_none', 0))
-        return cherrypy.serving.response.body
-    default.exposed = True
-
-
-class SessionAuthTool(HandlerTool):
-    
-    def _setargs(self):
-        for name in dir(cptools.SessionAuth):
-            if not name.startswith("__"):
-                setattr(self, name, None)
-
-
-class CachingTool(Tool):
-    """Caching Tool for CherryPy."""
-    
-    def _wrapper(self, **kwargs):
-        request = cherrypy.serving.request
-        if _caching.get(**kwargs):
-            request.handler = None
-        else:
-            if request.cacheable:
-                # Note the devious technique here of adding hooks on the fly
-                request.hooks.attach('before_finalize', _caching.tee_output,
-                                     priority = 90)
-    _wrapper.priority = 20
-    
-    def _setup(self):
-        """Hook caching into cherrypy.request."""
-        conf = self._merged_args()
-        
-        p = conf.pop("priority", None)
-        cherrypy.serving.request.hooks.attach('before_handler', self._wrapper,
-                                              priority=p, **conf)
-
-
-
-class Toolbox(object):
-    """A collection of Tools.
-    
-    This object also functions as a config namespace handler for itself.
-    Custom toolboxes should be added to each Application's toolboxes dict.
-    """
-    
-    def __init__(self, namespace):
-        self.namespace = namespace
-    
-    def __setattr__(self, name, value):
-        # If the Tool._name is None, supply it from the attribute name.
-        if isinstance(value, Tool):
-            if value._name is None:
-                value._name = name
-            value.namespace = self.namespace
-        object.__setattr__(self, name, value)
-    
-    def __enter__(self):
-        """Populate request.toolmaps from tools specified in config."""
-        cherrypy.serving.request.toolmaps[self.namespace] = map = {}
-        def populate(k, v):
-            toolname, arg = k.split(".", 1)
-            bucket = map.setdefault(toolname, {})
-            bucket[arg] = v
-        return populate
-    
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        """Run tool._setup() for each tool in our toolmap."""
-        map = cherrypy.serving.request.toolmaps.get(self.namespace)
-        if map:
-            for name, settings in map.items():
-                if settings.get("on", False):
-                    tool = getattr(self, name)
-                    tool._setup()
-
-
-class DeprecatedTool(Tool):
-    
-    _name = None
-    warnmsg = "This Tool is deprecated."
-    
-    def __init__(self, point, warnmsg=None):
-        self.point = point
-        if warnmsg is not None:
-            self.warnmsg = warnmsg
-    
-    def __call__(self, *args, **kwargs):
-        warnings.warn(self.warnmsg)
-        def tool_decorator(f):
-            return f
-        return tool_decorator
-    
-    def _setup(self):
-        warnings.warn(self.warnmsg)
-
-
-default_toolbox = _d = Toolbox("tools")
-_d.session_auth = SessionAuthTool(cptools.session_auth)
-_d.proxy = Tool('before_request_body', cptools.proxy, priority=30)
-_d.response_headers = Tool('on_start_resource', cptools.response_headers)
-_d.log_tracebacks = Tool('before_error_response', cptools.log_traceback)
-_d.log_headers = Tool('before_error_response', cptools.log_request_headers)
-_d.log_hooks = Tool('on_end_request', cptools.log_hooks, priority=100)
-_d.err_redirect = ErrorTool(cptools.redirect)
-_d.etags = Tool('before_finalize', cptools.validate_etags, priority=75)
-_d.decode = Tool('before_request_body', encoding.decode)
-# the order of encoding, gzip, caching is important
-_d.encode = Tool('before_handler', encoding.ResponseEncoder, priority=70)
-_d.gzip = Tool('before_finalize', encoding.gzip, priority=80)
-_d.staticdir = HandlerTool(static.staticdir)
-_d.staticfile = HandlerTool(static.staticfile)
-_d.sessions = SessionTool()
-_d.xmlrpc = ErrorTool(_xmlrpc.on_error)
-_d.caching = CachingTool('before_handler', _caching.get, 'caching')
-_d.expires = Tool('before_finalize', _caching.expires)
-_d.tidy = DeprecatedTool('before_finalize',
-    "The tidy tool has been removed from the standard distribution of CherryPy. "
-    "The most recent version can be found at http://tools.cherrypy.org/browser.")
-_d.nsgmls = DeprecatedTool('before_finalize',
-    "The nsgmls tool has been removed from the standard distribution of CherryPy. "
-    "The most recent version can be found at http://tools.cherrypy.org/browser.")
-_d.ignore_headers = Tool('before_request_body', cptools.ignore_headers)
-_d.referer = Tool('before_request_body', cptools.referer)
-_d.basic_auth = Tool('on_start_resource', auth.basic_auth)
-_d.digest_auth = Tool('on_start_resource', auth.digest_auth)
-_d.trailing_slash = Tool('before_handler', cptools.trailing_slash, priority=60)
-_d.flatten = Tool('before_finalize', cptools.flatten)
-_d.accept = Tool('on_start_resource', cptools.accept)
-_d.redirect = Tool('on_start_resource', cptools.redirect)
-_d.autovary = Tool('on_start_resource', cptools.autovary, priority=0)
-_d.json_in = Tool('before_request_body', jsontools.json_in, priority=30)
-_d.json_out = Tool('before_handler', jsontools.json_out, priority=30)
-_d.auth_basic = Tool('before_handler', auth_basic.basic_auth, priority=1)
-_d.auth_digest = Tool('before_handler', auth_digest.digest_auth, priority=1)
-
-del _d, cptools, encoding, auth, static
--- a/bundled/cherrypy/cherrypy/_cptree.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,278 +0,0 @@
-"""CherryPy Application and Tree objects."""
-
-import os
-import cherrypy
-from cherrypy import _cpconfig, _cplogging, _cprequest, _cpwsgi, tools
-from cherrypy.lib import httputil
-
-
-class Application(object):
-    """A CherryPy Application.
-    
-    Servers and gateways should not instantiate Request objects directly.
-    Instead, they should ask an Application object for a request object.
-    
-    An instance of this class may also be used as a WSGI callable
-    (WSGI application object) for itself.
-    """
-    
-    __metaclass__ = cherrypy._AttributeDocstrings
-    
-    root = None
-    root__doc = """
-    The top-most container of page handlers for this app. Handlers should
-    be arranged in a hierarchy of attributes, matching the expected URI
-    hierarchy; the default dispatcher then searches this hierarchy for a
-    matching handler. When using a dispatcher other than the default,
-    this value may be None."""
-    
-    config = {}
-    config__doc = """
-    A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict
-    of {key: value} pairs."""
-    
-    namespaces = _cpconfig.NamespaceSet()
-    toolboxes = {'tools': cherrypy.tools}
-    
-    log = None
-    log__doc = """A LogManager instance. See _cplogging."""
-    
-    wsgiapp = None
-    wsgiapp__doc = """A CPWSGIApp instance. See _cpwsgi."""
-    
-    request_class = _cprequest.Request
-    response_class = _cprequest.Response
-    
-    relative_urls = False
-    
-    def __init__(self, root, script_name="", config=None):
-        self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root)
-        self.root = root
-        self.script_name = script_name
-        self.wsgiapp = _cpwsgi.CPWSGIApp(self)
-        
-        self.namespaces = self.namespaces.copy()
-        self.namespaces["log"] = lambda k, v: setattr(self.log, k, v)
-        self.namespaces["wsgi"] = self.wsgiapp.namespace_handler
-        
-        self.config = self.__class__.config.copy()
-        if config:
-            self.merge(config)
-    
-    def __repr__(self):
-        return "%s.%s(%r, %r)" % (self.__module__, self.__class__.__name__,
-                                  self.root, self.script_name)
-    
-    script_name__doc = """
-    The URI "mount point" for this app. A mount point is that portion of
-    the URI which is constant for all URIs that are serviced by this
-    application; it does not include scheme, host, or proxy ("virtual host")
-    portions of the URI.
-    
-    For example, if script_name is "/my/cool/app", then the URL
-    "http://www.example.com/my/cool/app/page1" might be handled by a
-    "page1" method on the root object.
-    
-    The value of script_name MUST NOT end in a slash. If the script_name
-    refers to the root of the URI, it MUST be an empty string (not "/").
-    
-    If script_name is explicitly set to None, then the script_name will be
-    provided for each call from request.wsgi_environ['SCRIPT_NAME'].
-    """
-    def _get_script_name(self):
-        if self._script_name is None:
-            # None signals that the script name should be pulled from WSGI environ.
-            return cherrypy.serving.request.wsgi_environ['SCRIPT_NAME'].rstrip("/")
-        return self._script_name
-    def _set_script_name(self, value):
-        if value:
-            value = value.rstrip("/")
-        self._script_name = value
-    script_name = property(fget=_get_script_name, fset=_set_script_name,
-                           doc=script_name__doc)
-    
-    def merge(self, config):
-        """Merge the given config into self.config."""
-        _cpconfig.merge(self.config, config)
-        
-        # Handle namespaces specified in config.
-        self.namespaces(self.config.get("/", {}))
-    
-    def find_config(self, path, key, default=None):
-        """Return the most-specific value for key along path, or default."""
-        trail = path or "/"
-        while trail:
-            nodeconf = self.config.get(trail, {})
-            
-            if key in nodeconf:
-                return nodeconf[key]
-            
-            lastslash = trail.rfind("/")
-            if lastslash == -1:
-                break
-            elif lastslash == 0 and trail != "/":
-                trail = "/"
-            else:
-                trail = trail[:lastslash]
-        
-        return default
-    
-    def get_serving(self, local, remote, scheme, sproto):
-        """Create and return a Request and Response object."""
-        req = self.request_class(local, remote, scheme, sproto)
-        req.app = self
-        
-        for name, toolbox in self.toolboxes.items():
-            req.namespaces[name] = toolbox
-        
-        resp = self.response_class()
-        cherrypy.serving.load(req, resp)
-        cherrypy.engine.timeout_monitor.acquire()
-        cherrypy.engine.publish('acquire_thread')
-        
-        return req, resp
-    
-    def release_serving(self):
-        """Release the current serving (request and response)."""
-        req = cherrypy.serving.request
-        
-        cherrypy.engine.timeout_monitor.release()
-        
-        try:
-            req.close()
-        except:
-            cherrypy.log(traceback=True, severity=40)
-        
-        cherrypy.serving.clear()
-    
-    def __call__(self, environ, start_response):
-        return self.wsgiapp(environ, start_response)
-
-
-class Tree(object):
-    """A registry of CherryPy applications, mounted at diverse points.
-    
-    An instance of this class may also be used as a WSGI callable
-    (WSGI application object), in which case it dispatches to all
-    mounted apps.
-    """
-    
-    apps = {}
-    apps__doc = """
-    A dict of the form {script name: application}, where "script name"
-    is a string declaring the URI mount point (no trailing slash), and
-    "application" is an instance of cherrypy.Application (or an arbitrary
-    WSGI callable if you happen to be using a WSGI server)."""
-    
-    def __init__(self):
-        self.apps = {}
-    
-    def mount(self, root, script_name="", config=None):
-        """Mount a new app from a root object, script_name, and config.
-        
-        root: an instance of a "controller class" (a collection of page
-            handler methods) which represents the root of the application.
-            This may also be an Application instance, or None if using
-            a dispatcher other than the default.
-        script_name: a string containing the "mount point" of the application.
-            This should start with a slash, and be the path portion of the
-            URL at which to mount the given root. For example, if root.index()
-            will handle requests to "http://www.example.com:8080/dept/app1/",
-            then the script_name argument would be "/dept/app1".
-            
-            It MUST NOT end in a slash. If the script_name refers to the
-            root of the URI, it MUST be an empty string (not "/").
-        config: a file or dict containing application config.
-        """
-        if script_name is None:
-            raise TypeError(
-                "The 'script_name' argument may not be None. Application "
-                "objects may, however, possess a script_name of None (in "
-                "order to inpect the WSGI environ for SCRIPT_NAME upon each "
-                "request). You cannot mount such Applications on this Tree; "
-                "you must pass them to a WSGI server interface directly.")
-        
-        # Next line both 1) strips trailing slash and 2) maps "/" -> "".
-        script_name = script_name.rstrip("/")
-        
-        if isinstance(root, Application):
-            app = root
-            if script_name != "" and script_name != app.script_name:
-                raise ValueError("Cannot specify a different script name and "
-                                 "pass an Application instance to cherrypy.mount")
-            script_name = app.script_name
-        else:
-            app = Application(root, script_name)
-            
-            # If mounted at "", add favicon.ico
-            if (script_name == "" and root is not None
-                    and not hasattr(root, "favicon_ico")):
-                favicon = os.path.join(os.getcwd(), os.path.dirname(__file__),
-                                       "favicon.ico")
-                root.favicon_ico = tools.staticfile.handler(favicon)
-        
-        if config:
-            app.merge(config)
-        
-        self.apps[script_name] = app
-        
-        return app
-    
-    def graft(self, wsgi_callable, script_name=""):
-        """Mount a wsgi callable at the given script_name."""
-        # Next line both 1) strips trailing slash and 2) maps "/" -> "".
-        script_name = script_name.rstrip("/")
-        self.apps[script_name] = wsgi_callable
-    
-    def script_name(self, path=None):
-        """The script_name of the app at the given path, or None.
-        
-        If path is None, cherrypy.request is used.
-        """
-        if path is None:
-            try:
-                request = cherrypy.serving.request
-                path = httputil.urljoin(request.script_name,
-                                        request.path_info)
-            except AttributeError:
-                return None
-        
-        while True:
-            if path in self.apps:
-                return path
-            
-            if path == "":
-                return None
-            
-            # Move one node up the tree and try again.
-            path = path[:path.rfind("/")]
-    
-    def __call__(self, environ, start_response):
-        # If you're calling this, then you're probably setting SCRIPT_NAME
-        # to '' (some WSGI servers always set SCRIPT_NAME to '').
-        # Try to look up the app using the full path.
-        env1x = environ
-        if environ.get(u'wsgi.version') == (u'u', 0):
-            env1x = _cpwsgi.downgrade_wsgi_ux_to_1x(environ)
-        path = httputil.urljoin(env1x.get('SCRIPT_NAME', ''),
-                                env1x.get('PATH_INFO', ''))
-        sn = self.script_name(path or "/")
-        if sn is None:
-            start_response('404 Not Found', [])
-            return []
-        
-        app = self.apps[sn]
-        
-        # Correct the SCRIPT_NAME and PATH_INFO environ entries.
-        environ = environ.copy()
-        if environ.get(u'wsgi.version') == (u'u', 0):
-            # Python 2/WSGI u.0: all strings MUST be of type unicode
-            enc = environ[u'wsgi.url_encoding']
-            environ[u'SCRIPT_NAME'] = sn.decode(enc)
-            environ[u'PATH_INFO'] = path[len(sn.rstrip("/")):].decode(enc)
-        else:
-            # Python 2/WSGI 1.x: all strings MUST be of type str
-            environ['SCRIPT_NAME'] = sn
-            environ['PATH_INFO'] = path[len(sn.rstrip("/")):]
-        return app(environ, start_response)
-
--- a/bundled/cherrypy/cherrypy/_cpwsgi.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,340 +0,0 @@
-"""WSGI interface (see PEP 333)."""
-
-import sys as _sys
-
-import cherrypy as _cherrypy
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-from cherrypy import _cperror
-from cherrypy.lib import httputil
-
-
-def downgrade_wsgi_ux_to_1x(environ):
-    """Return a new environ dict for WSGI 1.x from the given WSGI u.x environ."""
-    env1x = {}
-    
-    url_encoding = environ[u'wsgi.url_encoding']
-    for k, v in environ.items():
-        if k in [u'PATH_INFO', u'SCRIPT_NAME', u'QUERY_STRING']:
-            v = v.encode(url_encoding)
-        elif isinstance(v, unicode):
-            v = v.encode('ISO-8859-1')
-        env1x[k.encode('ISO-8859-1')] = v
-    
-    return env1x
-
-
-class VirtualHost(object):
-    """Select a different WSGI application based on the Host header.
-    
-    This can be useful when running multiple sites within one CP server.
-    It allows several domains to point to different applications. For example:
-    
-        root = Root()
-        RootApp = cherrypy.Application(root)
-        Domain2App = cherrypy.Application(root)
-        SecureApp = cherrypy.Application(Secure())
-        
-        vhost = cherrypy._cpwsgi.VirtualHost(RootApp,
-            domains={'www.domain2.example': Domain2App,
-                     'www.domain2.example:443': SecureApp,
-                     })
-        
-        cherrypy.tree.graft(vhost)
-    
-    default: required. The default WSGI application.
-    
-    use_x_forwarded_host: if True (the default), any "X-Forwarded-Host"
-        request header will be used instead of the "Host" header. This
-        is commonly added by HTTP servers (such as Apache) when proxying.
-    
-    domains: a dict of {host header value: application} pairs.
-        The incoming "Host" request header is looked up in this dict,
-        and, if a match is found, the corresponding WSGI application
-        will be called instead of the default. Note that you often need
-        separate entries for "example.com" and "www.example.com".
-        In addition, "Host" headers may contain the port number.
-    """
-    
-    def __init__(self, default, domains=None, use_x_forwarded_host=True):
-        self.default = default
-        self.domains = domains or {}
-        self.use_x_forwarded_host = use_x_forwarded_host
-    
-    def __call__(self, environ, start_response):
-        domain = environ.get('HTTP_HOST', '')
-        if self.use_x_forwarded_host:
-            domain = environ.get("HTTP_X_FORWARDED_HOST", domain)
-        
-        nextapp = self.domains.get(domain)
-        if nextapp is None:
-            nextapp = self.default
-        return nextapp(environ, start_response)
-
-
-class InternalRedirector(object):
-    """WSGI middleware that handles raised cherrypy.InternalRedirect."""
-    
-    def __init__(self, nextapp, recursive=False):
-        self.nextapp = nextapp
-        self.recursive = recursive
-    
-    def __call__(self, environ, start_response):
-        redirections = []
-        while True:
-            environ = environ.copy()
-            try:
-                return self.nextapp(environ, start_response)
-            except _cherrypy.InternalRedirect, ir:
-                sn = environ.get('SCRIPT_NAME', '')
-                path = environ.get('PATH_INFO', '')
-                qs = environ.get('QUERY_STRING', '')
-                
-                # Add the *previous* path_info + qs to redirections.
-                old_uri = sn + path
-                if qs:
-                    old_uri += "?" + qs
-                redirections.append(old_uri)
-                
-                if not self.recursive:
-                    # Check to see if the new URI has been redirected to already
-                    new_uri = sn + ir.path
-                    if ir.query_string:
-                        new_uri += "?" + ir.query_string
-                    if new_uri in redirections:
-                        ir.request.close()
-                        raise RuntimeError("InternalRedirector visited the "
-                                           "same URL twice: %r" % new_uri)
-                
-                # Munge the environment and try again.
-                environ['REQUEST_METHOD'] = "GET"
-                environ['PATH_INFO'] = ir.path
-                environ['QUERY_STRING'] = ir.query_string
-                environ['wsgi.input'] = StringIO()
-                environ['CONTENT_LENGTH'] = "0"
-                environ['cherrypy.previous_request'] = ir.request
-
-
-class ExceptionTrapper(object):
-    
-    def __init__(self, nextapp, throws=(KeyboardInterrupt, SystemExit)):
-        self.nextapp = nextapp
-        self.throws = throws
-    
-    def __call__(self, environ, start_response):
-        return _TrappedResponse(self.nextapp, environ, start_response, self.throws)
-
-
-class _TrappedResponse(object):
-    
-    response = iter([])
-    
-    def __init__(self, nextapp, environ, start_response, throws):
-        self.nextapp = nextapp
-        self.environ = environ
-        self.start_response = start_response
-        self.throws = throws
-        self.started_response = False
-        self.response = self.trap(self.nextapp, self.environ, self.start_response)
-        self.iter_response = iter(self.response)
-    
-    def __iter__(self):
-        self.started_response = True
-        return self
-    
-    def next(self):
-        return self.trap(self.iter_response.next)
-    
-    def close(self):
-        if hasattr(self.response, 'close'):
-            self.response.close()
-    
-    def trap(self, func, *args, **kwargs):
-        try:
-            return func(*args, **kwargs)
-        except self.throws:
-            raise
-        except StopIteration:
-            raise
-        except:
-            tb = _cperror.format_exc()
-            #print('trapped (started %s):' % self.started_response, tb)
-            _cherrypy.log(tb, severity=40)
-            if not _cherrypy.request.show_tracebacks:
-                tb = ""
-            s, h, b = _cperror.bare_error(tb)
-            if self.started_response:
-                # Empty our iterable (so future calls raise StopIteration)
-                self.iter_response = iter([])
-            else:
-                self.iter_response = iter(b)
-            
-            try:
-                self.start_response(s, h, _sys.exc_info())
-            except:
-                # "The application must not trap any exceptions raised by
-                # start_response, if it called start_response with exc_info.
-                # Instead, it should allow such exceptions to propagate
-                # back to the server or gateway."
-                # But we still log and call close() to clean up ourselves.
-                _cherrypy.log(traceback=True, severity=40)
-                raise
-            
-            if self.started_response:
-                return "".join(b)
-            else:
-                return b
-
-
-#                           WSGI-to-CP Adapter                           #
-
-
-class AppResponse(object):
-    """WSGI response iterable for CherryPy applications."""
-    
-    def __init__(self, environ, start_response, cpapp):
-        if environ.get(u'wsgi.version') == (u'u', 0):
-            environ = downgrade_wsgi_ux_to_1x(environ)
-        self.environ = environ
-        self.cpapp = cpapp
-        try:
-            self.run()
-        except:
-            self.close()
-            raise
-        r = _cherrypy.serving.response
-        self.iter_response = iter(r.body)
-        self.write = start_response(r.output_status, r.header_list)
-    
-    def __iter__(self):
-        return self
-    
-    def next(self):
-        return self.iter_response.next()
-    
-    def close(self):
-        """Close and de-reference the current request and response. (Core)"""
-        self.cpapp.release_serving()
-    
-    def run(self):
-        """Create a Request object using environ."""
-        env = self.environ.get
-        
-        local = httputil.Host('', int(env('SERVER_PORT', 80)),
-                           env('SERVER_NAME', ''))
-        remote = httputil.Host(env('REMOTE_ADDR', ''),
-                               int(env('REMOTE_PORT', -1)),
-                               env('REMOTE_HOST', ''))
-        scheme = env('wsgi.url_scheme')
-        sproto = env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1")
-        request, resp = self.cpapp.get_serving(local, remote, scheme, sproto)
-        
-        # LOGON_USER is served by IIS, and is the name of the
-        # user after having been mapped to a local account.
-        # Both IIS and Apache set REMOTE_USER, when possible.
-        request.login = env('LOGON_USER') or env('REMOTE_USER') or None
-        request.multithread = self.environ['wsgi.multithread']
-        request.multiprocess = self.environ['wsgi.multiprocess']
-        request.wsgi_environ = self.environ
-        request.prev = env('cherrypy.previous_request', None)
-        
-        meth = self.environ['REQUEST_METHOD']
-        
-        path = httputil.urljoin(self.environ.get('SCRIPT_NAME', ''),
-                                self.environ.get('PATH_INFO', ''))
-        qs = self.environ.get('QUERY_STRING', '')
-        rproto = self.environ.get('SERVER_PROTOCOL')
-        headers = self.translate_headers(self.environ)
-        rfile = self.environ['wsgi.input']
-        request.run(meth, path, qs, rproto, headers, rfile)
-    
-    headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization',
-                   'CONTENT_LENGTH': 'Content-Length',
-                   'CONTENT_TYPE': 'Content-Type',
-                   'REMOTE_HOST': 'Remote-Host',
-                   'REMOTE_ADDR': 'Remote-Addr',
-                   }
-    
-    def translate_headers(self, environ):
-        """Translate CGI-environ header names to HTTP header names."""
-        for cgiName in environ:
-            # We assume all incoming header keys are uppercase already.
-            if cgiName in self.headerNames:
-                yield self.headerNames[cgiName], environ[cgiName]
-            elif cgiName[:5] == "HTTP_":
-                # Hackish attempt at recovering original header names.
-                translatedHeader = cgiName[5:].replace("_", "-")
-                yield translatedHeader, environ[cgiName]
-
-
-class CPWSGIApp(object):
-    """A WSGI application object for a CherryPy Application.
-    
-    pipeline: a list of (name, wsgiapp) pairs. Each 'wsgiapp' MUST be a
-        constructor that takes an initial, positional 'nextapp' argument,
-        plus optional keyword arguments, and returns a WSGI application
-        (that takes environ and start_response arguments). The 'name' can
-        be any you choose, and will correspond to keys in self.config.
-    
-    head: rather than nest all apps in the pipeline on each call, it's only
-        done the first time, and the result is memoized into self.head. Set
-        this to None again if you change self.pipeline after calling self.
-    
-    config: a dict whose keys match names listed in the pipeline. Each
-        value is a further dict which will be passed to the corresponding
-        named WSGI callable (from the pipeline) as keyword arguments.
-    """
-    
-    pipeline = [('ExceptionTrapper', ExceptionTrapper),
-                ('InternalRedirector', InternalRedirector),
-                ]
-    head = None
-    config = {}
-    
-    response_class = AppResponse
-    
-    def __init__(self, cpapp, pipeline=None):
-        self.cpapp = cpapp
-        self.pipeline = self.pipeline[:]
-        if pipeline:
-            self.pipeline.extend(pipeline)
-        self.config = self.config.copy()
-    
-    def tail(self, environ, start_response):
-        """WSGI application callable for the actual CherryPy application.
-        
-        You probably shouldn't call this; call self.__call__ instead,
-        so that any WSGI middleware in self.pipeline can run first.
-        """
-        return self.response_class(environ, start_response, self.cpapp)
-    
-    def __call__(self, environ, start_response):
-        head = self.head
-        if head is None:
-            # Create and nest the WSGI apps in our pipeline (in reverse order).
-            # Then memoize the result in self.head.
-            head = self.tail
-            for name, callable in self.pipeline[::-1]:
-                conf = self.config.get(name, {})
-                head = callable(head, **conf)
-            self.head = head
-        return head(environ, start_response)
-    
-    def namespace_handler(self, k, v):
-        """Config handler for the 'wsgi' namespace."""
-        if k == "pipeline":
-            # Note this allows multiple 'wsgi.pipeline' config entries
-            # (but each entry will be processed in a 'random' order).
-            # It should also allow developers to set default middleware
-            # in code (passed to self.__init__) that deployers can add to
-            # (but not remove) via config.
-            self.pipeline.extend(v)
-        elif k == "response_class":
-            self.response_class = v
-        else:
-            name, arg = k.split(".", 1)
-            bucket = self.config.setdefault(name, {})
-            bucket[arg] = v
-
--- a/bundled/cherrypy/cherrypy/_cpwsgi_server.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-"""WSGI server interface (see PEP 333). This adds some CP-specific bits to
-the framework-agnostic wsgiserver package.
-"""
-import sys
-
-import cherrypy
-from cherrypy import wsgiserver
-
-
-class CPHTTPRequest(wsgiserver.HTTPRequest):
-    pass
-
-
-class CPHTTPConnection(wsgiserver.HTTPConnection):
-    pass
-
-
-class CPWSGIServer(wsgiserver.CherryPyWSGIServer):
-    """Wrapper for wsgiserver.CherryPyWSGIServer.
-    
-    wsgiserver has been designed to not reference CherryPy in any way,
-    so that it can be used in other frameworks and applications. Therefore,
-    we wrap it here, so we can set our own mount points from cherrypy.tree
-    and apply some attributes from config -> cherrypy.server -> wsgiserver.
-    """
-    
-    def __init__(self, server_adapter=cherrypy.server):
-        self.server_adapter = server_adapter
-        self.max_request_header_size = self.server_adapter.max_request_header_size or 0
-        self.max_request_body_size = self.server_adapter.max_request_body_size or 0
-        
-        server_name = (self.server_adapter.socket_host or
-                       self.server_adapter.socket_file or
-                       None)
-        
-        self.wsgi_version = self.server_adapter.wsgi_version
-        s = wsgiserver.CherryPyWSGIServer
-        s.__init__(self, server_adapter.bind_addr, cherrypy.tree,
-                   self.server_adapter.thread_pool,
-                   server_name,
-                   max = self.server_adapter.thread_pool_max,
-                   request_queue_size = self.server_adapter.socket_queue_size,
-                   timeout = self.server_adapter.socket_timeout,
-                   shutdown_timeout = self.server_adapter.shutdown_timeout,
-                   )
-        self.protocol = self.server_adapter.protocol_version
-        self.nodelay = self.server_adapter.nodelay
-        
-        ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
-        if self.server_adapter.ssl_context:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain)
-            self.ssl_adapter.context = self.server_adapter.ssl_context
-        elif self.server_adapter.ssl_certificate:
-            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
-            self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain)
--- a/bundled/cherrypy/cherrypy/cherryd	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-#! /usr/bin/env python
-"""The CherryPy daemon."""
-
-import sys
-
-import cherrypy
-from cherrypy.process import plugins, servers
-
-
-def start(configfiles=None, daemonize=False, environment=None,
-          fastcgi=False, scgi=False, pidfile=None, imports=None):
-    """Subscribe all engine plugins and start the engine."""
-    sys.path = [''] + sys.path
-    for i in imports or []:
-        exec("import %s" % i)
-    
-    for c in configfiles or []:
-        cherrypy.config.update(c)
-        # If there's only one app mounted, merge config into it.
-        if len(cherrypy.tree.apps) == 1:
-            for app in cherrypy.tree.apps.values():
-                app.merge(c)
-    
-    engine = cherrypy.engine
-    
-    if environment is not None:
-        cherrypy.config.update({'environment': environment})
-    
-    # Only daemonize if asked to.
-    if daemonize:
-        # Don't print anything to stdout/sterr.
-        cherrypy.config.update({'log.screen': False})
-        plugins.Daemonizer(engine).subscribe()
-    
-    if pidfile:
-        plugins.PIDFile(engine, pidfile).subscribe()
-    
-    if hasattr(engine, "signal_handler"):
-        engine.signal_handler.subscribe()
-    if hasattr(engine, "console_control_handler"):
-        engine.console_control_handler.subscribe()
-    
-    if fastcgi and scgi:
-        # fastcgi and scgi aren't allowed together.
-        cherrypy.log.error("fastcgi and scgi aren't allowed together.", 'ENGINE')
-        sys.exit(1)
-    elif fastcgi or scgi:
-        # Turn off autoreload when using fastcgi or scgi.
-        cherrypy.config.update({'engine.autoreload_on': False})
-        # Turn off the default HTTP server (which is subscribed by default).
-        cherrypy.server.unsubscribe()
-        
-        addr = cherrypy.server.bind_addr
-        if fastcgi:
-            f = servers.FlupFCGIServer(application=cherrypy.tree,
-                                       bindAddress=addr)
-        else:
-            f = servers.FlupSCGIServer(application=cherrypy.tree,
-                                       bindAddress=addr)
-        s = servers.ServerAdapter(engine, httpserver=f, bind_addr=addr)
-        s.subscribe()
-    
-    # Always start the engine; this will start all other services
-    try:
-        engine.start()
-    except:
-        # Assume the error has been logged already via bus.log.
-        sys.exit(1)
-    else:
-        engine.block()
-
-
-if __name__ == '__main__':
-    from optparse import OptionParser
-    
-    p = OptionParser()
-    p.add_option('-c', '--config', action="append", dest='config',
-                 help="specify config file(s)")
-    p.add_option('-d', action="store_true", dest='daemonize',
-                 help="run the server as a daemon")
-    p.add_option('-e', '--environment', dest='environment', default=None,
-                 help="apply the given config environment")
-    p.add_option('-f', action="store_true", dest='fastcgi',
-                 help="start a fastcgi server instead of the default HTTP server")
-    p.add_option('-s', action="store_true", dest='scgi',
-                 help="start a scgi server instead of the default HTTP server")
-    p.add_option('-i', '--import', action="append", dest='imports',
-                 help="specify modules to import")
-    p.add_option('-p', '--pidfile', dest='pidfile', default=None,
-                 help="store the process id in the given file")
-    p.add_option('-P', '--Path', action="append", dest='Path',
-                 help="add the given paths to sys.path")
-    options, args = p.parse_args()
-    
-    if options.Path:
-        for p in options.Path:
-            sys.path.insert(0, p)
-    
-    start(options.config, options.daemonize,
-          options.environment, options.fastcgi, options.scgi, options.pidfile,
-          options.imports)
-
Binary file bundled/cherrypy/cherrypy/favicon.ico has changed
--- a/bundled/cherrypy/cherrypy/lib/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-"""CherryPy Library"""
-
-# Deprecated in CherryPy 3.2 -- remove in CherryPy 3.3
-from cherrypy.lib.reprconf import _Builder, unrepr, modules, attributes
-
-class file_generator(object):
-    """Yield the given input (a file object) in chunks (default 64k). (Core)"""
-    
-    def __init__(self, input, chunkSize=65536):
-        self.input = input
-        self.chunkSize = chunkSize
-    
-    def __iter__(self):
-        return self
-    
-    def __next__(self):
-        chunk = self.input.read(self.chunkSize)
-        if chunk:
-            return chunk
-        else:
-            self.input.close()
-            raise StopIteration()
-    next = __next__
-
-def file_generator_limited(fileobj, count, chunk_size=65536):
-    """Yield the given file object in chunks, stopping after `count`
-    bytes has been emitted.  Default chunk size is 64kB. (Core)
-    """
-    remaining = count
-    while remaining > 0:
-        chunk = fileobj.read(min(chunk_size, remaining))
-        chunklen = len(chunk)
-        if chunklen == 0:
-            return
-        remaining -= chunklen
-        yield chunk
-
-def set_vary_header(response, header_name):
-    "Add a Vary header to a response"
-    varies = response.headers.get("Vary", "")
-    varies = [x.strip() for x in varies.split(",") if x.strip()]
-    if header_name not in varies:
-        varies.append(header_name)
-    response.headers['Vary'] = ", ".join(varies)
--- a/bundled/cherrypy/cherrypy/lib/auth.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-import cherrypy
-from cherrypy.lib import httpauth
-
-
-def check_auth(users, encrypt=None, realm=None):
-    """If an authorization header contains credentials, return True, else False."""
-    request = cherrypy.serving.request
-    if 'authorization' in request.headers:
-        # make sure the provided credentials are correctly set
-        ah = httpauth.parseAuthorization(request.headers['authorization'])
-        if ah is None:
-            raise cherrypy.HTTPError(400, 'Bad Request')
-        
-        if not encrypt:
-            encrypt = httpauth.DIGEST_AUTH_ENCODERS[httpauth.MD5]
-        
-        if hasattr(users, '__call__'):
-            try:
-                # backward compatibility
-                users = users() # expect it to return a dictionary
-                
-                if not isinstance(users, dict):
-                    raise ValueError("Authentication users must be a dictionary")
-                
-                # fetch the user password
-                password = users.get(ah["username"], None)
-            except TypeError:
-                # returns a password (encrypted or clear text)
-                password = users(ah["username"])
-        else:
-            if not isinstance(users, dict):
-                raise ValueError("Authentication users must be a dictionary")
-            
-            # fetch the user password
-            password = users.get(ah["username"], None)
-        
-        # validate the authorization by re-computing it here
-        # and compare it with what the user-agent provided
-        if httpauth.checkResponse(ah, password, method=request.method,
-                                  encrypt=encrypt, realm=realm):
-            request.login = ah["username"]
-            return True
-        
-        request.login = False
-    return False
-
-def basic_auth(realm, users, encrypt=None, debug=False):
-    """If auth fails, raise 401 with a basic authentication header.
-    
-    realm: a string containing the authentication realm.
-    users: a dict of the form: {username: password} or a callable returning a dict.
-    encrypt: callable used to encrypt the password returned from the user-agent.
-             if None it defaults to a md5 encryption.
-    """
-    if check_auth(users, encrypt):
-        if debug:
-            cherrypy.log('Auth successful', 'TOOLS.BASIC_AUTH')
-        return
-    
-    # inform the user-agent this path is protected
-    cherrypy.serving.response.headers['www-authenticate'] = httpauth.basicAuth(realm)
-    
-    raise cherrypy.HTTPError(401, "You are not authorized to access that resource")
-
-def digest_auth(realm, users, debug=False):
-    """If auth fails, raise 401 with a digest authentication header.
-    
-    realm: a string containing the authentication realm.
-    users: a dict of the form: {username: password} or a callable returning a dict.
-    """
-    if check_auth(users, realm=realm):
-        if debug:
-            cherrypy.log('Auth successful', 'TOOLS.DIGEST_AUTH')
-        return
-    
-    # inform the user-agent this path is protected
-    cherrypy.serving.response.headers['www-authenticate'] = httpauth.digestAuth(realm)
-    
-    raise cherrypy.HTTPError(401, "You are not authorized to access that resource")
--- a/bundled/cherrypy/cherrypy/lib/auth_basic.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-__doc__ = """Module auth_basic.py provides a CherryPy 3.x tool which implements
-the server-side of HTTP Basic Access Authentication, as described in RFC 2617.
-
-Example usage, using the built-in checkpassword_dict function which uses a dict
-as the credentials store:
-
-userpassdict = {'bird' : 'bebop', 'ornette' : 'wayout'}
-checkpassword = cherrypy.lib.auth_basic.checkpassword_dict(userpassdict)
-basic_auth = {'tools.auth_basic.on': True,
-              'tools.auth_basic.realm': 'earth',
-              'tools.auth_basic.checkpassword': checkpassword,
-}
-app_config = { '/' : basic_auth }
-"""
-
-__author__ = 'visteya'
-__date__ = 'April 2009'
-
-import binascii
-import base64
-import cherrypy
-
-
-def checkpassword_dict(user_password_dict):
-    """Returns a checkpassword function which checks credentials
-    against a dictionary of the form: {username : password}.
-
-    If you want a simple dictionary-based authentication scheme, use
-    checkpassword_dict(my_credentials_dict) as the value for the
-    checkpassword argument to basic_auth().
-    """
-    def checkpassword(realm, user, password):
-        p = user_password_dict.get(user)
-        return p and p == password or False
-
-    return checkpassword
-
-
-def basic_auth(realm, checkpassword, debug=False):
-    """basic_auth is a CherryPy tool which hooks at before_handler to perform
-    HTTP Basic Access Authentication, as specified in RFC 2617.
-
-    If the request has an 'authorization' header with a 'Basic' scheme, this
-    tool attempts to authenticate the credentials supplied in that header.  If
-    the request has no 'authorization' header, or if it does but the scheme is
-    not 'Basic', or if authentication fails, the tool sends a 401 response with
-    a 'WWW-Authenticate' Basic header.
-
-    Arguments:
-    realm: a string containing the authentication realm.
-
-    checkpassword: a callable which checks the authentication credentials.
-        Its signature is checkpassword(realm, username, password). where
-        username and password are the values obtained from the request's
-        'authorization' header.  If authentication succeeds, checkpassword
-        returns True, else it returns False.
-    """
-    
-    if '"' in realm:
-        raise ValueError('Realm cannot contain the " (quote) character.')
-    request = cherrypy.serving.request
-    
-    auth_header = request.headers.get('authorization')
-    if auth_header is not None:
-        try:
-            scheme, params = auth_header.split(' ', 1)
-            if scheme.lower() == 'basic':
-                # since CherryPy claims compability with Python 2.3, we must use
-                # the legacy API of base64
-                username_password = base64.decodestring(params)
-                username, password = username_password.split(':', 1)
-                if checkpassword(realm, username, password):
-                    if debug:
-                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
-                    request.login = username
-                    return # successful authentication
-        except (ValueError, binascii.Error): # split() error, base64.decodestring() error
-            raise cherrypy.HTTPError(400, 'Bad Request')
-    
-    # Respond with 401 status and a WWW-Authenticate header
-    cherrypy.serving.response.headers['www-authenticate'] = 'Basic realm="%s"' % realm
-    raise cherrypy.HTTPError(401, "You are not authorized to access that resource")
-
--- a/bundled/cherrypy/cherrypy/lib/auth_digest.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,358 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-__doc__ = """An implementation of the server-side of HTTP Digest Access
-Authentication, which is described in RFC 2617.
-
-Example usage, using the built-in get_ha1_dict_plain function which uses a dict
-of plaintext passwords as the credentials store:
-
-userpassdict = {'alice' : '4x5istwelve'}
-get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain(userpassdict)
-digest_auth = {'tools.auth_digest.on': True,
-               'tools.auth_digest.realm': 'wonderland',
-               'tools.auth_digest.get_ha1': get_ha1,
-               'tools.auth_digest.key': 'a565c27146791cfb',
-}
-app_config = { '/' : digest_auth }
-"""
-
-__author__ = 'visteya'
-__date__ = 'April 2009'
-
-
-try:
-    from hashlib import md5
-except ImportError:
-    # Python 2.4 and earlier
-    from md5 import new as md5
-md5_hex = lambda s: md5(s).hexdigest()
-
-import time
-import base64
-from urllib2 import parse_http_list, parse_keqv_list
-
-import cherrypy
-
-qop_auth = 'auth'
-qop_auth_int = 'auth-int'
-valid_qops = (qop_auth, qop_auth_int)
-
-valid_algorithms = ('MD5', 'MD5-sess')
-
-
-def TRACE(msg):
-    cherrypy.log(msg, context='TOOLS.AUTH_DIGEST')
-
-# Three helper functions for users of the tool, providing three variants
-# of get_ha1() functions for three different kinds of credential stores.
-def get_ha1_dict_plain(user_password_dict):
-    """Returns a get_ha1 function which obtains a plaintext password from a
-    dictionary of the form: {username : password}.
-
-    If you want a simple dictionary-based authentication scheme, with plaintext
-    passwords, use get_ha1_dict_plain(my_userpass_dict) as the value for the
-    get_ha1 argument to digest_auth().
-    """
-    def get_ha1(realm, username):
-        password = user_password_dict.get(username)
-        if password:
-            return md5_hex('%s:%s:%s' % (username, realm, password))
-        return None
-
-    return get_ha1
-
-def get_ha1_dict(user_ha1_dict):
-    """Returns a get_ha1 function which obtains a HA1 password hash from a
-    dictionary of the form: {username : HA1}.
-
-    If you want a dictionary-based authentication scheme, but with
-    pre-computed HA1 hashes instead of plain-text passwords, use
-    get_ha1_dict(my_userha1_dict) as the value for the get_ha1
-    argument to digest_auth().
-    """
-    def get_ha1(realm, username):
-        return user_ha1_dict.get(user)
-
-    return get_ha1
-
-def get_ha1_file_htdigest(filename):
-    """Returns a get_ha1 function which obtains a HA1 password hash from a
-    flat file with lines of the same format as that produced by the Apache
-    htdigest utility. For example, for realm 'wonderland', username 'alice',
-    and password '4x5istwelve', the htdigest line would be:
-
-    alice:wonderland:3238cdfe91a8b2ed8e39646921a02d4c
-
-    If you want to use an Apache htdigest file as the credentials store,
-    then use get_ha1_file_htdigest(my_htdigest_file) as the value for the
-    get_ha1 argument to digest_auth().  It is recommended that the filename
-    argument be an absolute path, to avoid problems.
-    """
-    def get_ha1(realm, username):
-        result = None
-        f = open(filename, 'r')
-        for line in f:
-            u, r, ha1 = line.rstrip().split(':')
-            if u == username and r == realm:
-                result = ha1
-                break
-        f.close()
-        return result
-
-    return get_ha1
-
-
-def synthesize_nonce(s, key, timestamp=None):
-    """Synthesize a nonce value which resists spoofing and can be checked for staleness.
-    Returns a string suitable as the value for 'nonce' in the www-authenticate header.
-
-    Args:
-    s: a string related to the resource, such as the hostname of the server.
-    key: a secret string known only to the server.
-    timestamp: an integer seconds-since-the-epoch timestamp
-    """
-    if timestamp is None:
-        timestamp = int(time.time())
-    h = md5_hex('%s:%s:%s' % (timestamp, s, key))
-    nonce = '%s:%s' % (timestamp, h)
-    return nonce
-
-
-def H(s):
-    """The hash function H"""
-    return md5_hex(s)
-
-
-class HttpDigestAuthorization (object):
-    """Class to parse a Digest Authorization header and perform re-calculation
-    of the digest.
-    """
-
-    def errmsg(self, s):
-        return 'Digest Authorization header: %s' % s
-
-    def __init__(self, auth_header, http_method, debug=False):
-        self.http_method = http_method
-        self.debug = debug
-        scheme, params  = auth_header.split(" ", 1)
-        self.scheme = scheme.lower()
-        if self.scheme != 'digest':
-            raise ValueError('Authorization scheme is not "Digest"')
-
-        self.auth_header = auth_header
-
-        # make a dict of the params
-        items = parse_http_list(params)
-        paramsd = parse_keqv_list(items)
-
-        self.realm = paramsd.get('realm')
-        self.username = paramsd.get('username')
-        self.nonce = paramsd.get('nonce')
-        self.uri = paramsd.get('uri')
-        self.method = paramsd.get('method')
-        self.response = paramsd.get('response') # the response digest
-        self.algorithm = paramsd.get('algorithm', 'MD5')
-        self.cnonce = paramsd.get('cnonce')
-        self.opaque = paramsd.get('opaque')
-        self.qop = paramsd.get('qop') # qop
-        self.nc = paramsd.get('nc') # nonce count
-
-        # perform some correctness checks
-        if self.algorithm not in valid_algorithms:
-            raise ValueError(self.errmsg("Unsupported value for algorithm: '%s'" % self.algorithm))
-
-        has_reqd = self.username and \
-                   self.realm and \
-                   self.nonce and \
-                   self.uri and \
-                   self.response
-        if not has_reqd:
-            raise ValueError(self.errmsg("Not all required parameters are present."))
-
-        if self.qop:
-            if self.qop not in valid_qops:
-                raise ValueError(self.errmsg("Unsupported value for qop: '%s'" % self.qop))
-            if not (self.cnonce and self.nc):
-                raise ValueError(self.errmsg("If qop is sent then cnonce and nc MUST be present"))
-        else:
-            if self.cnonce or self.nc:
-                raise ValueError(self.errmsg("If qop is not sent, neither cnonce nor nc can be present"))
-
-
-    def __str__(self):
-        return 'authorization : %s' % self.auth_header
-
-    def validate_nonce(self, s, key):
-        """Validate the nonce.
-        Returns True if nonce was generated by synthesize_nonce() and the timestamp
-        is not spoofed, else returns False.
-
-        Args:
-        s: a string related to the resource, such as the hostname of the server.
-        key: a secret string known only to the server.
-        Both s and key must be the same values which were used to synthesize the nonce
-        we are trying to validate.
-        """
-        try:
-            timestamp, hashpart = self.nonce.split(':', 1)
-            s_timestamp, s_hashpart = synthesize_nonce(s, key, timestamp).split(':', 1)
-            is_valid = s_hashpart == hashpart
-            if self.debug:
-                TRACE('validate_nonce: %s' % is_valid)
-            return is_valid
-        except ValueError: # split() error
-            pass
-        return False
-
-
-    def is_nonce_stale(self, max_age_seconds=600):
-        """Returns True if a validated nonce is stale.  The nonce contains a
-        timestamp in plaintext and also a secure hash of the timestamp.  You should
-        first validate the nonce to ensure the plaintext timestamp is not spoofed.
-        """
-        try:
-            timestamp, hashpart = self.nonce.split(':', 1)
-            if int(timestamp) + max_age_seconds > int(time.time()):
-                return False
-        except ValueError: # int() error
-            pass
-        if self.debug:
-            TRACE("nonce is stale")
-        return True
-
-
-    def HA2(self, entity_body=''):
-        """Returns the H(A2) string.  See RFC 2617 3.2.2.3."""
-        # RFC 2617 3.2.2.3
-        # If the "qop" directive's value is "auth" or is unspecified, then A2 is:
-        #    A2 = method ":" digest-uri-value
-        #
-        # If the "qop" value is "auth-int", then A2 is:
-        #    A2 = method ":" digest-uri-value ":" H(entity-body)
-        if self.qop is None or self.qop == "auth":
-            a2 = '%s:%s' % (self.http_method, self.uri)
-        elif self.qop == "auth-int":
-            a2 = "%s:%s:%s" % (self.http_method, self.uri, H(entity_body))
-        else:
-            # in theory, this should never happen, since I validate qop in __init__()
-            raise ValueError(self.errmsg("Unrecognized value for qop!"))
-        return H(a2)
-
-
-    def request_digest(self, ha1, entity_body=''):
-        """Calculates the Request-Digest.  See RFC 2617 3.2.2.1.
-        Arguments:
-
-        ha1 : the HA1 string obtained from the credentials store.
-
-        entity_body : if 'qop' is set to 'auth-int', then A2 includes a hash
-            of the "entity body".  The entity body is the part of the
-            message which follows the HTTP headers.  See RFC 2617 section
-            4.3.  This refers to the entity the user agent sent in the request which
-            has the Authorization header.  Typically GET requests don't have an entity,
-            and POST requests do.
-        """
-        ha2 = self.HA2(entity_body)
-        # Request-Digest -- RFC 2617 3.2.2.1
-        if self.qop:
-            req = "%s:%s:%s:%s:%s" % (self.nonce, self.nc, self.cnonce, self.qop, ha2)
-        else:
-            req = "%s:%s" % (self.nonce, ha2)
-
-        # RFC 2617 3.2.2.2
-        #
-        # If the "algorithm" directive's value is "MD5" or is unspecified, then A1 is:
-        # A1 = unq(username-value) ":" unq(realm-value) ":" passwd
-        #
-        # If the "algorithm" directive's value is "MD5-sess", then A1 is
-        # calculated only once - on the first request by the client following
-        # receipt of a WWW-Authenticate challenge from the server.
-        # A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
-        #         ":" unq(nonce-value) ":" unq(cnonce-value)
-        if self.algorithm == 'MD5-sess':
-            ha1 = H('%s:%s:%s' % (ha1, self.nonce, self.cnonce))
-
-        digest = H('%s:%s' % (ha1, req))
-        return digest
-
-
-
-def www_authenticate(realm, key, algorithm='MD5', nonce=None, qop=qop_auth, stale=False):
-    """Constructs a WWW-Authenticate header for Digest authentication."""
-    if qop not in valid_qops:
-        raise ValueError("Unsupported value for qop: '%s'" % qop)
-    if algorithm not in valid_algorithms:
-        raise ValueError("Unsupported value for algorithm: '%s'" % algorithm)
-
-    if nonce is None:
-        nonce = synthesize_nonce(realm, key)
-    s = 'Digest realm="%s", nonce="%s", algorithm="%s", qop="%s"' % (
-                realm, nonce, algorithm, qop)
-    if stale:
-        s += ', stale="true"'
-    return s
-
-
-def digest_auth(realm, get_ha1, key, debug=False):
-    """digest_auth is a CherryPy tool which hooks at before_handler to perform
-    HTTP Digest Access Authentication, as specified in RFC 2617.
-    
-    If the request has an 'authorization' header with a 'Digest' scheme, this
-    tool authenticates the credentials supplied in that header.  If
-    the request has no 'authorization' header, or if it does but the scheme is
-    not "Digest", or if authentication fails, the tool sends a 401 response with
-    a 'WWW-Authenticate' Digest header.
-    
-    Arguments:
-    realm: a string containing the authentication realm.
-    
-    get_ha1: a callable which looks up a username in a credentials store
-        and returns the HA1 string, which is defined in the RFC to be
-        MD5(username : realm : password).  The function's signature is:
-            get_ha1(realm, username)
-        where username is obtained from the request's 'authorization' header.
-        If username is not found in the credentials store, get_ha1() returns
-        None.
-    
-    key: a secret string known only to the server, used in the synthesis of nonces.
-    """
-    request = cherrypy.serving.request
-    
-    auth_header = request.headers.get('authorization')
-    nonce_is_stale = False
-    if auth_header is not None:
-        try:
-            auth = HttpDigestAuthorization(auth_header, request.method, debug=debug)
-        except ValueError, e:
-            raise cherrypy.HTTPError(400, 'Bad Request: %s' % e)
-        
-        if debug:
-            TRACE(str(auth))
-        
-        if auth.validate_nonce(realm, key):
-            ha1 = get_ha1(realm, auth.username)
-            if ha1 is not None:
-                # note that for request.body to be available we need to hook in at
-                # before_handler, not on_start_resource like 3.1.x digest_auth does.
-                digest = auth.request_digest(ha1, entity_body=request.body)
-                if digest == auth.response: # authenticated
-                    if debug:
-                        TRACE("digest matches auth.response")
-                    # Now check if nonce is stale.
-                    # The choice of ten minutes' lifetime for nonce is somewhat arbitrary
-                    nonce_is_stale = auth.is_nonce_stale(max_age_seconds=600)
-                    if not nonce_is_stale:
-                        request.login = auth.username
-                        if debug:
-                            TRACE("authentication of %s successful" % auth.username)
-                        return
-    
-    # Respond with 401 status and a WWW-Authenticate header
-    header = www_authenticate(realm, key, stale=nonce_is_stale)
-    if debug:
-        TRACE(header)
-    cherrypy.serving.response.headers['WWW-Authenticate'] = header
-    raise cherrypy.HTTPError(401, "You are not authorized to access that resource")
-
--- a/bundled/cherrypy/cherrypy/lib/caching.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,401 +0,0 @@
-import datetime
-import threading
-import time
-
-import cherrypy
-from cherrypy.lib import cptools, httputil
-
-
-class Cache(object):
-    
-    def get(self):
-        raise NotImplemented
-    
-    def put(self, obj, size):
-        raise NotImplemented
-    
-    def delete(self):
-        raise NotImplemented
-    
-    def clear(self):
-        raise NotImplemented
-
-
-
-# ------------------------------- Memory Cache ------------------------------- #
-
-
-class AntiStampedeCache(dict):
-    
-    def wait(self, key, timeout=5, debug=False):
-        """Return the cached value for the given key, or None.
-        
-        If timeout is not None (the default), and the value is already
-        being calculated by another thread, wait until the given timeout has
-        elapsed. If the value is available before the timeout expires, it is
-        returned. If not, None is returned, and a sentinel placed in the cache
-        to signal other threads to wait.
-        
-        If timeout is None, no waiting is performed nor sentinels used.
-        """
-        value = self.get(key)
-        if isinstance(value, threading._Event):
-            if timeout is None:
-                # Ignore the other thread and recalc it ourselves.
-                if debug:
-                    cherrypy.log('No timeout', 'TOOLS.CACHING')
-                return None
-            
-            # Wait until it's done or times out.
-            if debug:
-                cherrypy.log('Waiting up to %s seconds' % timeout, 'TOOLS.CACHING')
-            value.wait(timeout)
-            if value.result is not None:
-                # The other thread finished its calculation. Use it.
-                if debug:
-                    cherrypy.log('Result!', 'TOOLS.CACHING')
-                return value.result
-            # Timed out. Stick an Event in the slot so other threads wait
-            # on this one to finish calculating the value.
-            if debug:
-                cherrypy.log('Timed out', 'TOOLS.CACHING')
-            e = threading.Event()
-            e.result = None
-            dict.__setitem__(self, key, e)
-            
-            return None
-        elif value is None:
-            # Stick an Event in the slot so other threads wait
-            # on this one to finish calculating the value.
-            if debug:
-                cherrypy.log('Timed out', 'TOOLS.CACHING')
-            e = threading.Event()
-            e.result = None
-            dict.__setitem__(self, key, e)
-        return value
-    
-    def __setitem__(self, key, value):
-        """Set the cached value for the given key."""
-        existing = self.get(key)
-        dict.__setitem__(self, key, value)
-        if isinstance(existing, threading._Event):
-            # Set Event.result so other threads waiting on it have
-            # immediate access without needing to poll the cache again.
-            existing.result = value
-            existing.set()
-
-
-class MemoryCache(Cache):
-    """An in-memory cache for varying response content.
-    
-    Each key in self.store is a URI, and each value is an AntiStampedeCache.
-    The response for any given URI may vary based on the values of
-    "selecting request headers"; that is, those named in the Vary
-    response header. We assume the list of header names to be constant
-    for each URI throughout the lifetime of the application, and store
-    that list in self.store[uri].selecting_headers.
-    
-    The items contained in self.store[uri] have keys which are tuples of request
-    header values (in the same order as the names in its selecting_headers),
-    and values which are the actual responses.
-    """
-    
-    maxobjects = 1000
-    maxobj_size = 100000
-    maxsize = 10000000
-    delay = 600
-    antistampede_timeout = 5
-    expire_freq = 0.1
-    debug = False
-    
-    def __init__(self):
-        self.clear()
-        
-        # Run self.expire_cache in a separate daemon thread.
-        t = threading.Thread(target=self.expire_cache, name='expire_cache')
-        self.expiration_thread = t
-        if hasattr(threading.Thread, "daemon"):
-            # Python 2.6+
-            t.daemon = True
-        else:
-            t.setDaemon(True)
-        t.start()
-    
-    def clear(self):
-        """Reset the cache to its initial, empty state."""
-        self.store = {}
-        self.expirations = {}
-        self.tot_puts = 0
-        self.tot_gets = 0
-        self.tot_hist = 0
-        self.tot_expires = 0
-        self.tot_non_modified = 0
-        self.cursize = 0
-    
-    def expire_cache(self):
-        # expire_cache runs in a separate thread which the servers are
-        # not aware of. It's possible that "time" will be set to None
-        # arbitrarily, so we check "while time" to avoid exceptions.
-        # See tickets #99 and #180 for more information.
-        while time:
-            now = time.time()
-            # Must make a copy of expirations so it doesn't change size
-            # during iteration
-            for expiration_time, objects in self.expirations.items():
-                if expiration_time <= now:
-                    for obj_size, uri, sel_header_values in objects:
-                        try:
-                            del self.store[uri][sel_header_values]
-                            self.tot_expires += 1
-                            self.cursize -= obj_size
-                        except KeyError:
-                            # the key may have been deleted elsewhere
-                            pass
-                    del self.expirations[expiration_time]
-            time.sleep(self.expire_freq)
-    
-    def get(self):
-        """Return the current variant if in the cache, else None."""
-        request = cherrypy.serving.request
-        self.tot_gets += 1
-        
-        uri = cherrypy.url(qs=request.query_string)
-        uricache = self.store.get(uri)
-        if uricache is None:
-            return None
-        
-        header_values = [request.headers.get(h, '')
-                         for h in uricache.selecting_headers]
-        header_values.sort()
-        variant = uricache.wait(key=tuple(header_values),
-                                timeout=self.antistampede_timeout,
-                                debug=self.debug)
-        if variant is not None:
-            self.tot_hist += 1
-        return variant
-    
-    def put(self, variant, size):
-        """Store the current variant in the cache."""
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-        
-        uri = cherrypy.url(qs=request.query_string)
-        uricache = self.store.get(uri)
-        if uricache is None:
-            uricache = AntiStampedeCache()
-            uricache.selecting_headers = [
-                e.value for e in response.headers.elements('Vary')]
-            self.store[uri] = uricache
-        
-        if len(self.store) < self.maxobjects:
-            total_size = self.cursize + size
-            
-            # checks if there's space for the object
-            if (size < self.maxobj_size and total_size < self.maxsize):
-                # add to the expirations list
-                expiration_time = response.time + self.delay
-                bucket = self.expirations.setdefault(expiration_time, [])
-                bucket.append((size, uri, uricache.selecting_headers))
-                
-                # add to the cache
-                header_values = [request.headers.get(h, '')
-                                 for h in uricache.selecting_headers]
-                header_values.sort()
-                uricache[tuple(header_values)] = variant
-                self.tot_puts += 1
-                self.cursize = total_size
-    
-    def delete(self):
-        """Remove ALL cached variants of the current resource."""
-        uri = cherrypy.url(qs=cherrypy.serving.request.query_string)
-        self.store.pop(uri, None)
-
-
-def get(invalid_methods=("POST", "PUT", "DELETE"), debug=False, **kwargs):
-    """Try to obtain cached output. If fresh enough, raise HTTPError(304).
-    
-    If POST, PUT, or DELETE:
-        * invalidates (deletes) any cached response for this resource
-        * sets request.cached = False
-        * sets request.cacheable = False
-    
-    else if a cached copy exists:
-        * sets request.cached = True
-        * sets request.cacheable = False
-        * sets response.headers to the cached values
-        * checks the cached Last-Modified response header against the
-            current If-(Un)Modified-Since request headers; raises 304
-            if necessary.
-        * sets response.status and response.body to the cached values
-        * returns True
-    
-    otherwise:
-        * sets request.cached = False
-        * sets request.cacheable = True
-        * returns False
-    """
-    request = cherrypy.serving.request
-    response = cherrypy.serving.response
-    
-    if not hasattr(cherrypy, "_cache"):
-        # Make a process-wide Cache object.
-        cherrypy._cache = kwargs.pop("cache_class", MemoryCache)()
-        
-        # Take all remaining kwargs and set them on the Cache object.
-        for k, v in kwargs.items():
-            setattr(cherrypy._cache, k, v)
-        cherrypy._cache.debug = debug
-    
-    # POST, PUT, DELETE should invalidate (delete) the cached copy.
-    # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10.
-    if request.method in invalid_methods:
-        if debug:
-            cherrypy.log('request.method %r in invalid_methods %r' %
-                         (request.method, invalid_methods), 'TOOLS.CACHING')
-        cherrypy._cache.delete()
-        request.cached = False
-        request.cacheable = False
-        return False
-    
-    if 'no-cache' in [e.value for e in request.headers.elements('Pragma')]:
-        request.cached = False
-        request.cacheable = True
-        return False
-    
-    cache_data = cherrypy._cache.get()
-    request.cached = bool(cache_data)
-    request.cacheable = not request.cached
-    if request.cached:
-        # Serve the cached copy.
-        max_age = cherrypy._cache.delay
-        for v in [e.value for e in request.headers.elements('Cache-Control')]:
-            atoms = v.split('=', 1)
-            directive = atoms.pop(0)
-            if directive == 'max-age':
-                if len(atoms) != 1 or not atoms[0].isdigit():
-                    raise cherrypy.HTTPError(400, "Invalid Cache-Control header")
-                max_age = int(atoms[0])
-                break
-            elif directive == 'no-cache':
-                if debug:
-                    cherrypy.log('Ignoring cache due to Cache-Control: no-cache',
-                                 'TOOLS.CACHING')
-                request.cached = False
-                request.cacheable = True
-                return False
-        
-        if debug:
-            cherrypy.log('Reading response from cache', 'TOOLS.CACHING')
-        s, h, b, create_time = cache_data
-        age = int(response.time - create_time)
-        if (age > max_age):
-            if debug:
-                cherrypy.log('Ignoring cache due to age > %d' % max_age,
-                             'TOOLS.CACHING')
-            request.cached = False
-            request.cacheable = True
-            return False
-        
-        # Copy the response headers. See http://www.cherrypy.org/ticket/721.
-        response.headers = rh = httputil.HeaderMap()
-        for k in h:
-            dict.__setitem__(rh, k, dict.__getitem__(h, k))
-        
-        # Add the required Age header
-        response.headers["Age"] = str(age)
-        
-        try:
-            # Note that validate_since depends on a Last-Modified header;
-            # this was put into the cached copy, and should have been
-            # resurrected just above (response.headers = cache_data[1]).
-            cptools.validate_since()
-        except cherrypy.HTTPRedirect, x:
-            if x.status == 304:
-                cherrypy._cache.tot_non_modified += 1
-            raise
-        
-        # serve it & get out from the request
-        response.status = s
-        response.body = b
-    else:
-        if debug:
-            cherrypy.log('request is not cached', 'TOOLS.CACHING')
-    return request.cached
-
-
-def tee_output():
-    request = cherrypy.serving.request
-    if 'no-store' in request.headers.values('Cache-Control'):
-        return
-    
-    def tee(body):
-        """Tee response.body into a list."""
-        if ('no-cache' in response.headers.values('Pragma') or
-            'no-store' in response.headers.values('Cache-Control')):
-            for chunk in body:
-                yield chunk
-            return
-        
-        output = []
-        for chunk in body:
-            output.append(chunk)
-            yield chunk
-        
-        # save the cache data
-        body = ''.join(output)
-        cherrypy._cache.put((response.status, response.headers or {},
-                             body, response.time), len(body))
-    
-    response = cherrypy.serving.response
-    response.body = tee(response.body)
-
-
-def expires(secs=0, force=False, debug=False):
-    """Tool for influencing cache mechanisms using the 'Expires' header.
-    
-    'secs' must be either an int or a datetime.timedelta, and indicates the
-    number of seconds between response.time and when the response should
-    expire. The 'Expires' header will be set to (response.time + secs).
-    
-    If 'secs' is zero, the 'Expires' header is set one year in the past, and
-    the following "cache prevention" headers are also set:
-       'Pragma': 'no-cache'
-       'Cache-Control': 'no-cache, must-revalidate'
-    
-    If 'force' is False (the default), the following headers are checked:
-    'Etag', 'Last-Modified', 'Age', 'Expires'. If any are already present,
-    none of the above response headers are set.
-    """
-    
-    response = cherrypy.serving.response
-    headers = response.headers
-    
-    cacheable = False
-    if not force:
-        # some header names that indicate that the response can be cached
-        for indicator in ('Etag', 'Last-Modified', 'Age', 'Expires'):
-            if indicator in headers:
-                cacheable = True
-                break
-    
-    if not cacheable and not force:
-        if debug:
-            cherrypy.log('request is not cacheable', 'TOOLS.EXPIRES')
-    else:
-        if debug:
-            cherrypy.log('request is cacheable', 'TOOLS.EXPIRES')
-        if isinstance(secs, datetime.timedelta):
-            secs = (86400 * secs.days) + secs.seconds
-        
-        if secs == 0:
-            if force or ("Pragma" not in headers):
-                headers["Pragma"] = "no-cache"
-            if cherrypy.serving.request.protocol >= (1, 1):
-                if force or "Cache-Control" not in headers:
-                    headers["Cache-Control"] = "no-cache, must-revalidate"
-            # Set an explicit Expires date in the past.
-            expiry = httputil.HTTPDate(1169942400.0)
-        else:
-            expiry = httputil.HTTPDate(response.time + secs)
-        if force or "Expires" not in headers:
-            headers["Expires"] = expiry
--- a/bundled/cherrypy/cherrypy/lib/covercp.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,364 +0,0 @@
-"""Code-coverage tools for CherryPy.
-
-To use this module, or the coverage tools in the test suite,
-you need to download 'coverage.py', either Gareth Rees' original
-implementation:
-http://www.garethrees.org/2001/12/04/python-coverage/
-
-or Ned Batchelder's enhanced version:
-http://www.nedbatchelder.com/code/modules/coverage.html
-
-To turn on coverage tracing, use the following code:
-
-    cherrypy.engine.subscribe('start', covercp.start)
-
-DO NOT subscribe anything on the 'start_thread' channel, as previously
-recommended. Calling start once in the main thread should be sufficient
-to start coverage on all threads. Calling start again in each thread
-effectively clears any coverage data gathered up to that point.
-
-Run your code, then use the covercp.serve() function to browse the
-results in a web browser. If you run this module from the command line,
-it will call serve() for you.
-"""
-
-import re
-import sys
-import cgi
-from urllib import quote_plus
-import os, os.path
-localFile = os.path.join(os.path.dirname(__file__), "coverage.cache")
-
-try:
-    from coverage import the_coverage as coverage
-    def start():
-        coverage.start()
-except ImportError:
-    # Setting coverage to None will raise errors
-    # that need to be trapped downstream.
-    coverage = None
-    
-    import warnings
-    warnings.warn("No code coverage will be performed; coverage.py could not be imported.")
-    
-    def start():
-        pass
-start.priority = 20
-
-TEMPLATE_MENU = """<html>
-<head>
-    <title>CherryPy Coverage Menu</title>
-    <style>
-        body {font: 9pt Arial, serif;}
-        #tree {
-            font-size: 8pt;
-            font-family: Andale Mono, monospace;
-            white-space: pre;
-            }
-        #tree a:active, a:focus {
-            background-color: black;
-            padding: 1px;
-            color: white;
-            border: 0px solid #9999FF;
-            -moz-outline-style: none;
-            }
-        .fail { color: red;}
-        .pass { color: #888;}
-        #pct { text-align: right;}
-        h3 {
-            font-size: small;
-            font-weight: bold;
-            font-style: italic;
-            margin-top: 5px; 
-            }
-        input { border: 1px solid #ccc; padding: 2px; }
-        .directory {
-            color: #933;
-            font-style: italic;
-            font-weight: bold;
-            font-size: 10pt;
-            }
-        .file {
-            color: #400;
-            }
-        a { text-decoration: none; }
-        #crumbs {
-            color: white;
-            font-size: 8pt;
-            font-family: Andale Mono, monospace;
-            width: 100%;
-            background-color: black;
-            }
-        #crumbs a {
-            color: #f88;
-            }
-        #options {
-            line-height: 2.3em;
-            border: 1px solid black;
-            background-color: #eee;
-            padding: 4px;
-            }
-        #exclude {
-            width: 100%;
-            margin-bottom: 3px;
-            border: 1px solid #999;
-            }
-        #submit {
-            background-color: black;
-            color: white;
-            border: 0;
-            margin-bottom: -9px;
-            }
-    </style>
-</head>
-<body>
-<h2>CherryPy Coverage</h2>"""
-
-TEMPLATE_FORM = """
-<div id="options">
-<form action='menu' method=GET>
-    <input type='hidden' name='base' value='%(base)s' />
-    Show percentages <input type='checkbox' %(showpct)s name='showpct' value='checked' /><br />
-    Hide files over <input type='text' id='pct' name='pct' value='%(pct)s' size='3' />%%<br />
-    Exclude files matching<br />
-    <input type='text' id='exclude' name='exclude' value='%(exclude)s' size='20' />
-    <br />
-
-    <input type='submit' value='Change view' id="submit"/>
-</form>
-</div>""" 
-
-TEMPLATE_FRAMESET = """<html>
-<head><title>CherryPy coverage data</title></head>
-<frameset cols='250, 1*'>
-    <frame src='menu?base=%s' />
-    <frame name='main' src='' />
-</frameset>
-</html>
-"""
-
-TEMPLATE_COVERAGE = """<html>
-<head>
-    <title>Coverage for %(name)s</title>
-    <style>
-        h2 { margin-bottom: .25em; }
-        p { margin: .25em; }
-        .covered { color: #000; background-color: #fff; }
-        .notcovered { color: #fee; background-color: #500; }
-        .excluded { color: #00f; background-color: #fff; }
-         table .covered, table .notcovered, table .excluded
-             { font-family: Andale Mono, monospace;
-               font-size: 10pt; white-space: pre; }
-
-         .lineno { background-color: #eee;}
-         .notcovered .lineno { background-color: #000;}
-         table { border-collapse: collapse;
-    </style>
-</head>
-<body>
-<h2>%(name)s</h2>
-<p>%(fullpath)s</p>
-<p>Coverage: %(pc)s%%</p>"""
-
-TEMPLATE_LOC_COVERED = """<tr class="covered">
-    <td class="lineno">%s&nbsp;</td>
-    <td>%s</td>
-</tr>\n"""
-TEMPLATE_LOC_NOT_COVERED = """<tr class="notcovered">
-    <td class="lineno">%s&nbsp;</td>
-    <td>%s</td>
-</tr>\n"""
-TEMPLATE_LOC_EXCLUDED = """<tr class="excluded">
-    <td class="lineno">%s&nbsp;</td>
-    <td>%s</td>
-</tr>\n"""
-
-TEMPLATE_ITEM = "%s%s<a class='file' href='report?name=%s' target='main'>%s</a>\n"
-
-def _percent(statements, missing):
-    s = len(statements)
-    e = s - len(missing)
-    if s > 0:
-        return int(round(100.0 * e / s))
-    return 0
-
-def _show_branch(root, base, path, pct=0, showpct=False, exclude=""):
-    
-    # Show the directory name and any of our children
-    dirs = [k for k, v in root.items() if v]
-    dirs.sort()
-    for name in dirs:
-        newpath = os.path.join(path, name)
-        
-        if newpath.lower().startswith(base):
-            relpath = newpath[len(base):]
-            yield "| " * relpath.count(os.sep)
-            yield "<a class='directory' href='menu?base=%s&exclude=%s'>%s</a>\n" % \
-                   (newpath, quote_plus(exclude), name)
-        
-        for chunk in _show_branch(root[name], base, newpath, pct, showpct, exclude):
-            yield chunk
-    
-    # Now list the files
-    if path.lower().startswith(base):
-        relpath = path[len(base):]
-        files = [k for k, v in root.items() if not v]
-        files.sort()
-        for name in files:
-            newpath = os.path.join(path, name)
-            
-            pc_str = ""
-            if showpct:
-                try:
-                    _, statements, _, missing, _ = coverage.analysis2(newpath)
-                except:
-                    # Yes, we really want to pass on all errors.
-                    pass
-                else:
-                    pc = _percent(statements, missing)
-                    pc_str = ("%3d%% " % pc).replace(' ','&nbsp;')
-                    if pc < float(pct) or pc == -1:
-                        pc_str = "<span class='fail'>%s</span>" % pc_str
-                    else:
-                        pc_str = "<span class='pass'>%s</span>" % pc_str
-            
-            yield TEMPLATE_ITEM % ("| " * (relpath.count(os.sep) + 1),
-                                   pc_str, newpath, name)
-
-def _skip_file(path, exclude):
-    if exclude:
-        return bool(re.search(exclude, path))
-
-def _graft(path, tree):
-    d = tree
-    
-    p = path
-    atoms = []
-    while True:
-        p, tail = os.path.split(p)
-        if not tail:
-            break
-        atoms.append(tail)
-    atoms.append(p)
-    if p != "/":
-        atoms.append("/")
-    
-    atoms.reverse()
-    for node in atoms:
-        if node:
-            d = d.setdefault(node, {})
-
-def get_tree(base, exclude):
-    """Return covered module names as a nested dict."""
-    tree = {}
-    coverage.get_ready()
-    runs = list(coverage.cexecuted.keys())
-    if runs:
-        for path in runs:
-            if not _skip_file(path, exclude) and not os.path.isdir(path):
-                _graft(path, tree)
-    return tree
-
-class CoverStats(object):
-    
-    def __init__(self, root=None):
-        if root is None:
-            # Guess initial depth. Files outside this path will not be
-            # reachable from the web interface.
-            import cherrypy
-            root = os.path.dirname(cherrypy.__file__)
-        self.root = root
-    
-    def index(self):
-        return TEMPLATE_FRAMESET % self.root.lower()
-    index.exposed = True
-    
-    def menu(self, base="/", pct="50", showpct="",
-             exclude=r'python\d\.\d|test|tut\d|tutorial'):
-        
-        # The coverage module uses all-lower-case names.
-        base = base.lower().rstrip(os.sep)
-        
-        yield TEMPLATE_MENU
-        yield TEMPLATE_FORM % locals()
-        
-        # Start by showing links for parent paths
-        yield "<div id='crumbs'>"
-        path = ""
-        atoms = base.split(os.sep)
-        atoms.pop()
-        for atom in atoms:
-            path += atom + os.sep
-            yield ("<a href='menu?base=%s&exclude=%s'>%s</a> %s"
-                   % (path, quote_plus(exclude), atom, os.sep))
-        yield "</div>"
-        
-        yield "<div id='tree'>"
-        
-        # Then display the tree
-        tree = get_tree(base, exclude)
-        if not tree:
-            yield "<p>No modules covered.</p>"
-        else:
-            for chunk in _show_branch(tree, base, "/", pct,
-                                      showpct=='checked', exclude):
-                yield chunk
-        
-        yield "</div>"
-        yield "</body></html>"
-    menu.exposed = True
-    
-    def annotated_file(self, filename, statements, excluded, missing):
-        source = open(filename, 'r')
-        buffer = []
-        for lineno, line in enumerate(source.readlines()):
-            lineno += 1
-            line = line.strip("\n\r")
-            empty_the_buffer = True
-            if lineno in excluded:
-                template = TEMPLATE_LOC_EXCLUDED
-            elif lineno in missing:
-                template = TEMPLATE_LOC_NOT_COVERED
-            elif lineno in statements:
-                template = TEMPLATE_LOC_COVERED
-            else:
-                empty_the_buffer = False
-                buffer.append((lineno, line))
-            if empty_the_buffer:
-                for lno, pastline in buffer:
-                    yield template % (lno, cgi.escape(pastline))
-                buffer = []
-                yield template % (lineno, cgi.escape(line))
-    
-    def report(self, name):
-        coverage.get_ready()
-        filename, statements, excluded, missing, _ = coverage.analysis2(name)
-        pc = _percent(statements, missing)
-        yield TEMPLATE_COVERAGE % dict(name=os.path.basename(name),
-                                       fullpath=name,
-                                       pc=pc)
-        yield '<table>\n'
-        for line in self.annotated_file(filename, statements, excluded,
-                                        missing):
-            yield line
-        yield '</table>'
-        yield '</body>'
-        yield '</html>'
-    report.exposed = True
-
-
-def serve(path=localFile, port=8080, root=None):
-    if coverage is None:
-        raise ImportError("The coverage module could not be imported.")
-    coverage.cache_default = path
-    
-    import cherrypy
-    cherrypy.config.update({'server.socket_port': int(port),
-                            'server.thread_pool': 10,
-                            'environment': "production",
-                            })
-    cherrypy.quickstart(CoverStats(root))
-
-if __name__ == "__main__":
-    serve(*tuple(sys.argv[1:]))
-
--- a/bundled/cherrypy/cherrypy/lib/cptools.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,580 +0,0 @@
-"""Functions for builtin CherryPy tools."""
-
-import logging
-try:
-    # Python 2.5+
-    from hashlib import md5
-except ImportError:
-    from md5 import new as md5
-import re
-
-try:
-    set
-except NameError:
-    from sets import Set as set
-
-import cherrypy
-from cherrypy.lib import httputil as _httputil
-
-
-#                     Conditional HTTP request support                     #
-
-def validate_etags(autotags=False, debug=False):
-    """Validate the current ETag against If-Match, If-None-Match headers.
-    
-    If autotags is True, an ETag response-header value will be provided
-    from an MD5 hash of the response body (unless some other code has
-    already provided an ETag header). If False (the default), the ETag
-    will not be automatic.
-    
-    WARNING: the autotags feature is not designed for URL's which allow
-    methods other than GET. For example, if a POST to the same URL returns
-    no content, the automatic ETag will be incorrect, breaking a fundamental
-    use for entity tags in a possibly destructive fashion. Likewise, if you
-    raise 304 Not Modified, the response body will be empty, the ETag hash
-    will be incorrect, and your application will break.
-    See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
-    """
-    response = cherrypy.serving.response
-    
-    # Guard against being run twice.
-    if hasattr(response, "ETag"):
-        return
-    
-    status, reason, msg = _httputil.valid_status(response.status)
-    
-    etag = response.headers.get('ETag')
-    
-    # Automatic ETag generation. See warning in docstring.
-    if etag:
-        if debug:
-            cherrypy.log('ETag already set: %s' % etag, 'TOOLS.ETAGS')
-    elif not autotags:
-        if debug:
-            cherrypy.log('Autotags off', 'TOOLS.ETAGS')
-    elif status != 200:
-        if debug:
-            cherrypy.log('Status not 200', 'TOOLS.ETAGS')
-    else:
-        etag = response.collapse_body()
-        etag = '"%s"' % md5(etag).hexdigest()
-        if debug:
-            cherrypy.log('Setting ETag: %s' % etag, 'TOOLS.ETAGS')
-        response.headers['ETag'] = etag
-    
-    response.ETag = etag
-    
-    # "If the request would, without the If-Match header field, result in
-    # anything other than a 2xx or 412 status, then the If-Match header
-    # MUST be ignored."
-    if debug:
-        cherrypy.log('Status: %s' % status, 'TOOLS.ETAGS')
-    if status >= 200 and status <= 299:
-        request = cherrypy.serving.request
-        
-        conditions = request.headers.elements('If-Match') or []
-        conditions = [str(x) for x in conditions]
-        if debug:
-            cherrypy.log('If-Match conditions: %s' % repr(conditions),
-                         'TOOLS.ETAGS')
-        if conditions and not (conditions == ["*"] or etag in conditions):
-            raise cherrypy.HTTPError(412, "If-Match failed: ETag %r did "
-                                     "not match %r" % (etag, conditions))
-        
-        conditions = request.headers.elements('If-None-Match') or []
-        conditions = [str(x) for x in conditions]
-        if debug:
-            cherrypy.log('If-None-Match conditions: %s' % repr(conditions),
-                         'TOOLS.ETAGS')
-        if conditions == ["*"] or etag in conditions:
-            if debug:
-                cherrypy.log('request.method: %s' % request.method, 'TOOLS.ETAGS')
-            if request.method in ("GET", "HEAD"):
-                raise cherrypy.HTTPRedirect([], 304)
-            else:
-                raise cherrypy.HTTPError(412, "If-None-Match failed: ETag %r "
-                                         "matched %r" % (etag, conditions))
-
-def validate_since():
-    """Validate the current Last-Modified against If-Modified-Since headers.
-    
-    If no code has set the Last-Modified response header, then no validation
-    will be performed.
-    """
-    response = cherrypy.serving.response
-    lastmod = response.headers.get('Last-Modified')
-    if lastmod:
-        status, reason, msg = _httputil.valid_status(response.status)
-        
-        request = cherrypy.serving.request
-        
-        since = request.headers.get('If-Unmodified-Since')
-        if since and since != lastmod:
-            if (status >= 200 and status <= 299) or status == 412:
-                raise cherrypy.HTTPError(412)
-        
-        since = request.headers.get('If-Modified-Since')
-        if since and since == lastmod:
-            if (status >= 200 and status <= 299) or status == 304:
-                if request.method in ("GET", "HEAD"):
-                    raise cherrypy.HTTPRedirect([], 304)
-                else:
-                    raise cherrypy.HTTPError(412)
-
-
-#                                Tool code                                #
-
-def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For',
-          scheme='X-Forwarded-Proto', debug=False):
-    """Change the base URL (scheme://host[:port][/path]).
-    
-    For running a CP server behind Apache, lighttpd, or other HTTP server.
-    
-    If you want the new request.base to include path info (not just the host),
-    you must explicitly set base to the full base path, and ALSO set 'local'
-    to '', so that the X-Forwarded-Host request header (which never includes
-    path info) does not override it. Regardless, the value for 'base' MUST
-    NOT end in a slash.
-    
-    cherrypy.request.remote.ip (the IP address of the client) will be
-    rewritten if the header specified by the 'remote' arg is valid.
-    By default, 'remote' is set to 'X-Forwarded-For'. If you do not
-    want to rewrite remote.ip, set the 'remote' arg to an empty string.
-    """
-    
-    request = cherrypy.serving.request
-    
-    if scheme:
-        s = request.headers.get(scheme, None)
-        if debug:
-            cherrypy.log('Testing scheme %r:%r' % (scheme, s), 'TOOLS.PROXY')
-        if s == 'on' and 'ssl' in scheme.lower():
-            # This handles e.g. webfaction's 'X-Forwarded-Ssl: on' header
-            scheme = 'https'
-        else:
-            # This is for lighttpd/pound/Mongrel's 'X-Forwarded-Proto: https'
-            scheme = s
-    if not scheme:
-        scheme = request.base[:request.base.find("://")]
-    
-    if local:
-        lbase = request.headers.get(local, None)
-        if debug:
-            cherrypy.log('Testing local %r:%r' % (local, lbase), 'TOOLS.PROXY')
-        if lbase is not None:
-            base = lbase.split(',')[0]
-    if not base:
-        port = request.local.port
-        if port == 80:
-            base = '127.0.0.1'
-        else:
-            base = '127.0.0.1:%s' % port
-    
-    if base.find("://") == -1:
-        # add http:// or https:// if needed
-        base = scheme + "://" + base
-    
-    request.base = base
-    
-    if remote:
-        xff = request.headers.get(remote)
-        if debug:
-            cherrypy.log('Testing remote %r:%r' % (remote, xff), 'TOOLS.PROXY')
-        if xff:
-            if remote == 'X-Forwarded-For':
-                # See http://bob.pythonmac.org/archives/2005/09/23/apache-x-forwarded-for-caveat/
-                xff = xff.split(',')[-1].strip()
-            request.remote.ip = xff
-
-
-def ignore_headers(headers=('Range',), debug=False):
-    """Delete request headers whose field names are included in 'headers'.
-    
-    This is a useful tool for working behind certain HTTP servers;
-    for example, Apache duplicates the work that CP does for 'Range'
-    headers, and will doubly-truncate the response.
-    """
-    request = cherrypy.serving.request
-    for name in headers:
-        if name in request.headers:
-            if debug:
-                cherrypy.log('Ignoring request header %r' % name,
-                             'TOOLS.IGNORE_HEADERS')
-            del request.headers[name]
-
-
-def response_headers(headers=None, debug=False):
-    """Set headers on the response."""
-    if debug:
-        cherrypy.log('Setting response headers: %s' % repr(headers),
-                     'TOOLS.RESPONSE_HEADERS')
-    for name, value in (headers or []):
-        cherrypy.serving.response.headers[name] = value
-response_headers.failsafe = True
-
-
-def referer(pattern, accept=True, accept_missing=False, error=403,
-            message='Forbidden Referer header.', debug=False):
-    """Raise HTTPError if Referer header does/does not match the given pattern.
-    
-    pattern: a regular expression pattern to test against the Referer.
-    accept: if True, the Referer must match the pattern; if False,
-        the Referer must NOT match the pattern.
-    accept_missing: if True, permit requests with no Referer header.
-    error: the HTTP error code to return to the client on failure.
-    message: a string to include in the response body on failure.
-    """
-    try:
-        ref = cherrypy.serving.request.headers['Referer']
-        match = bool(re.match(pattern, ref))
-        if debug:
-            cherrypy.log('Referer %r matches %r' % (ref, pattern),
-                         'TOOLS.REFERER')
-        if accept == match:
-            return
-    except KeyError:
-        if debug:
-            cherrypy.log('No Referer header', 'TOOLS.REFERER')
-        if accept_missing:
-            return
-    
-    raise cherrypy.HTTPError(error, message)
-
-
-class SessionAuth(object):
-    """Assert that the user is logged in."""
-    
-    session_key = "username"
-    debug = False
-    
-    def check_username_and_password(self, username, password):
-        pass
-    
-    def anonymous(self):
-        """Provide a temporary user name for anonymous users."""
-        pass
-    
-    def on_login(self, username):
-        pass
-    
-    def on_logout(self, username):
-        pass
-    
-    def on_check(self, username):
-        pass
-    
-    def login_screen(self, from_page='..', username='', error_msg='', **kwargs):
-        return """<html><body>
-Message: %(error_msg)s
-<form method="post" action="do_login">
-    Login: <input type="text" name="username" value="%(username)s" size="10" /><br />
-    Password: <input type="password" name="password" size="10" /><br />
-    <input type="hidden" name="from_page" value="%(from_page)s" /><br />
-    <input type="submit" />
-</form>
-</body></html>""" % {'from_page': from_page, 'username': username,
-                     'error_msg': error_msg}
-    
-    def do_login(self, username, password, from_page='..', **kwargs):
-        """Login. May raise redirect, or return True if request handled."""
-        response = cherrypy.serving.response
-        error_msg = self.check_username_and_password(username, password)
-        if error_msg:
-            body = self.login_screen(from_page, username, error_msg)
-            response.body = body
-            if "Content-Length" in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                del response.headers["Content-Length"]
-            return True
-        else:
-            cherrypy.serving.request.login = username
-            cherrypy.session[self.session_key] = username
-            self.on_login(username)
-            raise cherrypy.HTTPRedirect(from_page or "/")
-    
-    def do_logout(self, from_page='..', **kwargs):
-        """Logout. May raise redirect, or return True if request handled."""
-        sess = cherrypy.session
-        username = sess.get(self.session_key)
-        sess[self.session_key] = None
-        if username:
-            cherrypy.serving.request.login = None
-            self.on_logout(username)
-        raise cherrypy.HTTPRedirect(from_page)
-    
-    def do_check(self):
-        """Assert username. May raise redirect, or return True if request handled."""
-        sess = cherrypy.session
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-        
-        username = sess.get(self.session_key)
-        if not username:
-            sess[self.session_key] = username = self.anonymous()
-            if self.debug:
-                cherrypy.log('No session[username], trying anonymous', 'TOOLS.SESSAUTH')
-        if not username:
-            url = cherrypy.url(qs=request.query_string)
-            if self.debug:
-                cherrypy.log('No username, routing to login_screen with '
-                             'from_page %r' % url, 'TOOLS.SESSAUTH')
-            response.body = self.login_screen(url)
-            if "Content-Length" in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                del response.headers["Content-Length"]
-            return True
-        if self.debug:
-            cherrypy.log('Setting request.login to %r' % username, 'TOOLS.SESSAUTH')
-        request.login = username
-        self.on_check(username)
-    
-    def run(self):
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-        
-        path = request.path_info
-        if path.endswith('login_screen'):
-            if self.debug:
-                cherrypy.log('routing %r to login_screen' % path, 'TOOLS.SESSAUTH')
-            return self.login_screen(**request.params)
-        elif path.endswith('do_login'):
-            if request.method != 'POST':
-                response.headers['Allow'] = "POST"
-                if self.debug:
-                    cherrypy.log('do_login requires POST', 'TOOLS.SESSAUTH')
-                raise cherrypy.HTTPError(405)
-            if self.debug:
-                cherrypy.log('routing %r to do_login' % path, 'TOOLS.SESSAUTH')
-            return self.do_login(**request.params)
-        elif path.endswith('do_logout'):
-            if request.method != 'POST':
-                response.headers['Allow'] = "POST"
-                raise cherrypy.HTTPError(405)
-            if self.debug:
-                cherrypy.log('routing %r to do_logout' % path, 'TOOLS.SESSAUTH')
-            return self.do_logout(**request.params)
-        else:
-            if self.debug:
-                cherrypy.log('No special path, running do_check', 'TOOLS.SESSAUTH')
-            return self.do_check()
-
-
-def session_auth(**kwargs):
-    sa = SessionAuth()
-    for k, v in kwargs.items():
-        setattr(sa, k, v)
-    return sa.run()
-session_auth.__doc__ = """Session authentication hook.
-
-Any attribute of the SessionAuth class may be overridden via a keyword arg
-to this function:
-
-""" + "\n".join(["%s: %s" % (k, type(getattr(SessionAuth, k)).__name__)
-                 for k in dir(SessionAuth) if not k.startswith("__")])
-
-
-def log_traceback(severity=logging.ERROR, debug=False):
-    """Write the last error's traceback to the cherrypy error log."""
-    cherrypy.log("", "HTTP", severity=severity, traceback=True)
-
-def log_request_headers(debug=False):
-    """Write request headers to the cherrypy error log."""
-    h = ["  %s: %s" % (k, v) for k, v in cherrypy.serving.request.header_list]
-    cherrypy.log('\nRequest Headers:\n' + '\n'.join(h), "HTTP")
-
-def log_hooks(debug=False):
-    """Write request.hooks to the cherrypy error log."""
-    request = cherrypy.serving.request
-    
-    msg = []
-    # Sort by the standard points if possible.
-    from cherrypy import _cprequest
-    points = _cprequest.hookpoints
-    for k in request.hooks.keys():
-        if k not in points:
-            points.append(k)
-    
-    for k in points:
-        msg.append("    %s:" % k)
-        v = request.hooks.get(k, [])
-        v.sort()
-        for h in v:
-            msg.append("        %r" % h)
-    cherrypy.log('\nRequest Hooks for ' + cherrypy.url() +
-                 ':\n' + '\n'.join(msg), "HTTP")
-
-def redirect(url='', internal=True, debug=False):
-    """Raise InternalRedirect or HTTPRedirect to the given url."""
-    if debug:
-        cherrypy.log('Redirecting %sto: %s' %
-                     ({True: 'internal ', False: ''}[internal], url),
-                     'TOOLS.REDIRECT')
-    if internal:
-        raise cherrypy.InternalRedirect(url)
-    else:
-        raise cherrypy.HTTPRedirect(url)
-
-def trailing_slash(missing=True, extra=False, status=None, debug=False):
-    """Redirect if path_info has (missing|extra) trailing slash."""
-    request = cherrypy.serving.request
-    pi = request.path_info
-    
-    if debug:
-        cherrypy.log('is_index: %r, missing: %r, extra: %r, path_info: %r' %
-                     (request.is_index, missing, extra, pi),
-                     'TOOLS.TRAILING_SLASH')
-    if request.is_index is True:
-        if missing:
-            if not pi.endswith('/'):
-                new_url = cherrypy.url(pi + '/', request.query_string)
-                raise cherrypy.HTTPRedirect(new_url, status=status or 301)
-    elif request.is_index is False:
-        if extra:
-            # If pi == '/', don't redirect to ''!
-            if pi.endswith('/') and pi != '/':
-                new_url = cherrypy.url(pi[:-1], request.query_string)
-                raise cherrypy.HTTPRedirect(new_url, status=status or 301)
-
-def flatten(debug=False):
-    """Wrap response.body in a generator that recursively iterates over body.
-    
-    This allows cherrypy.response.body to consist of 'nested generators';
-    that is, a set of generators that yield generators.
-    """
-    import types
-    def flattener(input):
-        numchunks = 0
-        for x in input:
-            if not isinstance(x, types.GeneratorType):
-                numchunks += 1
-                yield x
-            else:
-                for y in flattener(x):
-                    numchunks += 1
-                    yield y
-        if debug:
-            cherrypy.log('Flattened %d chunks' % numchunks, 'TOOLS.FLATTEN')
-    response = cherrypy.serving.response
-    response.body = flattener(response.body)
-
-
-def accept(media=None, debug=False):
-    """Return the client's preferred media-type (from the given Content-Types).
-    
-    If 'media' is None (the default), no test will be performed.
-    
-    If 'media' is provided, it should be the Content-Type value (as a string)
-    or values (as a list or tuple of strings) which the current resource
-    can emit. The client's acceptable media ranges (as declared in the
-    Accept request header) will be matched in order to these Content-Type
-    values; the first such string is returned. That is, the return value
-    will always be one of the strings provided in the 'media' arg (or None
-    if 'media' is None).
-    
-    If no match is found, then HTTPError 406 (Not Acceptable) is raised.
-    Note that most web browsers send */* as a (low-quality) acceptable
-    media range, which should match any Content-Type. In addition, "...if
-    no Accept header field is present, then it is assumed that the client
-    accepts all media types."
-    
-    Matching types are checked in order of client preference first,
-    and then in the order of the given 'media' values.
-    
-    Note that this function does not honor accept-params (other than "q").
-    """
-    if not media:
-        return
-    if isinstance(media, basestring):
-        media = [media]
-    request = cherrypy.serving.request
-    
-    # Parse the Accept request header, and try to match one
-    # of the requested media-ranges (in order of preference).
-    ranges = request.headers.elements('Accept')
-    if not ranges:
-        # Any media type is acceptable.
-        if debug:
-            cherrypy.log('No Accept header elements', 'TOOLS.ACCEPT')
-        return media[0]
-    else:
-        # Note that 'ranges' is sorted in order of preference
-        for element in ranges:
-            if element.qvalue > 0:
-                if element.value == "*/*":
-                    # Matches any type or subtype
-                    if debug:
-                        cherrypy.log('Match due to */*', 'TOOLS.ACCEPT')
-                    return media[0]
-                elif element.value.endswith("/*"):
-                    # Matches any subtype
-                    mtype = element.value[:-1]  # Keep the slash
-                    for m in media:
-                        if m.startswith(mtype):
-                            if debug:
-                                cherrypy.log('Match due to %s' % element.value,
-                                             'TOOLS.ACCEPT')
-                            return m
-                else:
-                    # Matches exact value
-                    if element.value in media:
-                        if debug:
-                            cherrypy.log('Match due to %s' % element.value,
-                                         'TOOLS.ACCEPT')
-                        return element.value
-    
-    # No suitable media-range found.
-    ah = request.headers.get('Accept')
-    if ah is None:
-        msg = "Your client did not send an Accept header."
-    else:
-        msg = "Your client sent this Accept header: %s." % ah
-    msg += (" But this resource only emits these media types: %s." %
-            ", ".join(media))
-    raise cherrypy.HTTPError(406, msg)
-
-
-class MonitoredHeaderMap(_httputil.HeaderMap):
-    
-    def __init__(self):
-        self.accessed_headers = set()
-    
-    def __getitem__(self, key):
-        self.accessed_headers.add(key)
-        return _httputil.HeaderMap.__getitem__(self, key)
-    
-    def __contains__(self, key):
-        self.accessed_headers.add(key)
-        return _httputil.HeaderMap.__contains__(self, key)
-    
-    def get(self, key, default=None):
-        self.accessed_headers.add(key)
-        return _httputil.HeaderMap.get(self, key, default=default)
-    
-    def has_key(self, key):
-        self.accessed_headers.add(key)
-        return _httputil.HeaderMap.has_key(self, key)
-
-
-def autovary(ignore=None, debug=False):
-    """Auto-populate the Vary response header based on request.header access."""
-    request = cherrypy.serving.request
-    
-    req_h = request.headers
-    request.headers = MonitoredHeaderMap()
-    request.headers.update(req_h)
-    if ignore is None:
-        ignore = set(['Content-Disposition', 'Content-Length', 'Content-Type'])
-    
-    def set_response_header():
-        resp_h = cherrypy.serving.response.headers
-        v = set([e.value for e in resp_h.elements('Vary')])
-        if debug:
-            cherrypy.log('Accessed headers: %s' % request.headers.accessed_headers,
-                         'TOOLS.AUTOVARY')
-        v = v.union(request.headers.accessed_headers)
-        v = v.difference(ignore)
-        v = list(v)
-        v.sort()
-        resp_h['Vary'] = ', '.join(v)
-    request.hooks.attach('before_finalize', set_response_header, 95)
-
--- a/bundled/cherrypy/cherrypy/lib/encoding.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,362 +0,0 @@
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-try:
-    set
-except NameError:
-    from sets import Set as set
-import struct
-import time
-import types
-
-import cherrypy
-from cherrypy.lib import file_generator
-from cherrypy.lib import set_vary_header
-
-
-def decode(encoding=None, default_encoding='utf-8'):
-    """Replace or extend the list of charsets used to decode a request entity.
-    
-    Either argument may be a single string or a list of strings.
-    
-    encoding: If not None, restricts the set of charsets attempted while decoding
-    a request entity to the given set (even if a different charset is given in
-    the Content-Type request header).
-    
-    default_encoding: Only in effect if the 'encoding' argument is not given.
-    If given, the set of charsets attempted while decoding a request entity is
-    *extended* with the given value(s).
-    """
-    body = cherrypy.request.body
-    if encoding is not None:
-        if not isinstance(encoding, list):
-            encoding = [encoding]
-        body.attempt_charsets = encoding
-    elif default_encoding:
-        if not isinstance(default_encoding, list):
-            default_encoding = [default_encoding]
-        body.attempt_charsets = body.attempt_charsets + default_encoding
-
-
-class ResponseEncoder:
-    
-    default_encoding = 'utf-8'
-    failmsg = "Response body could not be encoded with %r."
-    encoding = None
-    errors = 'strict'
-    text_only = True
-    add_charset = True
-    debug = False
-    
-    def __init__(self, **kwargs):
-        for k, v in kwargs.items():
-            setattr(self, k, v)
-        
-        self.attempted_charsets = set()
-        request = cherrypy.serving.request
-        if request.handler is not None:
-            # Replace request.handler with self
-            if self.debug:
-                cherrypy.log('Replacing request.handler', 'TOOLS.ENCODE')
-            self.oldhandler = request.handler
-            request.handler = self
-    
-    def encode_stream(self, encoding):
-        """Encode a streaming response body.
-        
-        Use a generator wrapper, and just pray it works as the stream is
-        being written out.
-        """
-        if encoding in self.attempted_charsets:
-            return False
-        self.attempted_charsets.add(encoding)
-        
-        def encoder(body):
-            for chunk in body:
-                if isinstance(chunk, unicode):
-                    chunk = chunk.encode(encoding, self.errors)
-                yield chunk
-        self.body = encoder(self.body)
-        return True
-    
-    def encode_string(self, encoding):
-        """Encode a buffered response body."""
-        if encoding in self.attempted_charsets:
-            return False
-        self.attempted_charsets.add(encoding)
-        
-        try:
-            body = []
-            for chunk in self.body:
-                if isinstance(chunk, unicode):
-                    chunk = chunk.encode(encoding, self.errors)
-                body.append(chunk)
-            self.body = body
-        except (LookupError, UnicodeError):
-            return False
-        else:
-            return True
-    
-    def find_acceptable_charset(self):
-        request = cherrypy.serving.request
-        response = cherrypy.serving.response
-        
-        if self.debug:
-            cherrypy.log('response.stream %r' % response.stream, 'TOOLS.ENCODE')
-        if response.stream:
-            encoder = self.encode_stream
-        else:
-            encoder = self.encode_string
-            if "Content-Length" in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                # Encoded strings may be of different lengths from their
-                # unicode equivalents, and even from each other. For example:
-                # >>> t = u"\u7007\u3040"
-                # >>> len(t)
-                # 2
-                # >>> len(t.encode("UTF-8"))
-                # 6
-                # >>> len(t.encode("utf7"))
-                # 8
-                del response.headers["Content-Length"]
-        
-        # Parse the Accept-Charset request header, and try to provide one
-        # of the requested charsets (in order of user preference).
-        encs = request.headers.elements('Accept-Charset')
-        charsets = [enc.value.lower() for enc in encs]
-        if self.debug:
-            cherrypy.log('charsets %s' % repr(charsets), 'TOOLS.ENCODE')
-        
-        if self.encoding is not None:
-            # If specified, force this encoding to be used, or fail.
-            encoding = self.encoding.lower()
-            if self.debug:
-                cherrypy.log('Specified encoding %r' % encoding, 'TOOLS.ENCODE')
-            if (not charsets) or "*" in charsets or encoding in charsets:
-                if self.debug:
-                    cherrypy.log('Attempting encoding %r' % encoding, 'TOOLS.ENCODE')
-                if encoder(encoding):
-                    return encoding
-        else:
-            if not encs:
-                if self.debug:
-                    cherrypy.log('Attempting default encoding %r' %
-                                 self.default_encoding, 'TOOLS.ENCODE')
-                # Any character-set is acceptable.
-                if encoder(self.default_encoding):
-                    return self.default_encoding
-                else:
-                    raise cherrypy.HTTPError(500, self.failmsg % self.default_encoding)
-            else:
-                if "*" not in charsets:
-                    # If no "*" is present in an Accept-Charset field, then all
-                    # character sets not explicitly mentioned get a quality
-                    # value of 0, except for ISO-8859-1, which gets a quality
-                    # value of 1 if not explicitly mentioned.
-                    iso = 'iso-8859-1'
-                    if iso not in charsets:
-                        if self.debug:
-                            cherrypy.log('Attempting ISO-8859-1 encoding',
-                                         'TOOLS.ENCODE')
-                        if encoder(iso):
-                            return iso
-                
-                for element in encs:
-                    if element.qvalue > 0:
-                        if element.value == "*":
-                            # Matches any charset. Try our default.
-                            if self.debug:
-                                cherrypy.log('Attempting default encoding due '
-                                             'to %r' % element, 'TOOLS.ENCODE')
-                            if encoder(self.default_encoding):
-                                return self.default_encoding
-                        else:
-                            encoding = element.value
-                            if self.debug:
-                                cherrypy.log('Attempting encoding %r (qvalue >'
-                                             '0)' % element, 'TOOLS.ENCODE')
-                            if encoder(encoding):
-                                return encoding
-        
-        # No suitable encoding found.
-        ac = request.headers.get('Accept-Charset')
-        if ac is None:
-            msg = "Your client did not send an Accept-Charset header."
-        else:
-            msg = "Your client sent this Accept-Charset header: %s." % ac
-        msg += " We tried these charsets: %s." % ", ".join(self.attempted_charsets)
-        raise cherrypy.HTTPError(406, msg)
-    
-    def __call__(self, *args, **kwargs):
-        response = cherrypy.serving.response
-        self.body = self.oldhandler(*args, **kwargs)
-        
-        if isinstance(self.body, basestring):
-            # strings get wrapped in a list because iterating over a single
-            # item list is much faster than iterating over every character
-            # in a long string.
-            if self.body:
-                self.body = [self.body]
-            else:
-                # [''] doesn't evaluate to False, so replace it with [].
-                self.body = []
-        elif isinstance(self.body, types.FileType):
-            self.body = file_generator(self.body)
-        elif self.body is None:
-            self.body = []
-        
-        ct = response.headers.elements("Content-Type")
-        if self.debug:
-            cherrypy.log('Content-Type: %r' % ct, 'TOOLS.ENCODE')
-        if ct:
-            if self.text_only:
-                ct = ct[0]
-                if ct.value.lower().startswith("text/"):
-                    if self.debug:
-                        cherrypy.log('Content-Type %r starts with "text/"' % ct,
-                                     'TOOLS.ENCODE')
-                    do_find = True
-                else:
-                    if self.debug:
-                        cherrypy.log('Not finding because Content-Type %r does '
-                                     'not start with "text/"' % ct,
-                                     'TOOLS.ENCODE')
-                    do_find = False
-            else:
-                if self.debug:
-                    cherrypy.log('Finding because not text_only', 'TOOLS.ENCODE')
-                do_find = True
-            
-            if do_find:
-                # Set "charset=..." param on response Content-Type header
-                ct.params['charset'] = self.find_acceptable_charset()
-                if self.add_charset:
-                    if self.debug:
-                        cherrypy.log('Setting Content-Type %r' % ct,
-                                     'TOOLS.ENCODE')
-                    response.headers["Content-Type"] = str(ct)
-        
-        return self.body
-
-# GZIP
-
-def compress(body, compress_level):
-    """Compress 'body' at the given compress_level."""
-    import zlib
-    
-    # See http://www.gzip.org/zlib/rfc-gzip.html
-    yield '\x1f\x8b'       # ID1 and ID2: gzip marker
-    yield '\x08'           # CM: compression method
-    yield '\x00'           # FLG: none set
-    # MTIME: 4 bytes
-    yield struct.pack("<L", int(time.time()) & 0xFFFFFFFFL)
-    yield '\x02'           # XFL: max compression, slowest algo
-    yield '\xff'           # OS: unknown
-    
-    crc = zlib.crc32("")
-    size = 0
-    zobj = zlib.compressobj(compress_level,
-                            zlib.DEFLATED, -zlib.MAX_WBITS,
-                            zlib.DEF_MEM_LEVEL, 0)
-    for line in body:
-        size += len(line)
-        crc = zlib.crc32(line, crc)
-        yield zobj.compress(line)
-    yield zobj.flush()
-    
-    # CRC32: 4 bytes
-    yield struct.pack("<L", crc & 0xFFFFFFFFL)
-    # ISIZE: 4 bytes
-    yield struct.pack("<L", size & 0xFFFFFFFFL)
-
-def decompress(body):
-    import gzip
-    
-    zbuf = StringIO()
-    zbuf.write(body)
-    zbuf.seek(0)
-    zfile = gzip.GzipFile(mode='rb', fileobj=zbuf)
-    data = zfile.read()
-    zfile.close()
-    return data
-
-
-def gzip(compress_level=5, mime_types=['text/html', 'text/plain'], debug=False):
-    """Try to gzip the response body if Content-Type in mime_types.
-    
-    cherrypy.response.headers['Content-Type'] must be set to one of the
-    values in the mime_types arg before calling this function.
-    
-    No compression is performed if any of the following hold:
-        * The client sends no Accept-Encoding request header
-        * No 'gzip' or 'x-gzip' is present in the Accept-Encoding header
-        * No 'gzip' or 'x-gzip' with a qvalue > 0 is present
-        * The 'identity' value is given with a qvalue > 0.
-    """
-    request = cherrypy.serving.request
-    response = cherrypy.serving.response
-    
-    set_vary_header(response, "Accept-Encoding")
-    
-    if not response.body:
-        # Response body is empty (might be a 304 for instance)
-        if debug:
-            cherrypy.log('No response body', context='TOOLS.GZIP')
-        return
-    
-    # If returning cached content (which should already have been gzipped),
-    # don't re-zip.
-    if getattr(request, "cached", False):
-        if debug:
-            cherrypy.log('Not gzipping cached response', context='TOOLS.GZIP')
-        return
-    
-    acceptable = request.headers.elements('Accept-Encoding')
-    if not acceptable:
-        # If no Accept-Encoding field is present in a request,
-        # the server MAY assume that the client will accept any
-        # content coding. In this case, if "identity" is one of
-        # the available content-codings, then the server SHOULD use
-        # the "identity" content-coding, unless it has additional
-        # information that a different content-coding is meaningful
-        # to the client.
-        if debug:
-            cherrypy.log('No Accept-Encoding', context='TOOLS.GZIP')
-        return
-    
-    ct = response.headers.get('Content-Type', '').split(';')[0]
-    for coding in acceptable:
-        if coding.value == 'identity' and coding.qvalue != 0:
-            if debug:
-                cherrypy.log('Non-zero identity qvalue: %r' % coding,
-                             context='TOOLS.GZIP')
-            return
-        if coding.value in ('gzip', 'x-gzip'):
-            if coding.qvalue == 0:
-                if debug:
-                    cherrypy.log('Zero gzip qvalue: %r' % coding,
-                                 context='TOOLS.GZIP')
-                return
-            
-            if ct not in mime_types:
-                if debug:
-                    cherrypy.log('Content-Type %r not in mime_types %r' %
-                                 (ct, mime_types), context='TOOLS.GZIP')
-                return
-            
-            if debug:
-                cherrypy.log('Gzipping', context='TOOLS.GZIP')
-            # Return a generator that compresses the page
-            response.headers['Content-Encoding'] = 'gzip'
-            response.body = compress(response.body, compress_level)
-            if "Content-Length" in response.headers:
-                # Delete Content-Length header so finalize() recalcs it.
-                del response.headers["Content-Length"]
-            
-            return
-    
-    if debug:
-        cherrypy.log('No acceptable encoding found.', context='GZIP')
-    cherrypy.HTTPError(406, "identity, gzip").set_response()
-
--- a/bundled/cherrypy/cherrypy/lib/http.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-import warnings
-warnings.warn('cherrypy.lib.http has been deprecated and will be removed '
-              'in CherryPy 3.3 use cherrypy.lib.httputil instead.',
-              DeprecationWarning)
-
-from cherrypy.lib.httputil import *
-
--- a/bundled/cherrypy/cherrypy/lib/httpauth.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,361 +0,0 @@
-"""
-httpauth modules defines functions to implement HTTP Digest Authentication (RFC 2617).
-This has full compliance with 'Digest' and 'Basic' authentication methods. In
-'Digest' it supports both MD5 and MD5-sess algorithms.
-
-Usage:
-
-    First use 'doAuth' to request the client authentication for a
-    certain resource. You should send an httplib.UNAUTHORIZED response to the
-    client so he knows he has to authenticate itself.
-    
-    Then use 'parseAuthorization' to retrieve the 'auth_map' used in
-    'checkResponse'.
-
-    To use 'checkResponse' you must have already verified the password associated
-    with the 'username' key in 'auth_map' dict. Then you use the 'checkResponse'
-    function to verify if the password matches the one sent by the client.
-
-SUPPORTED_ALGORITHM - list of supported 'Digest' algorithms
-SUPPORTED_QOP - list of supported 'Digest' 'qop'.
-"""
-__version__ = 1, 0, 1
-__author__ = "Tiago Cogumbreiro <cogumbreiro@users.sf.net>"
-__credits__ = """
-    Peter van Kampen for its recipe which implement most of Digest authentication:
-    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/302378
-"""
-
-__license__ = """
-Copyright (c) 2005, Tiago Cogumbreiro <cogumbreiro@users.sf.net>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, 
-are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice, 
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice, 
-      this list of conditions and the following disclaimer in the documentation 
-      and/or other materials provided with the distribution.
-    * Neither the name of Sylvain Hellegouarch nor the names of his contributors 
-      may be used to endorse or promote products derived from this software 
-      without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""
-
-__all__ = ("digestAuth", "basicAuth", "doAuth", "checkResponse",
-           "parseAuthorization", "SUPPORTED_ALGORITHM", "md5SessionKey",
-           "calculateNonce", "SUPPORTED_QOP")
-
-################################################################################
-try:
-    # Python 2.5+
-    from hashlib import md5
-except ImportError:
-    from md5 import new as md5
-import time
-import base64
-from urllib2 import parse_http_list, parse_keqv_list
-
-MD5 = "MD5"
-MD5_SESS = "MD5-sess"
-AUTH = "auth"
-AUTH_INT = "auth-int"
-
-SUPPORTED_ALGORITHM = (MD5, MD5_SESS)
-SUPPORTED_QOP = (AUTH, AUTH_INT)
-
-################################################################################
-# doAuth
-#
-DIGEST_AUTH_ENCODERS = {
-    MD5: lambda val: md5(val).hexdigest(),
-    MD5_SESS: lambda val: md5(val).hexdigest(),
-#    SHA: lambda val: sha.new (val).hexdigest (),
-}
-
-def calculateNonce (realm, algorithm = MD5):
-    """This is an auxaliary function that calculates 'nonce' value. It is used
-    to handle sessions."""
-
-    global SUPPORTED_ALGORITHM, DIGEST_AUTH_ENCODERS
-    assert algorithm in SUPPORTED_ALGORITHM
-
-    try:
-        encoder = DIGEST_AUTH_ENCODERS[algorithm]
-    except KeyError:
-        raise NotImplementedError ("The chosen algorithm (%s) does not have "\
-                                   "an implementation yet" % algorithm)
-
-    return encoder ("%d:%s" % (time.time(), realm))
-
-def digestAuth (realm, algorithm = MD5, nonce = None, qop = AUTH):
-    """Challenges the client for a Digest authentication."""
-    global SUPPORTED_ALGORITHM, DIGEST_AUTH_ENCODERS, SUPPORTED_QOP
-    assert algorithm in SUPPORTED_ALGORITHM
-    assert qop in SUPPORTED_QOP
-
-    if nonce is None:
-        nonce = calculateNonce (realm, algorithm)
-
-    return 'Digest realm="%s", nonce="%s", algorithm="%s", qop="%s"' % (
-        realm, nonce, algorithm, qop
-    )
-
-def basicAuth (realm):
-    """Challengenes the client for a Basic authentication."""
-    assert '"' not in realm, "Realms cannot contain the \" (quote) character."
-
-    return 'Basic realm="%s"' % realm
-
-def doAuth (realm):
-    """'doAuth' function returns the challenge string b giving priority over
-    Digest and fallback to Basic authentication when the browser doesn't
-    support the first one.
-    
-    This should be set in the HTTP header under the key 'WWW-Authenticate'."""
-
-    return digestAuth (realm) + " " + basicAuth (realm)
-
-
-################################################################################
-# Parse authorization parameters
-#
-def _parseDigestAuthorization (auth_params):
-    # Convert the auth params to a dict
-    items = parse_http_list(auth_params)
-    params = parse_keqv_list(items)
-
-    # Now validate the params
-
-    # Check for required parameters
-    required = ["username", "realm", "nonce", "uri", "response"]
-    for k in required:
-        if k not in params:
-            return None
-
-    # If qop is sent then cnonce and nc MUST be present
-    if "qop" in params and not ("cnonce" in params \
-                                      and "nc" in params):
-        return None
-
-    # If qop is not sent, neither cnonce nor nc can be present
-    if ("cnonce" in params or "nc" in params) and \
-       "qop" not in params:
-        return None
-
-    return params
-
-
-def _parseBasicAuthorization (auth_params):
-    username, password = base64.decodestring (auth_params).split (":", 1)
-    return {"username": username, "password": password}
-
-AUTH_SCHEMES = {
-    "basic": _parseBasicAuthorization,
-    "digest": _parseDigestAuthorization,
-}
-
-def parseAuthorization (credentials):
-    """parseAuthorization will convert the value of the 'Authorization' key in
-    the HTTP header to a map itself. If the parsing fails 'None' is returned.
-    """
-
-    global AUTH_SCHEMES
-
-    auth_scheme, auth_params  = credentials.split(" ", 1)
-    auth_scheme = auth_scheme.lower ()
-
-    parser = AUTH_SCHEMES[auth_scheme]
-    params = parser (auth_params)
-
-    if params is None:
-        return
-
-    assert "auth_scheme" not in params
-    params["auth_scheme"] = auth_scheme
-    return params
-
-
-################################################################################
-# Check provided response for a valid password
-#
-def md5SessionKey (params, password):
-    """
-    If the "algorithm" directive's value is "MD5-sess", then A1 
-    [the session key] is calculated only once - on the first request by the
-    client following receipt of a WWW-Authenticate challenge from the server.
-
-    This creates a 'session key' for the authentication of subsequent
-    requests and responses which is different for each "authentication
-    session", thus limiting the amount of material hashed with any one
-    key.
-
-    Because the server need only use the hash of the user
-    credentials in order to create the A1 value, this construction could
-    be used in conjunction with a third party authentication service so
-    that the web server would not need the actual password value.  The
-    specification of such a protocol is beyond the scope of this
-    specification.
-"""
-
-    keys = ("username", "realm", "nonce", "cnonce")
-    params_copy = {}
-    for key in keys:
-        params_copy[key] = params[key]
-
-    params_copy["algorithm"] = MD5_SESS
-    return _A1 (params_copy, password)
-
-def _A1(params, password):
-    algorithm = params.get ("algorithm", MD5)
-    H = DIGEST_AUTH_ENCODERS[algorithm]
-
-    if algorithm == MD5:
-        # If the "algorithm" directive's value is "MD5" or is
-        # unspecified, then A1 is:
-        # A1 = unq(username-value) ":" unq(realm-value) ":" passwd
-        return "%s:%s:%s" % (params["username"], params["realm"], password)
-
-    elif algorithm == MD5_SESS:
-
-        # This is A1 if qop is set
-        # A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
-        #         ":" unq(nonce-value) ":" unq(cnonce-value)
-        h_a1 = H ("%s:%s:%s" % (params["username"], params["realm"], password))
-        return "%s:%s:%s" % (h_a1, params["nonce"], params["cnonce"])
-
-
-def _A2(params, method, kwargs):
-    # If the "qop" directive's value is "auth" or is unspecified, then A2 is:
-    # A2 = Method ":" digest-uri-value
-
-    qop = params.get ("qop", "auth")
-    if qop == "auth":
-        return method + ":" + params["uri"]
-    elif qop == "auth-int":
-        # If the "qop" value is "auth-int", then A2 is:
-        # A2 = Method ":" digest-uri-value ":" H(entity-body)
-        entity_body = kwargs.get ("entity_body", "")
-        H = kwargs["H"]
-
-        return "%s:%s:%s" % (
-            method,
-            params["uri"],
-            H(entity_body)
-        )
-
-    else:
-        raise NotImplementedError ("The 'qop' method is unknown: %s" % qop)
-
-def _computeDigestResponse(auth_map, password, method = "GET", A1 = None,**kwargs):
-    """
-    Generates a response respecting the algorithm defined in RFC 2617
-    """
-    params = auth_map
-
-    algorithm = params.get ("algorithm", MD5)
-
-    H = DIGEST_AUTH_ENCODERS[algorithm]
-    KD = lambda secret, data: H(secret + ":" + data)
-
-    qop = params.get ("qop", None)
-
-    H_A2 = H(_A2(params, method, kwargs))
-
-    if algorithm == MD5_SESS and A1 is not None:
-        H_A1 = H(A1)
-    else:
-        H_A1 = H(_A1(params, password))
-
-    if qop in ("auth", "auth-int"):
-        # If the "qop" value is "auth" or "auth-int":
-        # request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
-        #                              ":" nc-value
-        #                              ":" unq(cnonce-value)
-        #                              ":" unq(qop-value)
-        #                              ":" H(A2)
-        #                      ) <">
-        request = "%s:%s:%s:%s:%s" % (
-            params["nonce"],
-            params["nc"],
-            params["cnonce"],
-            params["qop"],
-            H_A2,
-        )
-    elif qop is None:
-        # If the "qop" directive is not present (this construction is
-        # for compatibility with RFC 2069):
-        # request-digest  =
-        #         <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
-        request = "%s:%s" % (params["nonce"], H_A2)
-
-    return KD(H_A1, request)
-
-def _checkDigestResponse(auth_map, password, method = "GET", A1 = None, **kwargs):
-    """This function is used to verify the response given by the client when
-    he tries to authenticate.
-    Optional arguments:
-     entity_body - when 'qop' is set to 'auth-int' you MUST provide the
-                   raw data you are going to send to the client (usually the
-                   HTML page.
-     request_uri - the uri from the request line compared with the 'uri'
-                   directive of the authorization map. They must represent
-                   the same resource (unused at this time).
-    """
-
-    if auth_map['realm'] != kwargs.get('realm', None):
-        return False
-
-    response =  _computeDigestResponse(auth_map, password, method, A1,**kwargs)
-
-    return response == auth_map["response"]
-
-def _checkBasicResponse (auth_map, password, method='GET', encrypt=None, **kwargs):
-    # Note that the Basic response doesn't provide the realm value so we cannot
-    # test it
-    try:
-        return encrypt(auth_map["password"], auth_map["username"]) == password
-    except TypeError:
-        return encrypt(auth_map["password"]) == password
-
-AUTH_RESPONSES = {
-    "basic": _checkBasicResponse,
-    "digest": _checkDigestResponse,
-}
-
-def checkResponse (auth_map, password, method = "GET", encrypt=None, **kwargs):
-    """'checkResponse' compares the auth_map with the password and optionally
-    other arguments that each implementation might need.
-    
-    If the response is of type 'Basic' then the function has the following
-    signature:
-    
-    checkBasicResponse (auth_map, password) -> bool
-    
-    If the response is of type 'Digest' then the function has the following
-    signature:
-    
-    checkDigestResponse (auth_map, password, method = 'GET', A1 = None) -> bool
-    
-    The 'A1' argument is only used in MD5_SESS algorithm based responses.
-    Check md5SessionKey() for more info.
-    """
-    global AUTH_RESPONSES
-    checker = AUTH_RESPONSES[auth_map["auth_scheme"]]
-    return checker (auth_map, password, method=method, encrypt=encrypt, **kwargs)
- 
-
-
-
--- a/bundled/cherrypy/cherrypy/lib/httputil.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,446 +0,0 @@
-"""HTTP library functions."""
-
-# This module contains functions for building an HTTP application
-# framework: any one, not just one whose name starts with "Ch". ;) If you
-# reference any modules from some popular framework inside *this* module,
-# FuManChu will personally hang you up by your thumbs and submit you
-# to a public caning.
-
-from binascii import b2a_base64
-from BaseHTTPServer import BaseHTTPRequestHandler
-response_codes = BaseHTTPRequestHandler.responses.copy()
-
-# From http://www.cherrypy.org/ticket/361
-response_codes[500] = ('Internal Server Error',
-                      'The server encountered an unexpected condition '
-                      'which prevented it from fulfilling the request.')
-response_codes[503] = ('Service Unavailable',
-                      'The server is currently unable to handle the '
-                      'request due to a temporary overloading or '
-                      'maintenance of the server.')
-
-import re
-import urllib
-
-from rfc822 import formatdate as HTTPDate
-
-
-def urljoin(*atoms):
-    """Return the given path *atoms, joined into a single URL.
-    
-    This will correctly join a SCRIPT_NAME and PATH_INFO into the
-    original URL, even if either atom is blank.
-    """
-    url = "/".join([x for x in atoms if x])
-    while "//" in url:
-        url = url.replace("//", "/")
-    # Special-case the final url of "", and return "/" instead.
-    return url or "/"
-
-def protocol_from_http(protocol_str):
-    """Return a protocol tuple from the given 'HTTP/x.y' string."""
-    return int(protocol_str[5]), int(protocol_str[7])
-
-def get_ranges(headervalue, content_length):
-    """Return a list of (start, stop) indices from a Range header, or None.
-    
-    Each (start, stop) tuple will be composed of two ints, which are suitable
-    for use in a slicing operation. That is, the header "Range: bytes=3-6",
-    if applied against a Python string, is requesting resource[3:7]. This
-    function will return the list [(3, 7)].
-    
-    If this function returns an empty list, you should return HTTP 416.
-    """
-    
-    if not headervalue:
-        return None
-    
-    result = []
-    bytesunit, byteranges = headervalue.split("=", 1)
-    for brange in byteranges.split(","):
-        start, stop = [x.strip() for x in brange.split("-", 1)]
-        if start:
-            if not stop:
-                stop = content_length - 1
-            start, stop = int(start), int(stop)
-            if start >= content_length:
-                # From rfc 2616 sec 14.16:
-                # "If the server receives a request (other than one
-                # including an If-Range request-header field) with an
-                # unsatisfiable Range request-header field (that is,
-                # all of whose byte-range-spec values have a first-byte-pos
-                # value greater than the current length of the selected
-                # resource), it SHOULD return a response code of 416
-                # (Requested range not satisfiable)."
-                continue
-            if stop < start:
-                # From rfc 2616 sec 14.16:
-                # "If the server ignores a byte-range-spec because it
-                # is syntactically invalid, the server SHOULD treat
-                # the request as if the invalid Range header field
-                # did not exist. (Normally, this means return a 200
-                # response containing the full entity)."
-                return None
-            result.append((start, stop + 1))
-        else:
-            if not stop:
-                # See rfc quote above.
-                return None
-            # Negative subscript (last N bytes)
-            result.append((content_length - int(stop), content_length))
-    
-    return result
-
-
-class HeaderElement(object):
-    """An element (with parameters) from an HTTP header's element list."""
-    
-    def __init__(self, value, params=None):
-        self.value = value
-        if params is None:
-            params = {}
-        self.params = params
-    
-    def __cmp__(self, other):
-        return cmp(self.value, other.value)
-    
-    def __unicode__(self):
-        p = [";%s=%s" % (k, v) for k, v in self.params.iteritems()]
-        return u"%s%s" % (self.value, "".join(p))
-    
-    def __str__(self):
-        return str(self.__unicode__())
-    
-    def parse(elementstr):
-        """Transform 'token;key=val' to ('token', {'key': 'val'})."""
-        # Split the element into a value and parameters. The 'value' may
-        # be of the form, "token=token", but we don't split that here.
-        atoms = [x.strip() for x in elementstr.split(";") if x.strip()]
-        if not atoms:
-            initial_value = ''
-        else:
-            initial_value = atoms.pop(0).strip()
-        params = {}
-        for atom in atoms:
-            atom = [x.strip() for x in atom.split("=", 1) if x.strip()]
-            key = atom.pop(0)
-            if atom:
-                val = atom[0]
-            else:
-                val = ""
-            params[key] = val
-        return initial_value, params
-    parse = staticmethod(parse)
-    
-    def from_str(cls, elementstr):
-        """Construct an instance from a string of the form 'token;key=val'."""
-        ival, params = cls.parse(elementstr)
-        return cls(ival, params)
-    from_str = classmethod(from_str)
-
-
-q_separator = re.compile(r'; *q *=')
-
-class AcceptElement(HeaderElement):
-    """An element (with parameters) from an Accept* header's element list.
-    
-    AcceptElement objects are comparable; the more-preferred object will be
-    "less than" the less-preferred object. They are also therefore sortable;
-    if you sort a list of AcceptElement objects, they will be listed in
-    priority order; the most preferred value will be first. Yes, it should
-    have been the other way around, but it's too late to fix now.
-    """
-    
-    def from_str(cls, elementstr):
-        qvalue = None
-        # The first "q" parameter (if any) separates the initial
-        # media-range parameter(s) (if any) from the accept-params.
-        atoms = q_separator.split(elementstr, 1)
-        media_range = atoms.pop(0).strip()
-        if atoms:
-            # The qvalue for an Accept header can have extensions. The other
-            # headers cannot, but it's easier to parse them as if they did.
-            qvalue = HeaderElement.from_str(atoms[0].strip())
-        
-        media_type, params = cls.parse(media_range)
-        if qvalue is not None:
-            params["q"] = qvalue
-        return cls(media_type, params)
-    from_str = classmethod(from_str)
-    
-    def qvalue(self):
-        val = self.params.get("q", "1")
-        if isinstance(val, HeaderElement):
-            val = val.value
-        return float(val)
-    qvalue = property(qvalue, doc="The qvalue, or priority, of this value.")
-    
-    def __cmp__(self, other):
-        diff = cmp(self.qvalue, other.qvalue)
-        if diff == 0:
-            diff = cmp(str(self), str(other))
-        return diff
-
-
-def header_elements(fieldname, fieldvalue):
-    """Return a sorted HeaderElement list from a comma-separated header str."""
-    if not fieldvalue:
-        return []
-    
-    result = []
-    for element in fieldvalue.split(","):
-        if fieldname.startswith("Accept") or fieldname == 'TE':
-            hv = AcceptElement.from_str(element)
-        else:
-            hv = HeaderElement.from_str(element)
-        result.append(hv)
-    result.sort()
-    result.reverse()
-    return result
-
-def decode_TEXT(value):
-    """Decode RFC-2047 TEXT (e.g. "=?utf-8?q?f=C3=BCr?=" -> u"f\xfcr")."""
-    from email.Header import decode_header
-    atoms = decode_header(value)
-    decodedvalue = ""
-    for atom, charset in atoms:
-        if charset is not None:
-            atom = atom.decode(charset)
-        decodedvalue += atom
-    return decodedvalue
-
-def valid_status(status):
-    """Return legal HTTP status Code, Reason-phrase and Message.
-    
-    The status arg must be an int, or a str that begins with an int.
-    
-    If status is an int, or a str and no reason-phrase is supplied,
-    a default reason-phrase will be provided.
-    """
-    
-    if not status:
-        status = 200
-    
-    status = str(status)
-    parts = status.split(" ", 1)
-    if len(parts) == 1:
-        # No reason supplied.
-        code, = parts
-        reason = None
-    else:
-        code, reason = parts
-        reason = reason.strip()
-    
-    try:
-        code = int(code)
-    except ValueError:
-        raise ValueError("Illegal response status from server "
-                         "(%s is non-numeric)." % repr(code))
-    
-    if code < 100 or code > 599:
-        raise ValueError("Illegal response status from server "
-                         "(%s is out of range)." % repr(code))
-    
-    if code not in response_codes:
-        # code is unknown but not illegal
-        default_reason, message = "", ""
-    else:
-        default_reason, message = response_codes[code]
-    
-    if reason is None:
-        reason = default_reason
-    
-    return code, reason, message
-
-
-def _parse_qs(qs, keep_blank_values=0, strict_parsing=0, encoding='utf-8'):
-    """Parse a query given as a string argument.
-
-    Arguments:
-
-    qs: URL-encoded query string to be parsed
-
-    keep_blank_values: flag indicating whether blank values in
-        URL encoded queries should be treated as blank strings.  A
-        true value indicates that blanks should be retained as blank
-        strings.  The default false value indicates that blank values
-        are to be ignored and treated as if they were  not included.
-
-    strict_parsing: flag indicating what to do with parsing errors. If
-        false (the default), errors are silently ignored. If true,
-        errors raise a ValueError exception.
-
-    Returns a dict, as G-d intended.
-    """
-    pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
-    d = {}
-    for name_value in pairs:
-        if not name_value and not strict_parsing:
-            continue
-        nv = name_value.split('=', 1)
-        if len(nv) != 2:
-            if strict_parsing:
-                raise ValueError("bad query field: %r" % (name_value,))
-            # Handle case of a control-name with no equal sign
-            if keep_blank_values:
-                nv.append('')
-            else:
-                continue
-        if len(nv[1]) or keep_blank_values:
-            name = urllib.unquote(nv[0].replace('+', ' '))
-            name = name.decode(encoding, 'strict')
-            value = urllib.unquote(nv[1].replace('+', ' '))
-            value = value.decode(encoding, 'strict')
-            if name in d:
-                if not isinstance(d[name], list):
-                    d[name] = [d[name]]
-                d[name].append(value)
-            else:
-                d[name] = value
-    return d
-
-
-image_map_pattern = re.compile(r"[0-9]+,[0-9]+")
-
-def parse_query_string(query_string, keep_blank_values=True, encoding='utf-8'):
-    """Build a params dictionary from a query_string.
-    
-    Duplicate key/value pairs in the provided query_string will be
-    returned as {'key': [val1, val2, ...]}. Single key/values will
-    be returned as strings: {'key': 'value'}.
-    """
-    if image_map_pattern.match(query_string):
-        # Server-side image map. Map the coords to 'x' and 'y'
-        # (like CGI::Request does).
-        pm = query_string.split(",")
-        pm = {'x': int(pm[0]), 'y': int(pm[1])}
-    else:
-        pm = _parse_qs(query_string, keep_blank_values, encoding=encoding)
-    return pm
-
-
-class CaseInsensitiveDict(dict):
-    """A case-insensitive dict subclass.
-    
-    Each key is changed on entry to str(key).title().
-    """
-    
-    def __getitem__(self, key):
-        return dict.__getitem__(self, str(key).title())
-    
-    def __setitem__(self, key, value):
-        dict.__setitem__(self, str(key).title(), value)
-    
-    def __delitem__(self, key):
-        dict.__delitem__(self, str(key).title())
-    
-    def __contains__(self, key):
-        return dict.__contains__(self, str(key).title())
-    
-    def get(self, key, default=None):
-        return dict.get(self, str(key).title(), default)
-    
-    def has_key(self, key):
-        return dict.has_key(self, str(key).title())
-    
-    def update(self, E):
-        for k in E.keys():
-            self[str(k).title()] = E[k]
-    
-    def fromkeys(cls, seq, value=None):
-        newdict = cls()
-        for k in seq:
-            newdict[str(k).title()] = value
-        return newdict
-    fromkeys = classmethod(fromkeys)
-    
-    def setdefault(self, key, x=None):
-        key = str(key).title()
-        try:
-            return self[key]
-        except KeyError:
-            self[key] = x
-            return x
-    
-    def pop(self, key, default):
-        return dict.pop(self, str(key).title(), default)
-
-
-class HeaderMap(CaseInsensitiveDict):
-    """A dict subclass for HTTP request and response headers.
-    
-    Each key is changed on entry to str(key).title(). This allows headers
-    to be case-insensitive and avoid duplicates.
-    
-    Values are header values (decoded according to RFC 2047 if necessary).
-    """
-    
-    protocol=(1, 1)
-    
-    def elements(self, key):
-        """Return a sorted list of HeaderElements for the given header."""
-        key = str(key).title()
-        value = self.get(key)
-        return header_elements(key, value)
-    
-    def values(self, key):
-        """Return a sorted list of HeaderElement.value for the given header."""
-        return [e.value for e in self.elements(key)]
-    
-    def output(self):
-        """Transform self into a list of (name, value) tuples."""
-        header_list = []
-        for k, v in self.items():
-            if isinstance(k, unicode):
-                k = k.encode("ISO-8859-1")
-            
-            if not isinstance(v, basestring):
-                v = str(v)
-            
-            if isinstance(v, unicode):
-                v = self.encode(v)
-            header_list.append((k, v))
-        return header_list
-    
-    def encode(self, v):
-        """Return the given header value, encoded for HTTP output."""
-        # HTTP/1.0 says, "Words of *TEXT may contain octets 
-        # from character sets other than US-ASCII." and 
-        # "Recipients of header field TEXT containing octets 
-        # outside the US-ASCII character set may assume that 
-        # they represent ISO-8859-1 characters." 
-        try:
-            v = v.encode("ISO-8859-1")
-        except UnicodeEncodeError:
-            if self.protocol == (1, 1):
-                # Encode RFC-2047 TEXT 
-                # (e.g. u"\u8200" -> "=?utf-8?b?6IiA?="). 
-                # We do our own here instead of using the email module
-                # because we never want to fold lines--folding has
-                # been deprecated by the HTTP working group.
-                v = b2a_base64(v.encode('utf-8'))
-                v = ('=?utf-8?b?' + v.strip('\n') + '?=')
-            else:
-                raise
-        return v
-
-class Host(object):
-    """An internet address.
-    
-    name should be the client's host name. If not available (because no DNS
-        lookup is performed), the IP address should be used instead.
-    """
-    
-    ip = "0.0.0.0"
-    port = 80
-    name = "unknown.tld"
-    
-    def __init__(self, ip, port, name=None):
-        self.ip = ip
-        self.port = port
-        if name is None:
-            name = ip
-        self.name = name
-    
-    def __repr__(self):
-        return "httputil.Host(%r, %r, %r)" % (self.ip, self.port, self.name)
--- a/bundled/cherrypy/cherrypy/lib/jsontools.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-import sys
-import cherrypy
-
-if sys.version_info >= (2, 6):
-    # Python 2.6: simplejson is part of the standard library
-    import json
-else:
-    try:
-        import simplejson as json
-    except ImportError:
-        json = None
-
-if json is None:
-    def json_decode(s):
-        raise ValueError('No JSON library is available')
-    def json_encode(s):
-        raise ValueError('No JSON library is available')
-else:
-    json_decode = json.JSONDecoder().decode
-    json_encode = json.JSONEncoder().iterencode
-
-def json_in(force=True, debug=False):
-    request = cherrypy.serving.request
-    def json_processor(entity):
-        """Read application/json data into request.json."""
-        if not entity.headers.get(u"Content-Length", u""):
-            raise cherrypy.HTTPError(411)
-        
-        body = entity.fp.read()
-        try:
-            request.json = json_decode(body)
-        except ValueError:
-            raise cherrypy.HTTPError(400, 'Invalid JSON document')
-    if force:
-        request.body.processors.clear()
-        request.body.default_proc = cherrypy.HTTPError(
-            415, 'Expected an application/json content type')
-    request.body.processors[u'application/json'] = json_processor
-
-def json_out(debug=False):
-    request = cherrypy.serving.request
-    response = cherrypy.serving.response
-    
-    real_handler = request.handler
-    def json_handler(*args, **kwargs):
-        response.headers['Content-Type'] = 'application/json'
-        value = real_handler(*args, **kwargs)
-        return json_encode(value)
-    request.handler = json_handler
-
--- a/bundled/cherrypy/cherrypy/lib/profiler.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-"""Profiler tools for CherryPy.
-
-CherryPy users
-==============
-
-You can profile any of your pages as follows:
-
-    from cherrypy.lib import profiler
-    
-    class Root:
-        p = profile.Profiler("/path/to/profile/dir")
-        
-        def index(self):
-            self.p.run(self._index)
-        index.exposed = True
-        
-        def _index(self):
-            return "Hello, world!"
-    
-    cherrypy.tree.mount(Root())
-
-
-You can also turn on profiling for all requests
-using the make_app function as WSGI middleware.
-
-
-CherryPy developers
-===================
-
-This module can be used whenever you make changes to CherryPy,
-to get a quick sanity-check on overall CP performance. Use the
-"--profile" flag when running the test suite. Then, use the serve()
-function to browse the results in a web browser. If you run this
-module from the command line, it will call serve() for you.
-
-"""
-
-
-# Make profiler output more readable by adding __init__ modules' parents.
-def new_func_strip_path(func_name):
-    filename, line, name = func_name
-    if filename.endswith("__init__.py"):
-        return os.path.basename(filename[:-12]) + filename[-12:], line, name
-    return os.path.basename(filename), line, name
-
-try:
-    import profile
-    import pstats
-    pstats.func_strip_path = new_func_strip_path
-except ImportError:
-    profile = None
-    pstats = None
-
-import os, os.path
-import sys
-import warnings
-
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-_count = 0
-
-class Profiler(object):
-    
-    def __init__(self, path=None):
-        if not path:
-            path = os.path.join(os.path.dirname(__file__), "profile")
-        self.path = path
-        if not os.path.exists(path):
-            os.makedirs(path)
-    
-    def run(self, func, *args, **params):
-        """Dump profile data into self.path."""
-        global _count
-        c = _count = _count + 1
-        path = os.path.join(self.path, "cp_%04d.prof" % c)
-        prof = profile.Profile()
-        result = prof.runcall(func, *args, **params)
-        prof.dump_stats(path)
-        return result
-    
-    def statfiles(self):
-        """statfiles() -> list of available profiles."""
-        return [f for f in os.listdir(self.path)
-                if f.startswith("cp_") and f.endswith(".prof")]
-    
-    def stats(self, filename, sortby='cumulative'):
-        """stats(index) -> output of print_stats() for the given profile."""
-        sio = StringIO()
-        if sys.version_info >= (2, 5):
-            s = pstats.Stats(os.path.join(self.path, filename), stream=sio)
-            s.strip_dirs()
-            s.sort_stats(sortby)
-            s.print_stats()
-        else:
-            # pstats.Stats before Python 2.5 didn't take a 'stream' arg,
-            # but just printed to stdout. So re-route stdout.
-            s = pstats.Stats(os.path.join(self.path, filename))
-            s.strip_dirs()
-            s.sort_stats(sortby)
-            oldout = sys.stdout
-            try:
-                sys.stdout = sio
-                s.print_stats()
-            finally:
-                sys.stdout = oldout
-        response = sio.getvalue()
-        sio.close()
-        return response
-    
-    def index(self):
-        return """<html>
-        <head><title>CherryPy profile data</title></head>
-        <frameset cols='200, 1*'>
-            <frame src='menu' />
-            <frame name='main' src='' />
-        </frameset>
-        </html>
-        """
-    index.exposed = True
-    
-    def menu(self):
-        yield "<h2>Profiling runs</h2>"
-        yield "<p>Click on one of the runs below to see profiling data.</p>"
-        runs = self.statfiles()
-        runs.sort()
-        for i in runs:
-            yield "<a href='report?filename=%s' target='main'>%s</a><br />" % (i, i)
-    menu.exposed = True
-    
-    def report(self, filename):
-        import cherrypy
-        cherrypy.response.headers['Content-Type'] = 'text/plain'
-        return self.stats(filename)
-    report.exposed = True
-
-
-class ProfileAggregator(Profiler):
-    
-    def __init__(self, path=None):
-        Profiler.__init__(self, path)
-        global _count
-        self.count = _count = _count + 1
-        self.profiler = profile.Profile()
-    
-    def run(self, func, *args):
-        path = os.path.join(self.path, "cp_%04d.prof" % self.count)
-        result = self.profiler.runcall(func, *args)
-        self.profiler.dump_stats(path)
-        return result
-
-
-class make_app:
-    def __init__(self, nextapp, path=None, aggregate=False):
-        """Make a WSGI middleware app which wraps 'nextapp' with profiling.
-        
-        nextapp: the WSGI application to wrap, usually an instance of
-            cherrypy.Application.
-        path: where to dump the profiling output.
-        aggregate: if True, profile data for all HTTP requests will go in
-            a single file. If False (the default), each HTTP request will
-            dump its profile data into a separate file.
-        """
-        if profile is None or pstats is None:
-            msg = ("Your installation of Python does not have a profile module. "
-                   "If you're on Debian, try `sudo apt-get install python-profiler`. "
-                   "See http://www.cherrypy.org/wiki/ProfilingOnDebian for details.")
-            warnings.warn(msg)
-        
-        self.nextapp = nextapp
-        self.aggregate = aggregate
-        if aggregate:
-            self.profiler = ProfileAggregator(path)
-        else:
-            self.profiler = Profiler(path)
-    
-    def __call__(self, environ, start_response):
-        def gather():
-            result = []
-            for line in self.nextapp(environ, start_response):
-                result.append(line)
-            return result
-        return self.profiler.run(gather)
-
-
-def serve(path=None, port=8080):
-    if profile is None or pstats is None:
-        msg = ("Your installation of Python does not have a profile module. "
-               "If you're on Debian, try `sudo apt-get install python-profiler`. "
-               "See http://www.cherrypy.org/wiki/ProfilingOnDebian for details.")
-        warnings.warn(msg)
-    
-    import cherrypy
-    cherrypy.config.update({'server.socket_port': int(port),
-                            'server.thread_pool': 10,
-                            'environment': "production",
-                            })
-    cherrypy.quickstart(Profiler(path))
-
-
-if __name__ == "__main__":
-    serve(*tuple(sys.argv[1:]))
-
--- a/bundled/cherrypy/cherrypy/lib/reprconf.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,345 +0,0 @@
-"""Generic configuration system using unrepr.
-
-Configuration data may be supplied as a Python dictionary, as a filename,
-or as an open file object. When you supply a filename or file, Python's
-builtin ConfigParser is used (with some extensions).
-
-Namespaces
-----------
-
-Configuration keys are separated into namespaces by the first "." in the key.
-
-The only key that cannot exist in a namespace is the "environment" entry.
-This special entry 'imports' other config entries from a template stored in
-the Config.environments dict.
-
-You can define your own namespaces to be called when new config is merged
-by adding a named handler to Config.namespaces. The name can be any string,
-and the handler must be either a callable or a context manager.
-"""
-
-from ConfigParser import ConfigParser
-try:
-    set
-except NameError:
-    from sets import Set as set
-import sys
-
-def as_dict(config):
-    """Return a dict from 'config' whether it is a dict, file, or filename."""
-    if isinstance(config, basestring):
-        config = Parser().dict_from_file(config)
-    elif hasattr(config, 'read'):
-        config = Parser().dict_from_file(config)
-    return config
-
-
-class NamespaceSet(dict):
-    """A dict of config namespace names and handlers.
-    
-    Each config entry should begin with a namespace name; the corresponding
-    namespace handler will be called once for each config entry in that
-    namespace, and will be passed two arguments: the config key (with the
-    namespace removed) and the config value.
-    
-    Namespace handlers may be any Python callable; they may also be
-    Python 2.5-style 'context managers', in which case their __enter__
-    method should return a callable to be used as the handler.
-    See cherrypy.tools (the Toolbox class) for an example.
-    """
-    
-    def __call__(self, config):
-        """Iterate through config and pass it to each namespace handler.
-        
-        'config' should be a flat dict, where keys use dots to separate
-        namespaces, and values are arbitrary.
-        
-        The first name in each config key is used to look up the corresponding
-        namespace handler. For example, a config entry of {'tools.gzip.on': v}
-        will call the 'tools' namespace handler with the args: ('gzip.on', v)
-        """
-        # Separate the given config into namespaces
-        ns_confs = {}
-        for k in config:
-            if "." in k:
-                ns, name = k.split(".", 1)
-                bucket = ns_confs.setdefault(ns, {})
-                bucket[name] = config[k]
-        
-        # I chose __enter__ and __exit__ so someday this could be
-        # rewritten using Python 2.5's 'with' statement:
-        # for ns, handler in self.iteritems():
-        #     with handler as callable:
-        #         for k, v in ns_confs.get(ns, {}).iteritems():
-        #             callable(k, v)
-        for ns, handler in self.items():
-            exit = getattr(handler, "__exit__", None)
-            if exit:
-                callable = handler.__enter__()
-                no_exc = True
-                try:
-                    try:
-                        for k, v in ns_confs.get(ns, {}).items():
-                            callable(k, v)
-                    except:
-                        # The exceptional case is handled here
-                        no_exc = False
-                        if exit is None:
-                            raise
-                        if not exit(*sys.exc_info()):
-                            raise
-                        # The exception is swallowed if exit() returns true
-                finally:
-                    # The normal and non-local-goto cases are handled here
-                    if no_exc and exit:
-                        exit(None, None, None)
-            else:
-                for k, v in ns_confs.get(ns, {}).items():
-                    handler(k, v)
-    
-    def __repr__(self):
-        return "%s.%s(%s)" % (self.__module__, self.__class__.__name__,
-                              dict.__repr__(self))
-    
-    def __copy__(self):
-        newobj = self.__class__()
-        newobj.update(self)
-        return newobj
-    copy = __copy__
-
-
-class Config(dict):
-    """A dict-like set of configuration data, with defaults and namespaces.
-    
-    May take a file, filename, or dict.
-    """
-    
-    defaults = {}
-    environments = {}
-    namespaces = NamespaceSet()
-    
-    def __init__(self, file=None, **kwargs):
-        self.reset()
-        if file is not None:
-            self.update(file)
-        if kwargs:
-            self.update(kwargs)
-    
-    def reset(self):
-        """Reset self to default values."""
-        self.clear()
-        dict.update(self, self.defaults)
-    
-    def update(self, config):
-        """Update self from a dict, file or filename."""
-        if isinstance(config, basestring):
-            # Filename
-            config = Parser().dict_from_file(config)
-        elif hasattr(config, 'read'):
-            # Open file object
-            config = Parser().dict_from_file(config)
-        else:
-            config = config.copy()
-        self._apply(config)
-    
-    def _apply(self, config):
-        """Update self from a dict."""
-        which_env = config.get('environment')
-        if which_env:
-            env = self.environments[which_env]
-            for k in env:
-                if k not in config:
-                    config[k] = env[k]
-        
-        dict.update(self, config)
-        self.namespaces(config)
-    
-    def __setitem__(self, k, v):
-        dict.__setitem__(self, k, v)
-        self.namespaces({k: v})
-
-
-class Parser(ConfigParser):
-    """Sub-class of ConfigParser that keeps the case of options and that raises
-    an exception if the file cannot be read.
-    """
-    
-    def optionxform(self, optionstr):
-        return optionstr
-    
-    def read(self, filenames):
-        if isinstance(filenames, basestring):
-            filenames = [filenames]
-        for filename in filenames:
-            # try:
-            #     fp = open(filename)
-            # except IOError:
-            #     continue
-            fp = open(filename)
-            try:
-                self._read(fp, filename)
-            finally:
-                fp.close()
-    
-    def as_dict(self, raw=False, vars=None):
-        """Convert an INI file to a dictionary"""
-        # Load INI file into a dict
-        result = {}
-        for section in self.sections():
-            if section not in result:
-                result[section] = {}
-            for option in self.options(section):
-                value = self.get(section, option, raw, vars)
-                try:
-                    value = unrepr(value)
-                except Exception, x:
-                    msg = ("Config error in section: %r, option: %r, "
-                           "value: %r. Config values must be valid Python." %
-                           (section, option, value))
-                    raise ValueError(msg, x.__class__.__name__, x.args)
-                result[section][option] = value
-        return result
-    
-    def dict_from_file(self, file):
-        if hasattr(file, 'read'):
-            self.readfp(file)
-        else:
-            self.read(file)
-        return self.as_dict()
-
-
-# public domain "unrepr" implementation, found on the web and then improved.
-
-class _Builder:
-    
-    def build(self, o):
-        m = getattr(self, 'build_' + o.__class__.__name__, None)
-        if m is None:
-            raise TypeError("unrepr does not recognize %s" %
-                            repr(o.__class__.__name__))
-        return m(o)
-    
-    def build_Subscript(self, o):
-        expr, flags, subs = o.getChildren()
-        expr = self.build(expr)
-        subs = self.build(subs)
-        return expr[subs]
-    
-    def build_CallFunc(self, o):
-        children = map(self.build, o.getChildren())
-        callee = children.pop(0)
-        kwargs = children.pop() or {}
-        starargs = children.pop() or ()
-        args = tuple(children) + tuple(starargs)
-        return callee(*args, **kwargs)
-    
-    def build_List(self, o):
-        return map(self.build, o.getChildren())
-    
-    def build_Const(self, o):
-        return o.value
-    
-    def build_Dict(self, o):
-        d = {}
-        i = iter(map(self.build, o.getChildren()))
-        for el in i:
-            d[el] = i.next()
-        return d
-    
-    def build_Tuple(self, o):
-        return tuple(self.build_List(o))
-    
-    def build_Name(self, o):
-        name = o.name
-        if name == 'None':
-            return None
-        if name == 'True':
-            return True
-        if name == 'False':
-            return False
-        
-        # See if the Name is a package or module. If it is, import it.
-        try:
-            return modules(name)
-        except ImportError:
-            pass
-        
-        # See if the Name is in builtins.
-        try:
-            import __builtin__
-            return getattr(__builtin__, name)
-        except AttributeError:
-            pass
-        
-        raise TypeError("unrepr could not resolve the name %s" % repr(name))
-    
-    def build_Add(self, o):
-        left, right = map(self.build, o.getChildren())
-        return left + right
-    
-    def build_Getattr(self, o):
-        parent = self.build(o.expr)
-        return getattr(parent, o.attrname)
-    
-    def build_NoneType(self, o):
-        return None
-    
-    def build_UnarySub(self, o):
-        return -self.build(o.getChildren()[0])
-    
-    def build_UnaryAdd(self, o):
-        return self.build(o.getChildren()[0])
-
-
-def _astnode(s):
-    """Return a Python ast Node compiled from a string."""
-    try:
-        import compiler
-    except ImportError:
-        # Fallback to eval when compiler package is not available,
-        # e.g. IronPython 1.0.
-        return eval(s)
-    
-    p = compiler.parse("__tempvalue__ = " + s)
-    return p.getChildren()[1].getChildren()[0].getChildren()[1]
-    
-
-def unrepr(s):
-    """Return a Python object compiled from a string."""
-    if not s:
-        return s
-    obj = _astnode(s)
-    return _Builder().build(obj)
-
-
-def modules(modulePath):
-    """Load a module and retrieve a reference to that module."""
-    try:
-        mod = sys.modules[modulePath]
-        if mod is None:
-            raise KeyError()
-    except KeyError:
-        # The last [''] is important.
-        mod = __import__(modulePath, globals(), locals(), [''])
-    return mod
-
-def attributes(full_attribute_name):
-    """Load a module and retrieve an attribute of that module."""
-    
-    # Parse out the path, module, and attribute
-    last_dot = full_attribute_name.rfind(".")
-    attr_name = full_attribute_name[last_dot + 1:]
-    mod_path = full_attribute_name[:last_dot]
-    
-    mod = modules(mod_path)
-    # Let an AttributeError propagate outward.
-    try:
-        attr = getattr(mod, attr_name)
-    except AttributeError:
-        raise AttributeError("'%s' object has no attribute '%s'"
-                             % (mod_path, attr_name))
-    
-    # Return a reference to the attribute.
-    return attr
-
-
--- a/bundled/cherrypy/cherrypy/lib/sessions.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,741 +0,0 @@
-"""Session implementation for CherryPy.
-
-We use cherrypy.request to store some convenient variables as
-well as data about the session for the current request. Instead of
-polluting cherrypy.request we use a Session object bound to
-cherrypy.session to store these variables.
-"""
-
-import datetime
-import os
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-import random
-try:
-    # Python 2.5+
-    from hashlib import sha1 as sha
-except ImportError:
-    from sha import new as sha
-import time
-import threading
-import types
-from warnings import warn
-
-import cherrypy
-from cherrypy.lib import httputil
-
-
-missing = object()
-
-class Session(object):
-    """A CherryPy dict-like Session object (one per request)."""
-    
-    __metaclass__ = cherrypy._AttributeDocstrings
-    
-    _id = None
-    id_observers = None
-    id_observers__doc = "A list of callbacks to which to pass new id's."
-    
-    id__doc = "The current session ID."
-    def _get_id(self):
-        return self._id
-    def _set_id(self, value):
-        self._id = value
-        for o in self.id_observers:
-            o(value)
-    id = property(_get_id, _set_id, doc=id__doc)
-    
-    timeout = 60
-    timeout__doc = "Number of minutes after which to delete session data."
-    
-    locked = False
-    locked__doc = """
-    If True, this session instance has exclusive read/write access
-    to session data."""
-    
-    loaded = False
-    loaded__doc = """
-    If True, data has been retrieved from storage. This should happen
-    automatically on the first attempt to access session data."""
-    
-    clean_thread = None
-    clean_thread__doc = "Class-level Monitor which calls self.clean_up."
-    
-    clean_freq = 5
-    clean_freq__doc = "The poll rate for expired session cleanup in minutes."
-    
-    originalid = None
-    originalid__doc = "The session id passed by the client. May be missing or unsafe."
-    
-    missing = False
-    missing__doc = "True if the session requested by the client did not exist."
-    
-    regenerated = False
-    regenerated__doc = """
-    True if the application called session.regenerate(). This is not set by
-    internal calls to regenerate the session id."""
-    
-    debug=False
-    
-    def __init__(self, id=None, **kwargs):
-        self.id_observers = []
-        self._data = {}
-        
-        for k, v in kwargs.items():
-            setattr(self, k, v)
-        
-        self.originalid = id
-        self.missing = False
-        if id is None:
-            if self.debug:
-                cherrypy.log('No id given; making a new one', 'TOOLS.SESSIONS')
-            self._regenerate()
-        else:
-            self.id = id
-            if not self._exists():
-                if self.debug:
-                    cherrypy.log('Expired or malicious session %r; '
-                                 'making a new one' % id, 'TOOLS.SESSIONS')
-                # Expired or malicious session. Make a new one.
-                # See http://www.cherrypy.org/ticket/709.
-                self.id = None
-                self.missing = True
-                self._regenerate()
-    
-    def regenerate(self):
-        """Replace the current session (with a new id)."""
-        self.regenerated = True
-        self._regenerate()
-    
-    def _regenerate(self):
-        if self.id is not None:
-            self.delete()
-        
-        old_session_was_locked = self.locked
-        if old_session_was_locked:
-            self.release_lock()
-        
-        self.id = None
-        while self.id is None:
-            self.id = self.generate_id()
-            # Assert that the generated id is not already stored.
-            if self._exists():
-                self.id = None
-        
-        if old_session_was_locked:
-            self.acquire_lock()
-    
-    def clean_up(self):
-        """Clean up expired sessions."""
-        pass
-    
-    try:
-        os.urandom(20)
-    except (AttributeError, NotImplementedError):
-        # os.urandom not available until Python 2.4. Fall back to random.random.
-        def generate_id(self):
-            """Return a new session id."""
-            return sha('%s' % random.random()).hexdigest()
-    else:
-        def generate_id(self):
-            """Return a new session id."""
-            return os.urandom(20).encode('hex')
-    
-    def save(self):
-        """Save session data."""
-        try:
-            # If session data has never been loaded then it's never been
-            #   accessed: no need to save it
-            if self.loaded:
-                t = datetime.timedelta(seconds = self.timeout * 60)
-                expiration_time = datetime.datetime.now() + t
-                if self.debug:
-                    cherrypy.log('Saving with expiry %s' % expiration_time,
-                                 'TOOLS.SESSIONS')
-                self._save(expiration_time)
-            
-        finally:
-            if self.locked:
-                # Always release the lock if the user didn't release it
-                self.release_lock()
-    
-    def load(self):
-        """Copy stored session data into this session instance."""
-        data = self._load()
-        # data is either None or a tuple (session_data, expiration_time)
-        if data is None or data[1] < datetime.datetime.now():
-            if self.debug:
-                cherrypy.log('Expired session, flushing data', 'TOOLS.SESSIONS')
-            self._data = {}
-        else:
-            self._data = data[0]
-        self.loaded = True
-        
-        # Stick the clean_thread in the class, not the instance.
-        # The instances are created and destroyed per-request.
-        cls = self.__class__
-        if self.clean_freq and not cls.clean_thread:
-            # clean_up is in instancemethod and not a classmethod,
-            # so that tool config can be accessed inside the method.
-            t = cherrypy.process.plugins.Monitor(
-                cherrypy.engine, self.clean_up, self.clean_freq * 60,
-                name='Session cleanup')
-            t.subscribe()
-            cls.clean_thread = t
-            t.start()
-    
-    def delete(self):
-        """Delete stored session data."""
-        self._delete()
-    
-    def __getitem__(self, key):
-        if not self.loaded: self.load()
-        return self._data[key]
-    
-    def __setitem__(self, key, value):
-        if not self.loaded: self.load()
-        self._data[key] = value
-    
-    def __delitem__(self, key):
-        if not self.loaded: self.load()
-        del self._data[key]
-    
-    def pop(self, key, default=missing):
-        """Remove the specified key and return the corresponding value.
-        If key is not found, default is returned if given,
-        otherwise KeyError is raised.
-        """
-        if not self.loaded: self.load()
-        if default is missing:
-            return self._data.pop(key)
-        else:
-            return self._data.pop(key, default)
-    
-    def __contains__(self, key):
-        if not self.loaded: self.load()
-        return key in self._data
-    
-    def has_key(self, key):
-        """D.has_key(k) -> True if D has a key k, else False."""
-        if not self.loaded: self.load()
-        return key in self._data
-    
-    def get(self, key, default=None):
-        """D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None."""
-        if not self.loaded: self.load()
-        return self._data.get(key, default)
-    
-    def update(self, d):
-        """D.update(E) -> None.  Update D from E: for k in E: D[k] = E[k]."""
-        if not self.loaded: self.load()
-        self._data.update(d)
-    
-    def setdefault(self, key, default=None):
-        """D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D."""
-        if not self.loaded: self.load()
-        return self._data.setdefault(key, default)
-    
-    def clear(self):
-        """D.clear() -> None.  Remove all items from D."""
-        if not self.loaded: self.load()
-        self._data.clear()
-    
-    def keys(self):
-        """D.keys() -> list of D's keys."""
-        if not self.loaded: self.load()
-        return self._data.keys()
-    
-    def items(self):
-        """D.items() -> list of D's (key, value) pairs, as 2-tuples."""
-        if not self.loaded: self.load()
-        return self._data.items()
-    
-    def values(self):
-        """D.values() -> list of D's values."""
-        if not self.loaded: self.load()
-        return self._data.values()
-
-
-class RamSession(Session):
-    
-    # Class-level objects. Don't rebind these!
-    cache = {}
-    locks = {}
-    
-    def clean_up(self):
-        """Clean up expired sessions."""
-        now = datetime.datetime.now()
-        for id, (data, expiration_time) in self.cache.items():
-            if expiration_time <= now:
-                try:
-                    del self.cache[id]
-                except KeyError:
-                    pass
-                try:
-                    del self.locks[id]
-                except KeyError:
-                    pass
-    
-    def _exists(self):
-        return self.id in self.cache
-    
-    def _load(self):
-        return self.cache.get(self.id)
-    
-    def _save(self, expiration_time):
-        self.cache[self.id] = (self._data, expiration_time)
-    
-    def _delete(self):
-        self.cache.pop(self.id, None)
-    
-    def acquire_lock(self):
-        """Acquire an exclusive lock on the currently-loaded session data."""
-        self.locked = True
-        self.locks.setdefault(self.id, threading.RLock()).acquire()
-    
-    def release_lock(self):
-        """Release the lock on the currently-loaded session data."""
-        self.locks[self.id].release()
-        self.locked = False
-    
-    def __len__(self):
-        """Return the number of active sessions."""
-        return len(self.cache)
-
-
-class FileSession(Session):
-    """Implementation of the File backend for sessions
-    
-    storage_path: the folder where session data will be saved. Each session
-        will be saved as pickle.dump(data, expiration_time) in its own file;
-        the filename will be self.SESSION_PREFIX + self.id.
-    """
-    
-    SESSION_PREFIX = 'session-'
-    LOCK_SUFFIX = '.lock'
-    pickle_protocol = pickle.HIGHEST_PROTOCOL
-    
-    def __init__(self, id=None, **kwargs):
-        # The 'storage_path' arg is required for file-based sessions.
-        kwargs['storage_path'] = os.path.abspath(kwargs['storage_path'])
-        Session.__init__(self, id=id, **kwargs)
-    
-    def setup(cls, **kwargs):
-        """Set up the storage system for file-based sessions.
-        
-        This should only be called once per process; this will be done
-        automatically when using sessions.init (as the built-in Tool does).
-        """
-        # The 'storage_path' arg is required for file-based sessions.
-        kwargs['storage_path'] = os.path.abspath(kwargs['storage_path'])
-        
-        for k, v in kwargs.items():
-            setattr(cls, k, v)
-        
-        # Warn if any lock files exist at startup.
-        lockfiles = [fname for fname in os.listdir(cls.storage_path)
-                     if (fname.startswith(cls.SESSION_PREFIX)
-                         and fname.endswith(cls.LOCK_SUFFIX))]
-        if lockfiles:
-            plural = ('', 's')[len(lockfiles) > 1]
-            warn("%s session lockfile%s found at startup. If you are "
-                 "only running one process, then you may need to "
-                 "manually delete the lockfiles found at %r."
-                 % (len(lockfiles), plural, cls.storage_path))
-    setup = classmethod(setup)
-    
-    def _get_file_path(self):
-        f = os.path.join(self.storage_path, self.SESSION_PREFIX + self.id)
-        if not os.path.abspath(f).startswith(self.storage_path):
-            raise cherrypy.HTTPError(400, "Invalid session id in cookie.")
-        return f
-    
-    def _exists(self):
-        path = self._get_file_path()
-        return os.path.exists(path)
-    
-    def _load(self, path=None):
-        if path is None:
-            path = self._get_file_path()
-        try:
-            f = open(path, "rb")
-            try:
-                return pickle.load(f)
-            finally:
-                f.close()
-        except (IOError, EOFError):
-            return None
-    
-    def _save(self, expiration_time):
-        f = open(self._get_file_path(), "wb")
-        try:
-            pickle.dump((self._data, expiration_time), f, self.pickle_protocol)
-        finally:
-            f.close()
-    
-    def _delete(self):
-        try:
-            os.unlink(self._get_file_path())
-        except OSError:
-            pass
-    
-    def acquire_lock(self, path=None):
-        """Acquire an exclusive lock on the currently-loaded session data."""
-        if path is None:
-            path = self._get_file_path()
-        path += self.LOCK_SUFFIX
-        while True:
-            try:
-                lockfd = os.open(path, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
-            except OSError:
-                time.sleep(0.1)
-            else:
-                os.close(lockfd) 
-                break
-        self.locked = True
-    
-    def release_lock(self, path=None):
-        """Release the lock on the currently-loaded session data."""
-        if path is None:
-            path = self._get_file_path()
-        os.unlink(path + self.LOCK_SUFFIX)
-        self.locked = False
-    
-    def clean_up(self):
-        """Clean up expired sessions."""
-        now = datetime.datetime.now()
-        # Iterate over all session files in self.storage_path
-        for fname in os.listdir(self.storage_path):
-            if (fname.startswith(self.SESSION_PREFIX)
-                and not fname.endswith(self.LOCK_SUFFIX)):
-                # We have a session file: lock and load it and check
-                #   if it's expired. If it fails, nevermind.
-                path = os.path.join(self.storage_path, fname)
-                self.acquire_lock(path)
-                try:
-                    contents = self._load(path)
-                    # _load returns None on IOError
-                    if contents is not None:
-                        data, expiration_time = contents
-                        if expiration_time < now:
-                            # Session expired: deleting it
-                            os.unlink(path)
-                finally:
-                    self.release_lock(path)
-    
-    def __len__(self):
-        """Return the number of active sessions."""
-        return len([fname for fname in os.listdir(self.storage_path)
-                    if (fname.startswith(self.SESSION_PREFIX)
-                        and not fname.endswith(self.LOCK_SUFFIX))])
-
-
-class PostgresqlSession(Session):
-    """ Implementation of the PostgreSQL backend for sessions. It assumes
-        a table like this:
-
-            create table session (
-                id varchar(40),
-                data text,
-                expiration_time timestamp
-            )
-    
-    You must provide your own get_db function.
-    """
-    
-    pickle_protocol = pickle.HIGHEST_PROTOCOL
-    
-    def __init__(self, id=None, **kwargs):
-        Session.__init__(self, id, **kwargs)
-        self.cursor = self.db.cursor()
-    
-    def setup(cls, **kwargs):
-        """Set up the storage system for Postgres-based sessions.
-        
-        This should only be called once per process; this will be done
-        automatically when using sessions.init (as the built-in Tool does).
-        """
-        for k, v in kwargs.items():
-            setattr(cls, k, v)
-        
-        self.db = self.get_db()
-    setup = classmethod(setup)
-    
-    def __del__(self):
-        if self.cursor:
-            self.cursor.close()
-        self.db.commit()
-    
-    def _exists(self):
-        # Select session data from table
-        self.cursor.execute('select data, expiration_time from session '
-                            'where id=%s', (self.id,))
-        rows = self.cursor.fetchall()
-        return bool(rows)
-    
-    def _load(self):
-        # Select session data from table
-        self.cursor.execute('select data, expiration_time from session '
-                            'where id=%s', (self.id,))
-        rows = self.cursor.fetchall()
-        if not rows:
-            return None
-        
-        pickled_data, expiration_time = rows[0]
-        data = pickle.loads(pickled_data)
-        return data, expiration_time
-    
-    def _save(self, expiration_time):
-        pickled_data = pickle.dumps(self._data, self.pickle_protocol)
-        self.cursor.execute('update session set data = %s, '
-                            'expiration_time = %s where id = %s',
-                            (pickled_data, expiration_time, self.id))
-    
-    def _delete(self):
-        self.cursor.execute('delete from session where id=%s', (self.id,))
-   
-    def acquire_lock(self):
-        """Acquire an exclusive lock on the currently-loaded session data."""
-        # We use the "for update" clause to lock the row
-        self.locked = True
-        self.cursor.execute('select id from session where id=%s for update',
-                            (self.id,))
-    
-    def release_lock(self):
-        """Release the lock on the currently-loaded session data."""
-        # We just close the cursor and that will remove the lock
-        #   introduced by the "for update" clause
-        self.cursor.close()
-        self.locked = False
-    
-    def clean_up(self):
-        """Clean up expired sessions."""
-        self.cursor.execute('delete from session where expiration_time < %s',
-                            (datetime.datetime.now(),))
-
-
-class MemcachedSession(Session):
-    
-    # The most popular memcached client for Python isn't thread-safe.
-    # Wrap all .get and .set operations in a single lock.
-    mc_lock = threading.RLock()
-    
-    # This is a seperate set of locks per session id.
-    locks = {}
-    
-    servers = ['127.0.0.1:11211']
-    
-    def setup(cls, **kwargs):
-        """Set up the storage system for memcached-based sessions.
-        
-        This should only be called once per process; this will be done
-        automatically when using sessions.init (as the built-in Tool does).
-        """
-        for k, v in kwargs.items():
-            setattr(cls, k, v)
-        
-        import memcache
-        cls.cache = memcache.Client(cls.servers)
-    setup = classmethod(setup)
-    
-    def _exists(self):
-        self.mc_lock.acquire()
-        try:
-            return bool(self.cache.get(self.id))
-        finally:
-            self.mc_lock.release()
-    
-    def _load(self):
-        self.mc_lock.acquire()
-        try:
-            return self.cache.get(self.id)
-        finally:
-            self.mc_lock.release()
-    
-    def _save(self, expiration_time):
-        # Send the expiration time as "Unix time" (seconds since 1/1/1970)
-        td = int(time.mktime(expiration_time.timetuple()))
-        self.mc_lock.acquire()
-        try:
-            if not self.cache.set(self.id, (self._data, expiration_time), td):
-                raise AssertionError("Session data for id %r not set." % self.id)
-        finally:
-            self.mc_lock.release()
-    
-    def _delete(self):
-        self.cache.delete(self.id)
-    
-    def acquire_lock(self):
-        """Acquire an exclusive lock on the currently-loaded session data."""
-        self.locked = True
-        self.locks.setdefault(self.id, threading.RLock()).acquire()
-    
-    def release_lock(self):
-        """Release the lock on the currently-loaded session data."""
-        self.locks[self.id].release()
-        self.locked = False
-    
-    def __len__(self):
-        """Return the number of active sessions."""
-        raise NotImplementedError
-
-
-# Hook functions (for CherryPy tools)
-
-def save():
-    """Save any changed session data."""
-    
-    if not hasattr(cherrypy.serving, "session"):
-        return
-    request = cherrypy.serving.request
-    response = cherrypy.serving.response
-    
-    # Guard against running twice
-    if hasattr(request, "_sessionsaved"):
-        return
-    request._sessionsaved = True
-    
-    if response.stream:
-        # If the body is being streamed, we have to save the data
-        #   *after* the response has been written out
-        request.hooks.attach('on_end_request', cherrypy.session.save)
-    else:
-        # If the body is not being streamed, we save the data now
-        # (so we can release the lock).
-        if isinstance(response.body, types.GeneratorType):
-            response.collapse_body()
-        cherrypy.session.save()
-save.failsafe = True
-
-def close():
-    """Close the session object for this request."""
-    sess = getattr(cherrypy.serving, "session", None)
-    if getattr(sess, "locked", False):
-        # If the session is still locked we release the lock
-        sess.release_lock()
-close.failsafe = True
-close.priority = 90
-
-
-def init(storage_type='ram', path=None, path_header=None, name='session_id',
-         timeout=60, domain=None, secure=False, clean_freq=5,
-         persistent=True, debug=False, **kwargs):
-    """Initialize session object (using cookies).
-    
-    storage_type: one of 'ram', 'file', 'postgresql'. This will be used
-        to look up the corresponding class in cherrypy.lib.sessions
-        globals. For example, 'file' will use the FileSession class.
-    path: the 'path' value to stick in the response cookie metadata.
-    path_header: if 'path' is None (the default), then the response
-        cookie 'path' will be pulled from request.headers[path_header].
-    name: the name of the cookie.
-    timeout: the expiration timeout (in minutes) for the stored session data.
-        If 'persistent' is True (the default), this is also the timeout
-        for the cookie.
-    domain: the cookie domain.
-    secure: if False (the default) the cookie 'secure' value will not
-        be set. If True, the cookie 'secure' value will be set (to 1).
-    clean_freq (minutes): the poll rate for expired session cleanup.
-    persistent: if True (the default), the 'timeout' argument will be used
-        to expire the cookie. If False, the cookie will not have an expiry,
-        and the cookie will be a "session cookie" which expires when the
-        browser is closed.
-    
-    Any additional kwargs will be bound to the new Session instance,
-    and may be specific to the storage type. See the subclass of Session
-    you're using for more information.
-    """
-    
-    request = cherrypy.serving.request
-    
-    # Guard against running twice
-    if hasattr(request, "_session_init_flag"):
-        return
-    request._session_init_flag = True
-    
-    # Check if request came with a session ID
-    id = None
-    if name in request.cookie:
-        id = request.cookie[name].value
-        if debug:
-            cherrypy.log('ID obtained from request.cookie: %r' % id,
-                         'TOOLS.SESSIONS')
-    
-    # Find the storage class and call setup (first time only).
-    storage_class = storage_type.title() + 'Session'
-    storage_class = globals()[storage_class]
-    if not hasattr(cherrypy, "session"):
-        if hasattr(storage_class, "setup"):
-            storage_class.setup(**kwargs)
-    
-    # Create and attach a new Session instance to cherrypy.serving.
-    # It will possess a reference to (and lock, and lazily load)
-    # the requested session data.
-    kwargs['timeout'] = timeout
-    kwargs['clean_freq'] = clean_freq
-    cherrypy.serving.session = sess = storage_class(id, **kwargs)
-    sess.debug = debug
-    def update_cookie(id):
-        """Update the cookie every time the session id changes."""
-        cherrypy.serving.response.cookie[name] = id
-    sess.id_observers.append(update_cookie)
-    
-    # Create cherrypy.session which will proxy to cherrypy.serving.session
-    if not hasattr(cherrypy, "session"):
-        cherrypy.session = cherrypy._ThreadLocalProxy('session')
-    
-    if persistent:
-        cookie_timeout = timeout
-    else:
-        # See http://support.microsoft.com/kb/223799/EN-US/
-        # and http://support.mozilla.com/en-US/kb/Cookies
-        cookie_timeout = None
-    set_response_cookie(path=path, path_header=path_header, name=name,
-                        timeout=cookie_timeout, domain=domain, secure=secure)
-
-
-def set_response_cookie(path=None, path_header=None, name='session_id',
-                        timeout=60, domain=None, secure=False):
-    """Set a response cookie for the client.
-    
-    path: the 'path' value to stick in the response cookie metadata.
-    path_header: if 'path' is None (the default), then the response
-        cookie 'path' will be pulled from request.headers[path_header].
-    name: the name of the cookie.
-    timeout: the expiration timeout for the cookie. If 0 or other boolean
-        False, no 'expires' param will be set, and the cookie will be a
-        "session cookie" which expires when the browser is closed.
-    domain: the cookie domain.
-    secure: if False (the default) the cookie 'secure' value will not
-        be set. If True, the cookie 'secure' value will be set (to 1).
-    """
-    # Set response cookie
-    cookie = cherrypy.serving.response.cookie
-    cookie[name] = cherrypy.serving.session.id
-    cookie[name]['path'] = (path or cherrypy.serving.request.headers.get(path_header)
-                            or '/')
-    
-    # We'd like to use the "max-age" param as indicated in
-    # http://www.faqs.org/rfcs/rfc2109.html but IE doesn't
-    # save it to disk and the session is lost if people close
-    # the browser. So we have to use the old "expires" ... sigh ...
-##    cookie[name]['max-age'] = timeout * 60
-    if timeout:
-        e = time.time() + (timeout * 60)
-        cookie[name]['expires'] = httputil.HTTPDate(e)
-    if domain is not None:
-        cookie[name]['domain'] = domain
-    if secure:
-        cookie[name]['secure'] = 1
-
-
-def expire():
-    """Expire the current session cookie."""
-    name = cherrypy.serving.request.config.get('tools.sessions.name', 'session_id')
-    one_year = 60 * 60 * 24 * 365
-    e = time.time() - one_year
-    cherrypy.serving.response.cookie[name]['expires'] = httputil.HTTPDate(e)
-
-
--- a/bundled/cherrypy/cherrypy/lib/static.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,346 +0,0 @@
-import logging
-import mimetypes
-mimetypes.init()
-mimetypes.types_map['.dwg']='image/x-dwg'
-mimetypes.types_map['.ico']='image/x-icon'
-mimetypes.types_map['.bz2']='application/x-bzip2'
-mimetypes.types_map['.gz']='application/x-gzip'
-
-import os
-import re
-import stat
-import time
-from urllib import unquote
-
-import cherrypy
-from cherrypy.lib import cptools, httputil, file_generator_limited
-
-
-def serve_file(path, content_type=None, disposition=None, name=None, debug=False):
-    """Set status, headers, and body in order to serve the given path.
-    
-    The Content-Type header will be set to the content_type arg, if provided.
-    If not provided, the Content-Type will be guessed by the file extension
-    of the 'path' argument.
-    
-    If disposition is not None, the Content-Disposition header will be set
-    to "<disposition>; filename=<name>". If name is None, it will be set
-    to the basename of path. If disposition is None, no Content-Disposition
-    header will be written.
-    """
-    
-    response = cherrypy.serving.response
-    
-    # If path is relative, users should fix it by making path absolute.
-    # That is, CherryPy should not guess where the application root is.
-    # It certainly should *not* use cwd (since CP may be invoked from a
-    # variety of paths). If using tools.staticdir, you can make your relative
-    # paths become absolute by supplying a value for "tools.staticdir.root".
-    if not os.path.isabs(path):
-        msg = "'%s' is not an absolute path." % path
-        if debug:
-            cherrypy.log(msg, 'TOOLS.STATICFILE')
-        raise ValueError(msg)
-    
-    try:
-        st = os.stat(path)
-    except OSError:
-        if debug:
-            cherrypy.log('os.stat(%r) failed' % path, 'TOOLS.STATIC')
-        raise cherrypy.NotFound()
-    
-    # Check if path is a directory.
-    if stat.S_ISDIR(st.st_mode):
-        # Let the caller deal with it as they like.
-        if debug:
-            cherrypy.log('%r is a directory' % path, 'TOOLS.STATIC')
-        raise cherrypy.NotFound()
-    
-    # Set the Last-Modified response header, so that
-    # modified-since validation code can work.
-    response.headers['Last-Modified'] = httputil.HTTPDate(st.st_mtime)
-    cptools.validate_since()
-    
-    if content_type is None:
-        # Set content-type based on filename extension
-        ext = ""
-        i = path.rfind('.')
-        if i != -1:
-            ext = path[i:].lower()
-        content_type = mimetypes.types_map.get(ext, None)
-    if content_type is not None:
-        response.headers['Content-Type'] = content_type
-    if debug:
-        cherrypy.log('Content-Type: %r' % content_type, 'TOOLS.STATIC')
-    
-    cd = None
-    if disposition is not None:
-        if name is None:
-            name = os.path.basename(path)
-        cd = '%s; filename="%s"' % (disposition, name)
-        response.headers["Content-Disposition"] = cd
-    if debug:
-        cherrypy.log('Content-Disposition: %r' % cd, 'TOOLS.STATIC')
-    
-    # Set Content-Length and use an iterable (file object)
-    #   this way CP won't load the whole file in memory
-    content_length = st.st_size
-    fileobj = open(path, 'rb')
-    return _serve_fileobj(fileobj, content_type, content_length, debug=debug)
-
-def serve_fileobj(fileobj, content_type=None, disposition=None, name=None,
-                  debug=False):
-    """Set status, headers, and body in order to serve the given file object.
-    
-    The Content-Type header will be set to the content_type arg, if provided.
-    
-    If disposition is not None, the Content-Disposition header will be set
-    to "<disposition>; filename=<name>". If name is None, 'filename' will
-    not be set. If disposition is None, no Content-Disposition header will
-    be written.
-
-    CAUTION: If the request contains a 'Range' header, one or more seek()s will
-    be performed on the file object.  This may cause undesired behavior if
-    the file object is not seekable.  It could also produce undesired results
-    if the caller set the read position of the file object prior to calling
-    serve_fileobj(), expecting that the data would be served starting from that
-    position.
-    """
-    
-    response = cherrypy.serving.response
-    
-    try:
-        st = os.fstat(fileobj.fileno())
-    except AttributeError:
-        if debug:
-            cherrypy.log('os has no fstat attribute', 'TOOLS.STATIC')
-        content_length = None
-    else:
-        # Set the Last-Modified response header, so that
-        # modified-since validation code can work.
-        response.headers['Last-Modified'] = httputil.HTTPDate(st.st_mtime)
-        cptools.validate_since()
-        content_length = st.st_size
-    
-    if content_type is not None:
-        response.headers['Content-Type'] = content_type
-    if debug:
-        cherrypy.log('Content-Type: %r' % content_type, 'TOOLS.STATIC')
-    
-    cd = None
-    if disposition is not None:
-        if name is None:
-            cd = disposition
-        else:
-            cd = '%s; filename="%s"' % (disposition, name)
-        response.headers["Content-Disposition"] = cd
-    if debug:
-        cherrypy.log('Content-Disposition: %r' % cd, 'TOOLS.STATIC')
-    
-    return _serve_fileobj(fileobj, content_type, content_length, debug=debug)
-
-def _serve_fileobj(fileobj, content_type, content_length, debug=False):
-    """Internal. Set response.body to the given file object, perhaps ranged."""
-    response = cherrypy.serving.response
-    
-    # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code
-    request = cherrypy.serving.request
-    if request.protocol >= (1, 1):
-        response.headers["Accept-Ranges"] = "bytes"
-        r = httputil.get_ranges(request.headers.get('Range'), content_length)
-        if r == []:
-            response.headers['Content-Range'] = "bytes */%s" % content_length
-            message = "Invalid Range (first-byte-pos greater than Content-Length)"
-            if debug:
-                cherrypy.log(message, 'TOOLS.STATIC')
-            raise cherrypy.HTTPError(416, message)
-        
-        if r:
-            if len(r) == 1:
-                # Return a single-part response.
-                start, stop = r[0]
-                if stop > content_length:
-                    stop = content_length
-                r_len = stop - start
-                if debug:
-                    cherrypy.log('Single part; start: %r, stop: %r' % (start, stop),
-                                 'TOOLS.STATIC')
-                response.status = "206 Partial Content"
-                response.headers['Content-Range'] = (
-                    "bytes %s-%s/%s" % (start, stop - 1, content_length))
-                response.headers['Content-Length'] = r_len
-                fileobj.seek(start)
-                response.body = file_generator_limited(fileobj, r_len)
-            else:
-                # Return a multipart/byteranges response.
-                response.status = "206 Partial Content"
-                import mimetools
-                boundary = mimetools.choose_boundary()
-                ct = "multipart/byteranges; boundary=%s" % boundary
-                response.headers['Content-Type'] = ct
-                if "Content-Length" in response.headers:
-                    # Delete Content-Length header so finalize() recalcs it.
-                    del response.headers["Content-Length"]
-                
-                def file_ranges():
-                    # Apache compatibility:
-                    yield "\r\n"
-                    
-                    for start, stop in r:
-                        if debug:
-                            cherrypy.log('Multipart; start: %r, stop: %r' % (start, stop),
-                                         'TOOLS.STATIC')
-                        yield "--" + boundary
-                        yield "\r\nContent-type: %s" % content_type
-                        yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n"
-                               % (start, stop - 1, content_length))
-                        fileobj.seek(start)
-                        for chunk in file_generator_limited(fileobj, stop-start):
-                            yield chunk
-                        yield "\r\n"
-                    # Final boundary
-                    yield "--" + boundary + "--"
-                    
-                    # Apache compatibility:
-                    yield "\r\n"
-                response.body = file_ranges()
-            return response.body
-        else:
-            if debug:
-                cherrypy.log('No byteranges requested', 'TOOLS.STATIC')
-    
-    # Set Content-Length and use an iterable (file object)
-    #   this way CP won't load the whole file in memory
-    response.headers['Content-Length'] = content_length
-    response.body = fileobj
-    return response.body
-
-def serve_download(path, name=None):
-    """Serve 'path' as an application/x-download attachment."""
-    # This is such a common idiom I felt it deserved its own wrapper.
-    return serve_file(path, "application/x-download", "attachment", name)
-
-
-def _attempt(filename, content_types, debug=False):
-    if debug:
-        cherrypy.log('Attempting %r (content_types %r)' %
-                     (filename, content_types), 'TOOLS.STATICDIR')
-    try:
-        # you can set the content types for a
-        # complete directory per extension
-        content_type = None
-        if content_types:
-            r, ext = os.path.splitext(filename)
-            content_type = content_types.get(ext[1:], None)
-        serve_file(filename, content_type=content_type, debug=debug)
-        return True
-    except cherrypy.NotFound:
-        # If we didn't find the static file, continue handling the
-        # request. We might find a dynamic handler instead.
-        if debug:
-            cherrypy.log('NotFound', 'TOOLS.STATICFILE')
-        return False
-
-def staticdir(section, dir, root="", match="", content_types=None, index="",
-              debug=False):
-    """Serve a static resource from the given (root +) dir.
-    
-    If 'match' is given, request.path_info will be searched for the given
-    regular expression before attempting to serve static content.
-    
-    If content_types is given, it should be a Python dictionary of
-    {file-extension: content-type} pairs, where 'file-extension' is
-    a string (e.g. "gif") and 'content-type' is the value to write
-    out in the Content-Type response header (e.g. "image/gif").
-    
-    If 'index' is provided, it should be the (relative) name of a file to
-    serve for directory requests. For example, if the dir argument is
-    '/home/me', the Request-URI is 'myapp', and the index arg is
-    'index.html', the file '/home/me/myapp/index.html' will be sought.
-    """
-    request = cherrypy.serving.request
-    if request.method not in ('GET', 'HEAD'):
-        if debug:
-            cherrypy.log('request.method not GET or HEAD', 'TOOLS.STATICDIR')
-        return False
-    
-    if match and not re.search(match, request.path_info):
-        if debug:
-            cherrypy.log('request.path_info %r does not match pattern %r' %
-                         (request.path_info, match), 'TOOLS.STATICDIR')
-        return False
-    
-    # Allow the use of '~' to refer to a user's home directory.
-    dir = os.path.expanduser(dir)
-
-    # If dir is relative, make absolute using "root".
-    if not os.path.isabs(dir):
-        if not root:
-            msg = "Static dir requires an absolute dir (or root)."
-            if debug:
-                cherrypy.log(msg, 'TOOLS.STATICDIR')
-            raise ValueError(msg)
-        dir = os.path.join(root, dir)
-    
-    # Determine where we are in the object tree relative to 'section'
-    # (where the static tool was defined).
-    if section == 'global':
-        section = "/"
-    section = section.rstrip(r"\/")
-    branch = request.path_info[len(section) + 1:]
-    branch = unquote(branch.lstrip(r"\/"))
-    
-    # If branch is "", filename will end in a slash
-    filename = os.path.join(dir, branch)
-    if debug:
-        cherrypy.log('Checking file %r to fulfill %r' %
-                     (filename, request.path_info), 'TOOLS.STATICDIR')
-    
-    # There's a chance that the branch pulled from the URL might
-    # have ".." or similar uplevel attacks in it. Check that the final
-    # filename is a child of dir.
-    if not os.path.normpath(filename).startswith(os.path.normpath(dir)):
-        raise cherrypy.HTTPError(403) # Forbidden
-    
-    handled = _attempt(filename, content_types)
-    if not handled:
-        # Check for an index file if a folder was requested.
-        if index:
-            handled = _attempt(os.path.join(filename, index), content_types)
-            if handled:
-                request.is_index = filename[-1] in (r"\/")
-    return handled
-
-def staticfile(filename, root=None, match="", content_types=None, debug=False):
-    """Serve a static resource from the given (root +) filename.
-    
-    If 'match' is given, request.path_info will be searched for the given
-    regular expression before attempting to serve static content.
-    
-    If content_types is given, it should be a Python dictionary of
-    {file-extension: content-type} pairs, where 'file-extension' is
-    a string (e.g. "gif") and 'content-type' is the value to write
-    out in the Content-Type response header (e.g. "image/gif").
-    """
-    request = cherrypy.serving.request
-    if request.method not in ('GET', 'HEAD'):
-        if debug:
-            cherrypy.log('request.method not GET or HEAD', 'TOOLS.STATICFILE')
-        return False
-    
-    if match and not re.search(match, request.path_info):
-        if debug:
-            cherrypy.log('request.path_info %r does not match pattern %r' %
-                         (request.path_info, match), 'TOOLS.STATICFILE')
-        return False
-    
-    # If filename is relative, make absolute using "root".
-    if not os.path.isabs(filename):
-        if not root:
-            msg = "Static tool requires an absolute filename (got '%s')." % filename
-            if debug:
-                cherrypy.log(msg, 'TOOLS.STATICFILE')
-            raise ValueError(msg)
-        filename = os.path.join(root, filename)
-    
-    return _attempt(filename, content_types, debug=debug)
--- a/bundled/cherrypy/cherrypy/lib/xmlrpc.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-import sys
-
-import cherrypy
-
-
-def process_body():
-    """Return (params, method) from request body."""
-    try:
-        import xmlrpclib
-        return xmlrpclib.loads(cherrypy.request.body.read())
-    except Exception:
-        return ('ERROR PARAMS', ), 'ERRORMETHOD'
-
-
-def patched_path(path):
-    """Return 'path', doctored for RPC."""
-    if not path.endswith('/'):
-        path += '/'
-    if path.startswith('/RPC2/'):
-        # strip the first /rpc2
-        path = path[5:]
-    return path
-
-
-def _set_response(body):
-    # The XML-RPC spec (http://www.xmlrpc.com/spec) says:
-    # "Unless there's a lower-level error, always return 200 OK."
-    # Since Python's xmlrpclib interprets a non-200 response
-    # as a "Protocol Error", we'll just return 200 every time.
-    response = cherrypy.response
-    response.status = '200 OK'
-    response.body = body
-    response.headers['Content-Type'] = 'text/xml'
-    response.headers['Content-Length'] = len(body)
-
-
-def respond(body, encoding='utf-8', allow_none=0):
-    from xmlrpclib import Fault, dumps
-    if not isinstance(body, Fault):
-        body = (body,)
-    _set_response(dumps(body, methodresponse=1,
-                        encoding=encoding,
-                        allow_none=allow_none))
-
-def on_error(*args, **kwargs):
-    body = str(sys.exc_info()[1])
-    from xmlrpclib import Fault, dumps
-    _set_response(dumps(Fault(1, body)))
-
--- a/bundled/cherrypy/cherrypy/process/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-"""Site container for an HTTP server.
-
-A Web Site Process Bus object is used to connect applications, servers,
-and frameworks with site-wide services such as daemonization, process
-reload, signal handling, drop privileges, PID file management, logging
-for all of these, and many more.
-
-The 'plugins' module defines a few abstract and concrete services for
-use with the bus. Some use tool-specific channels; see the documentation
-for each class.
-"""
-
-from cherrypy.process.wspbus import bus
-from cherrypy.process import plugins, servers
--- a/bundled/cherrypy/cherrypy/process/plugins.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,562 +0,0 @@
-"""Site services for use with a Web Site Process Bus."""
-
-import os
-import re
-try:
-    set
-except NameError:
-    from sets import Set as set
-import signal as _signal
-import sys
-import time
-import thread
-import threading
-
-# _module__file__base is used by Autoreload to make
-# absolute any filenames retrieved from sys.modules which are not
-# already absolute paths.  This is to work around Python's quirk
-# of importing the startup script and using a relative filename
-# for it in sys.modules.
-#
-# Autoreload examines sys.modules afresh every time it runs. If an application
-# changes the current directory by executing os.chdir(), then the next time
-# Autoreload runs, it will not be able to find any filenames which are
-# not absolute paths, because the current directory is not the same as when the
-# module was first imported.  Autoreload will then wrongly conclude the file has
-# "changed", and initiate the shutdown/re-exec sequence.
-# See ticket #917.
-# For this workaround to have a decent probability of success, this module
-# needs to be imported as early as possible, before the app has much chance
-# to change the working directory.
-_module__file__base = os.getcwd()
-
-
-class SimplePlugin(object):
-    """Plugin base class which auto-subscribes methods for known channels."""
-    
-    def __init__(self, bus):
-        self.bus = bus
-    
-    def subscribe(self):
-        """Register this object as a (multi-channel) listener on the bus."""
-        for channel in self.bus.listeners:
-            # Subscribe self.start, self.exit, etc. if present.
-            method = getattr(self, channel, None)
-            if method is not None:
-                self.bus.subscribe(channel, method)
-    
-    def unsubscribe(self):
-        """Unregister this object as a listener on the bus."""
-        for channel in self.bus.listeners:
-            # Unsubscribe self.start, self.exit, etc. if present.
-            method = getattr(self, channel, None)
-            if method is not None:
-                self.bus.unsubscribe(channel, method)
-
-
-
-class SignalHandler(object):
-    """Register bus channels (and listeners) for system signals.
-    
-    By default, instantiating this object subscribes the following signals
-    and listeners:
-    
-        TERM: bus.exit
-        HUP : bus.restart
-        USR1: bus.graceful
-    """
-    
-    # Map from signal numbers to names
-    signals = {}
-    for k, v in vars(_signal).items():
-        if k.startswith('SIG') and not k.startswith('SIG_'):
-            signals[v] = k
-    del k, v
-    
-    def __init__(self, bus):
-        self.bus = bus
-        # Set default handlers
-        self.handlers = {'SIGTERM': self.bus.exit,
-                         'SIGHUP': self.handle_SIGHUP,
-                         'SIGUSR1': self.bus.graceful,
-                         }
-        
-        self._previous_handlers = {}
-    
-    def subscribe(self):
-        for sig, func in self.handlers.items():
-            try:
-                self.set_handler(sig, func)
-            except ValueError:
-                pass
-    
-    def unsubscribe(self):
-        for signum, handler in self._previous_handlers.items():
-            signame = self.signals[signum]
-            
-            if handler is None:
-                self.bus.log("Restoring %s handler to SIG_DFL." % signame)
-                handler = _signal.SIG_DFL
-            else:
-                self.bus.log("Restoring %s handler %r." % (signame, handler))
-            
-            try:
-                our_handler = _signal.signal(signum, handler)
-                if our_handler is None:
-                    self.bus.log("Restored old %s handler %r, but our "
-                                 "handler was not registered." %
-                                 (signame, handler), level=30)
-            except ValueError:
-                self.bus.log("Unable to restore %s handler %r." %
-                             (signame, handler), level=40, traceback=True)
-    
-    def set_handler(self, signal, listener=None):
-        """Subscribe a handler for the given signal (number or name).
-        
-        If the optional 'listener' argument is provided, it will be
-        subscribed as a listener for the given signal's channel.
-        
-        If the given signal name or number is not available on the current
-        platform, ValueError is raised.
-        """
-        if isinstance(signal, basestring):
-            signum = getattr(_signal, signal, None)
-            if signum is None:
-                raise ValueError("No such signal: %r" % signal)
-            signame = signal
-        else:
-            try:
-                signame = self.signals[signal]
-            except KeyError:
-                raise ValueError("No such signal: %r" % signal)
-            signum = signal
-        
-        prev = _signal.signal(signum, self._handle_signal)
-        self._previous_handlers[signum] = prev
-        
-        if listener is not None:
-            self.bus.log("Listening for %s." % signame)
-            self.bus.subscribe(signame, listener)
-    
-    def _handle_signal(self, signum=None, frame=None):
-        """Python signal handler (self.set_handler subscribes it for you)."""
-        signame = self.signals[signum]
-        self.bus.log("Caught signal %s." % signame)
-        self.bus.publish(signame)
-    
-    def handle_SIGHUP(self):
-        if os.isatty(sys.stdin.fileno()):
-            # not daemonized (may be foreground or background)
-            self.bus.log("SIGHUP caught but not daemonized. Exiting.")
-            self.bus.exit()
-        else:
-            self.bus.log("SIGHUP caught while daemonized. Restarting.")
-            self.bus.restart()
-
-
-try:
-    import pwd, grp
-except ImportError:
-    pwd, grp = None, None
-
-
-class DropPrivileges(SimplePlugin):
-    """Drop privileges. uid/gid arguments not available on Windows.
-    
-    Special thanks to Gavin Baker: http://antonym.org/node/100.
-    """
-    
-    def __init__(self, bus, umask=None, uid=None, gid=None):
-        SimplePlugin.__init__(self, bus)
-        self.finalized = False
-        self.uid = uid
-        self.gid = gid
-        self.umask = umask
-    
-    def _get_uid(self):
-        return self._uid
-    def _set_uid(self, val):
-        if val is not None:
-            if pwd is None:
-                self.bus.log("pwd module not available; ignoring uid.",
-                             level=30)
-                val = None
-            elif isinstance(val, basestring):
-                val = pwd.getpwnam(val)[2]
-        self._uid = val
-    uid = property(_get_uid, _set_uid, doc="The uid under which to run.")
-    
-    def _get_gid(self):
-        return self._gid
-    def _set_gid(self, val):
-        if val is not None:
-            if grp is None:
-                self.bus.log("grp module not available; ignoring gid.",
-                             level=30)
-                val = None
-            elif isinstance(val, basestring):
-                val = grp.getgrnam(val)[2]
-        self._gid = val
-    gid = property(_get_gid, _set_gid, doc="The gid under which to run.")
-    
-    def _get_umask(self):
-        return self._umask
-    def _set_umask(self, val):
-        if val is not None:
-            try:
-                os.umask
-            except AttributeError:
-                self.bus.log("umask function not available; ignoring umask.",
-                             level=30)
-                val = None
-        self._umask = val
-    umask = property(_get_umask, _set_umask, doc="The umask under which to run.")
-    
-    def start(self):
-        # uid/gid
-        def current_ids():
-            """Return the current (uid, gid) if available."""
-            name, group = None, None
-            if pwd:
-                name = pwd.getpwuid(os.getuid())[0]
-            if grp:
-                group = grp.getgrgid(os.getgid())[0]
-            return name, group
-        
-        if self.finalized:
-            if not (self.uid is None and self.gid is None):
-                self.bus.log('Already running as uid: %r gid: %r' %
-                             current_ids())
-        else:
-            if self.uid is None and self.gid is None:
-                if pwd or grp:
-                    self.bus.log('uid/gid not set', level=30)
-            else:
-                self.bus.log('Started as uid: %r gid: %r' % current_ids())
-                if self.gid is not None:
-                    os.setgid(self.gid)
-                if self.uid is not None:
-                    os.setuid(self.uid)
-                self.bus.log('Running as uid: %r gid: %r' % current_ids())
-        
-        # umask
-        if self.finalized:
-            if self.umask is not None:
-                self.bus.log('umask already set to: %03o' % self.umask)
-        else:
-            if self.umask is None:
-                self.bus.log('umask not set', level=30)
-            else:
-                old_umask = os.umask(self.umask)
-                self.bus.log('umask old: %03o, new: %03o' %
-                             (old_umask, self.umask))
-        
-        self.finalized = True
-    # This is slightly higher than the priority for server.start
-    # in order to facilitate the most common use: starting on a low
-    # port (which requires root) and then dropping to another user.
-    start.priority = 77
-
-
-class Daemonizer(SimplePlugin):
-    """Daemonize the running script.
-    
-    Use this with a Web Site Process Bus via:
-        
-        Daemonizer(bus).subscribe()
-    
-    When this component finishes, the process is completely decoupled from
-    the parent environment. Please note that when this component is used,
-    the return code from the parent process will still be 0 if a startup
-    error occurs in the forked children. Errors in the initial daemonizing
-    process still return proper exit codes. Therefore, if you use this
-    plugin to daemonize, don't use the return code as an accurate indicator
-    of whether the process fully started. In fact, that return code only
-    indicates if the process succesfully finished the first fork.
-    """
-    
-    def __init__(self, bus, stdin='/dev/null', stdout='/dev/null',
-                 stderr='/dev/null'):
-        SimplePlugin.__init__(self, bus)
-        self.stdin = stdin
-        self.stdout = stdout
-        self.stderr = stderr
-        self.finalized = False
-    
-    def start(self):
-        if self.finalized:
-            self.bus.log('Already deamonized.')
-        
-        # forking has issues with threads:
-        # http://www.opengroup.org/onlinepubs/000095399/functions/fork.html
-        # "The general problem with making fork() work in a multi-threaded
-        #  world is what to do with all of the threads..."
-        # So we check for active threads:
-        if threading.activeCount() != 1:
-            self.bus.log('There are %r active threads. '
-                         'Daemonizing now may cause strange failures.' %
-                         threading.enumerate(), level=30)
-        
-        # See http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
-        # (or http://www.faqs.org/faqs/unix-faq/programmer/faq/ section 1.7)
-        # and http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012
-        
-        # Finish up with the current stdout/stderr
-        sys.stdout.flush()
-        sys.stderr.flush()
-        
-        # Do first fork.
-        try:
-            pid = os.fork()
-            if pid == 0:
-                # This is the child process. Continue.
-                pass
-            else:
-                # This is the first parent. Exit, now that we've forked.
-                self.bus.log('Forking once.')
-                os._exit(0)
-        except OSError, exc:
-            # Python raises OSError rather than returning negative numbers.
-            sys.exit("%s: fork #1 failed: (%d) %s\n"
-                     % (sys.argv[0], exc.errno, exc.strerror))
-        
-        os.setsid()
-        
-        # Do second fork
-        try:
-            pid = os.fork()
-            if pid > 0:
-                self.bus.log('Forking twice.')
-                os._exit(0) # Exit second parent
-        except OSError, exc:
-            sys.exit("%s: fork #2 failed: (%d) %s\n"
-                     % (sys.argv[0], exc.errno, exc.strerror))
-        
-        os.chdir("/")
-        os.umask(0)
-        
-        si = open(self.stdin, "r")
-        so = open(self.stdout, "a+")
-        se = open(self.stderr, "a+")
-
-        # os.dup2(fd, fd2) will close fd2 if necessary,
-        # so we don't explicitly close stdin/out/err.
-        # See http://docs.python.org/lib/os-fd-ops.html
-        os.dup2(si.fileno(), sys.stdin.fileno())
-        os.dup2(so.fileno(), sys.stdout.fileno())
-        os.dup2(se.fileno(), sys.stderr.fileno())
-        
-        self.bus.log('Daemonized to PID: %s' % os.getpid())
-        self.finalized = True
-    start.priority = 65
-
-
-class PIDFile(SimplePlugin):
-    """Maintain a PID file via a WSPBus."""
-    
-    def __init__(self, bus, pidfile):
-        SimplePlugin.__init__(self, bus)
-        self.pidfile = pidfile
-        self.finalized = False
-    
-    def start(self):
-        pid = os.getpid()
-        if self.finalized:
-            self.bus.log('PID %r already written to %r.' % (pid, self.pidfile))
-        else:
-            open(self.pidfile, "wb").write(str(pid))
-            self.bus.log('PID %r written to %r.' % (pid, self.pidfile))
-            self.finalized = True
-    start.priority = 70
-    
-    def exit(self):
-        try:
-            os.remove(self.pidfile)
-            self.bus.log('PID file removed: %r.' % self.pidfile)
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except:
-            pass
-
-
-class PerpetualTimer(threading._Timer):
-    """A subclass of threading._Timer whose run() method repeats."""
-    
-    def run(self):
-        while True:
-            self.finished.wait(self.interval)
-            if self.finished.isSet():
-                return
-            try:
-                self.function(*self.args, **self.kwargs)
-            except Exception, x:
-                self.bus.log("Error in perpetual timer thread function %r." %
-                             self.function, level=40, traceback=True)
-                # Quit on first error to avoid massive logs.
-                raise
-
-
-class Monitor(SimplePlugin):
-    """WSPBus listener to periodically run a callback in its own thread.
-    
-    bus: a Web Site Process Bus object.
-    callback: the function to call at intervals.
-    frequency: the time in seconds between callback runs.
-    """
-    
-    frequency = 60
-    
-    def __init__(self, bus, callback, frequency=60, name=None):
-        SimplePlugin.__init__(self, bus)
-        self.callback = callback
-        self.frequency = frequency
-        self.thread = None
-        self.name = name
-    
-    def start(self):
-        """Start our callback in its own perpetual timer thread."""
-        if self.frequency > 0:
-            threadname = self.name or self.__class__.__name__
-            if self.thread is None:
-                self.thread = PerpetualTimer(self.frequency, self.callback)
-                self.thread.bus = self.bus
-                self.thread.setName(threadname)
-                self.thread.start()
-                self.bus.log("Started monitor thread %r." % threadname)
-            else:
-                self.bus.log("Monitor thread %r already started." % threadname)
-    start.priority = 70
-    
-    def stop(self):
-        """Stop our callback's perpetual timer thread."""
-        if self.thread is None:
-            self.bus.log("No thread running for %s." % self.name or self.__class__.__name__)
-        else:
-            if self.thread is not threading.currentThread():
-                name = self.thread.getName()
-                self.thread.cancel()
-                self.thread.join()
-                self.bus.log("Stopped thread %r." % name)
-            self.thread = None
-    
-    def graceful(self):
-        """Stop the callback's perpetual timer thread and restart it."""
-        self.stop()
-        self.start()
-
-
-class Autoreloader(Monitor):
-    """Monitor which re-executes the process when files change."""
-    
-    frequency = 1
-    match = '.*'
-    
-    def __init__(self, bus, frequency=1, match='.*'):
-        self.mtimes = {}
-        self.files = set()
-        self.match = match
-        Monitor.__init__(self, bus, self.run, frequency)
-    
-    def start(self):
-        """Start our own perpetual timer thread for self.run."""
-        if self.thread is None:
-            self.mtimes = {}
-        Monitor.start(self)
-    start.priority = 70 
-    
-    def sysfiles(self):
-        """Return a Set of filenames which the Autoreloader will monitor."""
-        files = set()
-        for k, m in sys.modules.items():
-            if re.match(self.match, k):
-                if hasattr(m, '__loader__') and hasattr(m.__loader__, 'archive'):
-                    f = m.__loader__.archive
-                else:
-                    f = getattr(m, '__file__', None)
-                    if f is not None and not os.path.isabs(f):
-                        # ensure absolute paths so a os.chdir() in the app doesn't break me
-                        f = os.path.normpath(os.path.join(_module__file__base, f))
-                files.add(f)
-        return files
-    
-    def run(self):
-        """Reload the process if registered files have been modified."""
-        for filename in self.sysfiles() | self.files:
-            if filename:
-                if filename.endswith('.pyc'):
-                    filename = filename[:-1]
-                
-                oldtime = self.mtimes.get(filename, 0)
-                if oldtime is None:
-                    # Module with no .py file. Skip it.
-                    continue
-                
-                try:
-                    mtime = os.stat(filename).st_mtime
-                except OSError:
-                    # Either a module with no .py file, or it's been deleted.
-                    mtime = None
-                
-                if filename not in self.mtimes:
-                    # If a module has no .py file, this will be None.
-                    self.mtimes[filename] = mtime
-                else:
-                    if mtime is None or mtime > oldtime:
-                        # The file has been deleted or modified.
-                        self.bus.log("Restarting because %s changed." % filename)
-                        self.thread.cancel()
-                        self.bus.log("Stopped thread %r." % self.thread.getName())
-                        self.bus.restart()
-                        return
-
-
-class ThreadManager(SimplePlugin):
-    """Manager for HTTP request threads.
-    
-    If you have control over thread creation and destruction, publish to
-    the 'acquire_thread' and 'release_thread' channels (for each thread).
-    This will register/unregister the current thread and publish to
-    'start_thread' and 'stop_thread' listeners in the bus as needed.
-    
-    If threads are created and destroyed by code you do not control
-    (e.g., Apache), then, at the beginning of every HTTP request,
-    publish to 'acquire_thread' only. You should not publish to
-    'release_thread' in this case, since you do not know whether
-    the thread will be re-used or not. The bus will call
-    'stop_thread' listeners for you when it stops.
-    """
-    
-    def __init__(self, bus):
-        self.threads = {}
-        SimplePlugin.__init__(self, bus)
-        self.bus.listeners.setdefault('acquire_thread', set())
-        self.bus.listeners.setdefault('release_thread', set())
-    
-    def acquire_thread(self):
-        """Run 'start_thread' listeners for the current thread.
-        
-        If the current thread has already been seen, any 'start_thread'
-        listeners will not be run again.
-        """
-        thread_ident = thread.get_ident()
-        if thread_ident not in self.threads:
-            # We can't just use _get_ident as the thread ID
-            # because some platforms reuse thread ID's.
-            i = len(self.threads) + 1
-            self.threads[thread_ident] = i
-            self.bus.publish('start_thread', i)
-    
-    def release_thread(self):
-        """Release the current thread and run 'stop_thread' listeners."""
-        thread_ident = threading._get_ident()
-        i = self.threads.pop(thread_ident, None)
-        if i is not None:
-            self.bus.publish('stop_thread', i)
-    
-    def stop(self):
-        """Release all threads and run all 'stop_thread' listeners."""
-        for thread_ident, i in self.threads.items():
-            self.bus.publish('stop_thread', i)
-        self.threads.clear()
-    graceful = stop
-
--- a/bundled/cherrypy/cherrypy/process/servers.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-"""Adapt an HTTP server."""
-
-import time
-
-
-class ServerAdapter(object):
-    """Adapter for an HTTP server.
-    
-    If you need to start more than one HTTP server (to serve on multiple
-    ports, or protocols, etc.), you can manually register each one and then
-    start them all with bus.start:
-    
-        s1 = ServerAdapter(bus, MyWSGIServer(host='0.0.0.0', port=80))
-        s2 = ServerAdapter(bus, another.HTTPServer(host='127.0.0.1', SSL=True))
-        s1.subscribe()
-        s2.subscribe()
-        bus.start()
-    """
-    
-    def __init__(self, bus, httpserver=None, bind_addr=None):
-        self.bus = bus
-        self.httpserver = httpserver
-        self.bind_addr = bind_addr
-        self.interrupt = None
-        self.running = False
-    
-    def subscribe(self):
-        self.bus.subscribe('start', self.start)
-        self.bus.subscribe('stop', self.stop)
-    
-    def unsubscribe(self):
-        self.bus.unsubscribe('start', self.start)
-        self.bus.unsubscribe('stop', self.stop)
-    
-    def start(self):
-        """Start the HTTP server."""
-        if self.bind_addr is None:
-            on_what = "unknown interface (dynamic?)"
-        elif isinstance(self.bind_addr, tuple):
-            host, port = self.bind_addr
-            on_what = "%s:%s" % (host, port)
-        else:
-            on_what = "socket file: %s" % self.bind_addr
-        
-        if self.running:
-            self.bus.log("Already serving on %s" % on_what)
-            return
-        
-        self.interrupt = None
-        if not self.httpserver:
-            raise ValueError("No HTTP server has been created.")
-        
-        # Start the httpserver in a new thread.
-        if isinstance(self.bind_addr, tuple):
-            wait_for_free_port(*self.bind_addr)
-        
-        import threading
-        t = threading.Thread(target=self._start_http_thread)
-        t.setName("HTTPServer " + t.getName())
-        t.start()
-        
-        self.wait()
-        self.running = True
-        self.bus.log("Serving on %s" % on_what)
-    start.priority = 75
-    
-    def _start_http_thread(self):
-        """HTTP servers MUST be running in new threads, so that the
-        main thread persists to receive KeyboardInterrupt's. If an
-        exception is raised in the httpserver's thread then it's
-        trapped here, and the bus (and therefore our httpserver)
-        are shut down.
-        """
-        try:
-            self.httpserver.start()
-        except KeyboardInterrupt, exc:
-            self.bus.log("<Ctrl-C> hit: shutting down HTTP server")
-            self.interrupt = exc
-            self.bus.exit()
-        except SystemExit, exc:
-            self.bus.log("SystemExit raised: shutting down HTTP server")
-            self.interrupt = exc
-            self.bus.exit()
-            raise
-        except:
-            import sys
-            self.interrupt = sys.exc_info()[1]
-            self.bus.log("Error in HTTP server: shutting down",
-                         traceback=True, level=40)
-            self.bus.exit()
-            raise
-    
-    def wait(self):
-        """Wait until the HTTP server is ready to receive requests."""
-        while not getattr(self.httpserver, "ready", False):
-            if self.interrupt:
-                raise self.interrupt
-            time.sleep(.1)
-        
-        # Wait for port to be occupied
-        if isinstance(self.bind_addr, tuple):
-            host, port = self.bind_addr
-            wait_for_occupied_port(host, port)
-    
-    def stop(self):
-        """Stop the HTTP server."""
-        if self.running:
-            # stop() MUST block until the server is *truly* stopped.
-            self.httpserver.stop()
-            # Wait for the socket to be truly freed.
-            if isinstance(self.bind_addr, tuple):
-                wait_for_free_port(*self.bind_addr)
-            self.running = False
-            self.bus.log("HTTP Server %s shut down" % self.httpserver)
-        else:
-            self.bus.log("HTTP Server %s already shut down" % self.httpserver)
-    stop.priority = 25
-    
-    def restart(self):
-        """Restart the HTTP server."""
-        self.stop()
-        self.start()
-
-
-class FlupFCGIServer(object):
-    """Adapter for a flup.server.fcgi.WSGIServer."""
-    
-    def __init__(self, *args, **kwargs):
-        if kwargs.get('bindAddress', None) is None:
-            import socket
-            if not hasattr(socket.socket, 'fromfd'):
-                raise ValueError(
-                    'Dynamic FCGI server not available on this platform. '
-                    'You must use a static or external one by providing a '
-                    'legal bindAddress.')
-        self.args = args
-        self.kwargs = kwargs
-        self.ready = False
-    
-    def start(self):
-        """Start the FCGI server."""
-        # We have to instantiate the server class here because its __init__
-        # starts a threadpool. If we do it too early, daemonize won't work.
-        from flup.server.fcgi import WSGIServer
-        self.fcgiserver = WSGIServer(*self.args, **self.kwargs)
-        # TODO: report this bug upstream to flup.
-        # If we don't set _oldSIGs on Windows, we get:
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 108, in run
-        #     self._restoreSignalHandlers()
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 156, in _restoreSignalHandlers
-        #     for signum,handler in self._oldSIGs:
-        #   AttributeError: 'WSGIServer' object has no attribute '_oldSIGs'
-        self.fcgiserver._installSignalHandlers = lambda: None
-        self.fcgiserver._oldSIGs = []
-        self.ready = True
-        self.fcgiserver.run()
-    
-    def stop(self):
-        """Stop the HTTP server."""
-        # Forcibly stop the fcgi server main event loop.
-        self.fcgiserver._keepGoing = False
-        # Force all worker threads to die off.
-        self.fcgiserver._threadPool.maxSpare = self.fcgiserver._threadPool._idleCount
-        self.ready = False
-
-
-class FlupSCGIServer(object):
-    """Adapter for a flup.server.scgi.WSGIServer."""
-    
-    def __init__(self, *args, **kwargs):
-        self.args = args
-        self.kwargs = kwargs
-        self.ready = False
-    
-    def start(self):
-        """Start the SCGI server."""
-        # We have to instantiate the server class here because its __init__
-        # starts a threadpool. If we do it too early, daemonize won't work.
-        from flup.server.scgi import WSGIServer
-        self.scgiserver = WSGIServer(*self.args, **self.kwargs)
-        # TODO: report this bug upstream to flup.
-        # If we don't set _oldSIGs on Windows, we get:
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 108, in run
-        #     self._restoreSignalHandlers()
-        #   File "C:\Python24\Lib\site-packages\flup\server\threadedserver.py",
-        #   line 156, in _restoreSignalHandlers
-        #     for signum,handler in self._oldSIGs:
-        #   AttributeError: 'WSGIServer' object has no attribute '_oldSIGs'
-        self.scgiserver._installSignalHandlers = lambda: None
-        self.scgiserver._oldSIGs = []
-        self.ready = True
-        self.scgiserver.run()
-    
-    def stop(self):
-        """Stop the HTTP server."""
-        self.ready = False
-        # Forcibly stop the scgi server main event loop.
-        self.scgiserver._keepGoing = False
-        # Force all worker threads to die off.
-        self.scgiserver._threadPool.maxSpare = 0
-
-
-def client_host(server_host):
-    """Return the host on which a client can connect to the given listener."""
-    if server_host == '0.0.0.0':
-        # 0.0.0.0 is INADDR_ANY, which should answer on localhost.
-        return '127.0.0.1'
-    if server_host == '::':
-        # :: is IN6ADDR_ANY, which should answer on localhost.
-        return '::1'
-    return server_host
-
-def check_port(host, port, timeout=1.0):
-    """Raise an error if the given port is not free on the given host."""
-    if not host:
-        raise ValueError("Host values of '' or None are not allowed.")
-    host = client_host(host)
-    port = int(port)
-    
-    import socket
-    
-    # AF_INET or AF_INET6 socket
-    # Get the correct address family for our host (allows IPv6 addresses)
-    try:
-        info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                  socket.SOCK_STREAM)
-    except socket.gaierror:
-        if ':' in host:
-            info = [(socket.AF_INET6, socket.SOCK_STREAM, 0, "", (host, port, 0, 0))]
-        else:
-            info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", (host, port))]
-    
-    for res in info:
-        af, socktype, proto, canonname, sa = res
-        s = None
-        try:
-            s = socket.socket(af, socktype, proto)
-            # See http://groups.google.com/group/cherrypy-users/
-            #        browse_frm/thread/bbfe5eb39c904fe0
-            s.settimeout(timeout)
-            s.connect((host, port))
-            s.close()
-            raise IOError("Port %s is in use on %s; perhaps the previous "
-                          "httpserver did not shut down properly." %
-                          (repr(port), repr(host)))
-        except socket.error:
-            if s:
-                s.close()
-
-def wait_for_free_port(host, port):
-    """Wait for the specified port to become free (drop requests)."""
-    if not host:
-        raise ValueError("Host values of '' or None are not allowed.")
-    
-    for trial in range(50):
-        try:
-            # we are expecting a free port, so reduce the timeout
-            check_port(host, port, timeout=0.1)
-        except IOError:
-            # Give the old server thread time to free the port.
-            time.sleep(0.1)
-        else:
-            return
-    
-    raise IOError("Port %r not free on %r" % (port, host))
-
-def wait_for_occupied_port(host, port):
-    """Wait for the specified port to become active (receive requests)."""
-    if not host:
-        raise ValueError("Host values of '' or None are not allowed.")
-    
-    for trial in range(50):
-        try:
-            check_port(host, port)
-        except IOError:
-            return
-        else:
-            time.sleep(.1)
-    
-    raise IOError("Port %r not bound on %r" % (port, host))
--- a/bundled/cherrypy/cherrypy/process/win32.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-"""Windows service. Requires pywin32."""
-
-import os
-import win32api
-import win32con
-import win32event
-import win32service
-import win32serviceutil
-
-from cherrypy.process import wspbus, plugins
-
-
-class ConsoleCtrlHandler(plugins.SimplePlugin):
-    """A WSPBus plugin for handling Win32 console events (like Ctrl-C)."""
-    
-    def __init__(self, bus):
-        self.is_set = False
-        plugins.SimplePlugin.__init__(self, bus)
-    
-    def start(self):
-        if self.is_set:
-            self.bus.log('Handler for console events already set.', level=40)
-            return
-        
-        result = win32api.SetConsoleCtrlHandler(self.handle, 1)
-        if result == 0:
-            self.bus.log('Could not SetConsoleCtrlHandler (error %r)' %
-                         win32api.GetLastError(), level=40)
-        else:
-            self.bus.log('Set handler for console events.', level=40)
-            self.is_set = True
-    
-    def stop(self):
-        if not self.is_set:
-            self.bus.log('Handler for console events already off.', level=40)
-            return
-        
-        try:
-            result = win32api.SetConsoleCtrlHandler(self.handle, 0)
-        except ValueError:
-            # "ValueError: The object has not been registered"
-            result = 1
-        
-        if result == 0:
-            self.bus.log('Could not remove SetConsoleCtrlHandler (error %r)' %
-                         win32api.GetLastError(), level=40)
-        else:
-            self.bus.log('Removed handler for console events.', level=40)
-            self.is_set = False
-    
-    def handle(self, event):
-        """Handle console control events (like Ctrl-C)."""
-        if event in (win32con.CTRL_C_EVENT, win32con.CTRL_LOGOFF_EVENT,
-                     win32con.CTRL_BREAK_EVENT, win32con.CTRL_SHUTDOWN_EVENT,
-                     win32con.CTRL_CLOSE_EVENT):
-            self.bus.log('Console event %s: shutting down bus' % event)
-            
-            # Remove self immediately so repeated Ctrl-C doesn't re-call it.
-            try:
-                self.stop()
-            except ValueError:
-                pass
-            
-            self.bus.exit()
-            # 'First to return True stops the calls'
-            return 1
-        return 0
-
-
-class Win32Bus(wspbus.Bus):
-    """A Web Site Process Bus implementation for Win32.
-    
-    Instead of time.sleep, this bus blocks using native win32event objects.
-    """
-    
-    def __init__(self):
-        self.events = {}
-        wspbus.Bus.__init__(self)
-    
-    def _get_state_event(self, state):
-        """Return a win32event for the given state (creating it if needed)."""
-        try:
-            return self.events[state]
-        except KeyError:
-            event = win32event.CreateEvent(None, 0, 0,
-                                           "WSPBus %s Event (pid=%r)" %
-                                           (state.name, os.getpid()))
-            self.events[state] = event
-            return event
-    
-    def _get_state(self):
-        return self._state
-    def _set_state(self, value):
-        self._state = value
-        event = self._get_state_event(value)
-        win32event.PulseEvent(event)
-    state = property(_get_state, _set_state)
-    
-    def wait(self, state, interval=0.1, channel=None):
-        """Wait for the given state(s), KeyboardInterrupt or SystemExit.
-        
-        Since this class uses native win32event objects, the interval
-        argument is ignored.
-        """
-        if isinstance(state, (tuple, list)):
-            # Don't wait for an event that beat us to the punch ;)
-            if self.state not in state:
-                events = tuple([self._get_state_event(s) for s in state])
-                win32event.WaitForMultipleObjects(events, 0, win32event.INFINITE)
-        else:
-            # Don't wait for an event that beat us to the punch ;)
-            if self.state != state:
-                event = self._get_state_event(state)
-                win32event.WaitForSingleObject(event, win32event.INFINITE)
-
-
-class _ControlCodes(dict):
-    """Control codes used to "signal" a service via ControlService.
-    
-    User-defined control codes are in the range 128-255. We generally use
-    the standard Python value for the Linux signal and add 128. Example:
-    
-        >>> signal.SIGUSR1
-        10
-        control_codes['graceful'] = 128 + 10
-    """
-    
-    def key_for(self, obj):
-        """For the given value, return its corresponding key."""
-        for key, val in self.items():
-            if val is obj:
-                return key
-        raise ValueError("The given object could not be found: %r" % obj)
-
-control_codes = _ControlCodes({'graceful': 138})
-
-
-def signal_child(service, command):
-    if command == 'stop':
-        win32serviceutil.StopService(service)
-    elif command == 'restart':
-        win32serviceutil.RestartService(service)
-    else:
-        win32serviceutil.ControlService(service, control_codes[command])
-
-
-class PyWebService(win32serviceutil.ServiceFramework):
-    """Python Web Service."""
-    
-    _svc_name_ = "Python Web Service"
-    _svc_display_name_ = "Python Web Service"
-    _svc_deps_ = None        # sequence of service names on which this depends
-    _exe_name_ = "pywebsvc"
-    _exe_args_ = None        # Default to no arguments
-    
-    # Only exists on Windows 2000 or later, ignored on windows NT
-    _svc_description_ = "Python Web Service"
-    
-    def SvcDoRun(self):
-        from cherrypy import process
-        process.bus.start()
-        process.bus.block()
-    
-    def SvcStop(self):
-        from cherrypy import process
-        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-        process.bus.exit()
-    
-    def SvcOther(self, control):
-        process.bus.publish(control_codes.key_for(control))
-
-
-if __name__ == '__main__':
-    win32serviceutil.HandleCommandLine(PyWebService)
--- a/bundled/cherrypy/cherrypy/process/wspbus.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,384 +0,0 @@
-"""An implementation of the Web Site Process Bus.
-
-This module is completely standalone, depending only on the stdlib.
-
-Web Site Process Bus
---------------------
-
-A Bus object is used to contain and manage site-wide behavior:
-daemonization, HTTP server start/stop, process reload, signal handling,
-drop privileges, PID file management, logging for all of these,
-and many more.
-
-In addition, a Bus object provides a place for each web framework
-to register code that runs in response to site-wide events (like
-process start and stop), or which controls or otherwise interacts with
-the site-wide components mentioned above. For example, a framework which
-uses file-based templates would add known template filenames to an
-autoreload component.
-
-Ideally, a Bus object will be flexible enough to be useful in a variety
-of invocation scenarios:
-
- 1. The deployer starts a site from the command line via a framework-
-     neutral deployment script; applications from multiple frameworks
-     are mixed in a single site. Command-line arguments and configuration
-     files are used to define site-wide components such as the HTTP server,
-     WSGI component graph, autoreload behavior, signal handling, etc.
- 2. The deployer starts a site via some other process, such as Apache;
-     applications from multiple frameworks are mixed in a single site.
-     Autoreload and signal handling (from Python at least) are disabled.
- 3. The deployer starts a site via a framework-specific mechanism;
-     for example, when running tests, exploring tutorials, or deploying
-     single applications from a single framework. The framework controls
-     which site-wide components are enabled as it sees fit.
-
-The Bus object in this package uses topic-based publish-subscribe
-messaging to accomplish all this. A few topic channels are built in
-('start', 'stop', 'exit', 'graceful', 'log', and 'main'). Frameworks and
-site containers are free to define their own. If a message is sent to a
-channel that has not been defined or has no listeners, there is no effect.
-
-In general, there should only ever be a single Bus object per process.
-Frameworks and site containers share a single Bus object by publishing
-messages and subscribing listeners.
-
-The Bus object works as a finite state machine which models the current
-state of the process. Bus methods move it from one state to another;
-those methods then publish to subscribed listeners on the channel for
-the new state.
-
-                        O
-                        |
-                        V
-       STOPPING --> STOPPED --> EXITING -> X
-          A   A         |
-          |    \___     |
-          |        \    |
-          |         V   V
-        STARTED <-- STARTING
-
-"""
-
-import atexit
-import os
-try:
-    set
-except NameError:
-    from sets import Set as set
-import sys
-import threading
-import time
-import traceback as _traceback
-import warnings
-
-# Here I save the value of os.getcwd(), which, if I am imported early enough,
-# will be the directory from which the startup script was run.  This is needed
-# by _do_execv(), to change back to the original directory before execv()ing a
-# new process.  This is a defense against the application having changed the
-# current working directory (which could make sys.executable "not found" if
-# sys.executable is a relative-path, and/or cause other problems).
-_startup_cwd = os.getcwd()
-
-class ChannelFailures(Exception):
-    delimiter = '\n'
-    
-    def __init__(self, *args, **kwargs):
-        # Don't use 'super' here; Exceptions are old-style in Py2.4
-        # See http://www.cherrypy.org/ticket/959
-        Exception.__init__(self, *args, **kwargs)
-        self._exceptions = list()
-    
-    def handle_exception(self):
-        self._exceptions.append(sys.exc_info())
-    
-    def get_instances(self):
-        return [instance for cls, instance, traceback in self._exceptions]
-    
-    def __str__(self):
-        exception_strings = map(repr, self.get_instances())
-        return self.delimiter.join(exception_strings)
-    
-    def __nonzero__(self):
-        return bool(self._exceptions)
-
-# Use a flag to indicate the state of the bus.
-class _StateEnum(object):
-    class State(object):
-        name = None
-        def __repr__(self):
-            return "states.%s" % self.name
-    
-    def __setattr__(self, key, value):
-        if isinstance(value, self.State):
-            value.name = key
-        object.__setattr__(self, key, value)
-states = _StateEnum()
-states.STOPPED = states.State()
-states.STARTING = states.State()
-states.STARTED = states.State()
-states.STOPPING = states.State()
-states.EXITING = states.State()
-
-
-class Bus(object):
-    """Process state-machine and messenger for HTTP site deployment.
-    
-    All listeners for a given channel are guaranteed to be called even
-    if others at the same channel fail. Each failure is logged, but
-    execution proceeds on to the next listener. The only way to stop all
-    processing from inside a listener is to raise SystemExit and stop the
-    whole server.
-    """
-    
-    states = states
-    state = states.STOPPED
-    execv = False
-    
-    def __init__(self):
-        self.execv = False
-        self.state = states.STOPPED
-        self.listeners = dict(
-            [(channel, set()) for channel
-             in ('start', 'stop', 'exit', 'graceful', 'log', 'main')])
-        self._priorities = {}
-    
-    def subscribe(self, channel, callback, priority=None):
-        """Add the given callback at the given channel (if not present)."""
-        if channel not in self.listeners:
-            self.listeners[channel] = set()
-        self.listeners[channel].add(callback)
-        
-        if priority is None:
-            priority = getattr(callback, 'priority', 50)
-        self._priorities[(channel, callback)] = priority
-    
-    def unsubscribe(self, channel, callback):
-        """Discard the given callback (if present)."""
-        listeners = self.listeners.get(channel)
-        if listeners and callback in listeners:
-            listeners.discard(callback)
-            del self._priorities[(channel, callback)]
-    
-    def publish(self, channel, *args, **kwargs):
-        """Return output of all subscribers for the given channel."""
-        if channel not in self.listeners:
-            return []
-        
-        exc = ChannelFailures()
-        output = []
-        
-        items = [(self._priorities[(channel, listener)], listener)
-                 for listener in self.listeners[channel]]
-        items.sort()
-        for priority, listener in items:
-            try:
-                output.append(listener(*args, **kwargs))
-            except KeyboardInterrupt:
-                raise
-            except SystemExit, e:
-                # If we have previous errors ensure the exit code is non-zero
-                if exc and e.code == 0:
-                    e.code = 1
-                raise
-            except:
-                exc.handle_exception()
-                if channel == 'log':
-                    # Assume any further messages to 'log' will fail.
-                    pass
-                else:
-                    self.log("Error in %r listener %r" % (channel, listener),
-                             level=40, traceback=True)
-        if exc:
-            raise exc
-        return output
-    
-    def _clean_exit(self):
-        """An atexit handler which asserts the Bus is not running."""
-        if self.state != states.EXITING:
-            warnings.warn(
-                "The main thread is exiting, but the Bus is in the %r state; "
-                "shutting it down automatically now. You must either call "
-                "bus.block() after start(), or call bus.exit() before the "
-                "main thread exits." % self.state, RuntimeWarning)
-            self.exit()
-    
-    def start(self):
-        """Start all services."""
-        atexit.register(self._clean_exit)
-        
-        self.state = states.STARTING
-        self.log('Bus STARTING')
-        try:
-            self.publish('start')
-            self.state = states.STARTED
-            self.log('Bus STARTED')
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except:
-            self.log("Shutting down due to error in start listener:",
-                     level=40, traceback=True)
-            e_info = sys.exc_info()
-            try:
-                self.exit()
-            except:
-                # Any stop/exit errors will be logged inside publish().
-                pass
-            raise e_info[0], e_info[1], e_info[2]
-    
-    def exit(self):
-        """Stop all services and prepare to exit the process."""
-        exitstate = self.state
-        try:
-            self.stop()
-            
-            self.state = states.EXITING
-            self.log('Bus EXITING')
-            self.publish('exit')
-            # This isn't strictly necessary, but it's better than seeing
-            # "Waiting for child threads to terminate..." and then nothing.
-            self.log('Bus EXITED')
-        except:
-            # This method is often called asynchronously (whether thread,
-            # signal handler, console handler, or atexit handler), so we
-            # can't just let exceptions propagate out unhandled.
-            # Assume it's been logged and just die.
-            os._exit(70) # EX_SOFTWARE
-        
-        if exitstate == states.STARTING:
-            # exit() was called before start() finished, possibly due to
-            # Ctrl-C because a start listener got stuck. In this case,
-            # we could get stuck in a loop where Ctrl-C never exits the
-            # process, so we just call os.exit here.
-            os._exit(70) # EX_SOFTWARE
-    
-    def restart(self):
-        """Restart the process (may close connections).
-        
-        This method does not restart the process from the calling thread;
-        instead, it stops the bus and asks the main thread to call execv.
-        """
-        self.execv = True
-        self.exit()
-    
-    def graceful(self):
-        """Advise all services to reload."""
-        self.log('Bus graceful')
-        self.publish('graceful')
-    
-    def block(self, interval=0.1):
-        """Wait for the EXITING state, KeyboardInterrupt or SystemExit.
-        
-        This function is intended to be called only by the main thread.
-        After waiting for the EXITING state, it also waits for all threads
-        to terminate, and then calls os.execv if self.execv is True. This
-        design allows another thread to call bus.restart, yet have the main
-        thread perform the actual execv call (required on some platforms).
-        """
-        try:
-            self.wait(states.EXITING, interval=interval, channel='main')
-        except (KeyboardInterrupt, IOError):
-            # The time.sleep call might raise
-            # "IOError: [Errno 4] Interrupted function call" on KBInt.
-            self.log('Keyboard Interrupt: shutting down bus')
-            self.exit()
-        except SystemExit:
-            self.log('SystemExit raised: shutting down bus')
-            self.exit()
-            raise
-        
-        # Waiting for ALL child threads to finish is necessary on OS X.
-        # See http://www.cherrypy.org/ticket/581.
-        # It's also good to let them all shut down before allowing
-        # the main thread to call atexit handlers.
-        # See http://www.cherrypy.org/ticket/751.
-        self.log("Waiting for child threads to terminate...")
-        for t in threading.enumerate():
-            if t != threading.currentThread() and t.isAlive():
-                # Note that any dummy (external) threads are always daemonic.
-                if hasattr(threading.Thread, "daemon"):
-                    # Python 2.6+
-                    d = t.daemon
-                else:
-                    d = t.isDaemon()
-                if not d:
-                    t.join()
-        
-        if self.execv:
-            self._do_execv()
-    
-    def wait(self, state, interval=0.1, channel=None):
-        """Wait for the given state(s)."""
-        if isinstance(state, (tuple, list)):
-            states = state
-        else:
-            states = [state]
-        
-        def _wait():
-            while self.state not in states:
-                time.sleep(interval)
-                self.publish(channel)
-        
-        # From http://psyco.sourceforge.net/psycoguide/bugs.html:
-        # "The compiled machine code does not include the regular polling
-        # done by Python, meaning that a KeyboardInterrupt will not be
-        # detected before execution comes back to the regular Python
-        # interpreter. Your program cannot be interrupted if caught
-        # into an infinite Psyco-compiled loop."
-        try:
-            sys.modules['psyco'].cannotcompile(_wait)
-        except (KeyError, AttributeError):
-            pass
-        
-        _wait()
-    
-    def _do_execv(self):
-        """Re-execute the current process.
-        
-        This must be called from the main thread, because certain platforms
-        (OS X) don't allow execv to be called in a child thread very well.
-        """
-        args = sys.argv[:]
-        self.log('Re-spawning %s' % ' '.join(args))
-        args.insert(0, sys.executable)
-        if sys.platform == 'win32':
-            args = ['"%s"' % arg for arg in args]
-
-        os.chdir(_startup_cwd)
-        os.execv(sys.executable, args)
-    
-    def stop(self):
-        """Stop all services."""
-        self.state = states.STOPPING
-        self.log('Bus STOPPING')
-        self.publish('stop')
-        self.state = states.STOPPED
-        self.log('Bus STOPPED')
-    
-    def start_with_callback(self, func, args=None, kwargs=None):
-        """Start 'func' in a new thread T, then start self (and return T)."""
-        if args is None:
-            args = ()
-        if kwargs is None:
-            kwargs = {}
-        args = (func,) + args
-        
-        def _callback(func, *a, **kw):
-            self.wait(states.STARTED)
-            func(*a, **kw)
-        t = threading.Thread(target=_callback, args=args, kwargs=kwargs)
-        t.setName('Bus Callback ' + t.getName())
-        t.start()
-        
-        self.start()
-        
-        return t
-    
-    def log(self, msg="", level=20, traceback=False):
-        """Log the given message. Append the last traceback if requested."""
-        if traceback:
-            exc = sys.exc_info()
-            msg += "\n" + "".join(_traceback.format_exception(*exc))
-        self.publish('log', msg, level)
-
-bus = Bus()
--- a/bundled/cherrypy/cherrypy/scaffold/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-"""<MyProject>, a CherryPy application.
-
-Use this as a base for creating new CherryPy applications. When you want
-to make a new app, copy and paste this folder to some other location
-(maybe site-packages) and rename it to the name of your project,
-then tweak as desired.
-
-Even before any tweaking, this should serve a few demonstration pages.
-Change to this directory and run:
-
-    ../cherryd -c site.conf
-
-"""
-
-import cherrypy
-from cherrypy import tools, url
-
-import os
-local_dir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-class Root:
-    
-    _cp_config = {'tools.log_tracebacks.on': True,
-                  }
-    
-    def index(self):
-        return """<html>
-<body>Try some <a href='%s?a=7'>other</a> path,
-or a <a href='%s?n=14'>default</a> path.<br />
-Or, just look at the pretty picture:<br />
-<img src='%s' />
-</body></html>""" % (url("other"), url("else"),
-                     url("files/made_with_cherrypy_small.png"))
-    index.exposed = True
-    
-    def default(self, *args, **kwargs):
-        return "args: %s kwargs: %s" % (args, kwargs)
-    default.exposed = True
-    
-    def other(self, a=2, b='bananas', c=None):
-        cherrypy.response.headers['Content-Type'] = 'text/plain'
-        if c is None:
-            return "Have %d %s." % (int(a), b)
-        else:
-            return "Have %d %s, %s." % (int(a), b, c)
-    other.exposed = True
-    
-    files = cherrypy.tools.staticdir.handler(
-                section="/files",
-                dir=os.path.join(local_dir, "static"),
-                # Ignore .php files, etc.
-                match=r'\.(css|gif|html?|ico|jpe?g|js|png|swf|xml)$',
-                )
-
-
-root = Root()
-
-# Uncomment the following to use your own favicon instead of CP's default.
-#favicon_path = os.path.join(local_dir, "favicon.ico")
-#root.favicon_ico = tools.staticfile.handler(filename=favicon_path)
--- a/bundled/cherrypy/cherrypy/scaffold/apache-fcgi.conf	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-# Apache2 server conf file for using CherryPy with mod_fcgid.
-
-# This doesn't have to be "C:/", but it has to be a directory somewhere, and
-# MUST match the directory used in the FastCgiExternalServer directive, below.
-DocumentRoot "C:/"
-
-ServerName 127.0.0.1
-Listen 80
-LoadModule fastcgi_module modules/mod_fastcgi.dll
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-# Send requests for any URI to our fastcgi handler.
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-
-# The FastCgiExternalServer directive defines filename as an external FastCGI application.
-# If filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot.
-# The filename does not have to exist in the local filesystem. URIs that Apache resolves to this
-# filename will be handled by this external FastCGI application.
-FastCgiExternalServer "C:/fastcgi.pyc" -host 127.0.0.1:8088
\ No newline at end of file
--- a/bundled/cherrypy/cherrypy/scaffold/example.conf	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-[/]
-log.error_file: "error.log"
-log.access_file: "access.log"
\ No newline at end of file
--- a/bundled/cherrypy/cherrypy/scaffold/site.conf	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-[global]
-# Uncomment this when you're done developing
-#environment: "production"
-
-server.socket_host: "0.0.0.0"
-server.socket_port: 8088
-
-# Uncomment the following lines to run on HTTPS at the same time
-#server.2.socket_host: "0.0.0.0"
-#server.2.socket_port: 8433
-#server.2.ssl_certificate: '../test/test.pem'
-#server.2.ssl_private_key: '../test/test.pem'
-
-tree.myapp: cherrypy.Application(scaffold.root, "/", "example.conf")
Binary file bundled/cherrypy/cherrypy/scaffold/static/made_with_cherrypy_small.png has changed
--- a/bundled/cherrypy/cherrypy/test/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-"""Regression test suite for CherryPy.
-
-Run test.py to exercise all tests.
-"""
-
--- a/bundled/cherrypy/cherrypy/test/benchmark.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,408 +0,0 @@
-"""CherryPy Benchmark Tool
-
-    Usage:
-        benchmark.py --null --notests --help --cpmodpy --modpython --ab=path --apache=path
-    
-    --null:        use a null Request object (to bench the HTTP server only)
-    --notests:     start the server but do not run the tests; this allows
-                   you to check the tested pages with a browser
-    --help:        show this help message
-    --cpmodpy:     run tests via apache on 8080 (with the builtin _cpmodpy)
-    --modpython:   run tests via apache on 8080 (with modpython_gateway)
-    --ab=path:     Use the ab script/executable at 'path' (see below)
-    --apache=path: Use the apache script/exe at 'path' (see below)
-    
-    To run the benchmarks, the Apache Benchmark tool "ab" must either be on
-    your system path, or specified via the --ab=path option.
-    
-    To run the modpython tests, the "apache" executable or script must be
-    on your system path, or provided via the --apache=path option. On some
-    platforms, "apache" may be called "apachectl" or "apache2ctl"--create
-    a symlink to them if needed.
-"""
-
-import getopt
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-import re
-import sys
-import time
-import traceback
-
-import cherrypy
-from cherrypy import _cperror, _cpmodpy
-from cherrypy.lib import httputil
-
-
-AB_PATH = ""
-APACHE_PATH = "apache"
-SCRIPT_NAME = "/cpbench/users/rdelon/apps/blog"
-
-__all__ = ['ABSession', 'Root', 'print_report',
-           'run_standard_benchmarks', 'safe_threads',
-           'size_report', 'startup', 'thread_report',
-           ]
-
-size_cache = {}
-
-class Root:
-    
-    def index(self):
-        return """<html>
-<head>
-    <title>CherryPy Benchmark</title>
-</head>
-<body>
-    <ul>
-        <li><a href="hello">Hello, world! (14 byte dynamic)</a></li>
-        <li><a href="static/index.html">Static file (14 bytes static)</a></li>
-        <li><form action="sizer">Response of length:
-            <input type='text' name='size' value='10' /></form>
-        </li>
-    </ul>
-</body>
-</html>"""
-    index.exposed = True
-    
-    def hello(self):
-        return "Hello, world\r\n"
-    hello.exposed = True
-    
-    def sizer(self, size):
-        resp = size_cache.get(size, None)
-        if resp is None:
-            size_cache[size] = resp = "X" * int(size)
-        return resp
-    sizer.exposed = True
-
-
-cherrypy.config.update({
-    'log.error.file': '',
-    'environment': 'production',
-    'server.socket_host': '127.0.0.1',
-    'server.socket_port': 8080,
-    'server.max_request_header_size': 0,
-    'server.max_request_body_size': 0,
-    'engine.deadlock_poll_freq': 0,
-    })
-
-# Cheat mode on ;)
-del cherrypy.config['tools.log_tracebacks.on']
-del cherrypy.config['tools.log_headers.on']
-del cherrypy.config['tools.trailing_slash.on']
-
-appconf = {
-    '/static': {
-        'tools.staticdir.on': True,
-        'tools.staticdir.dir': 'static',
-        'tools.staticdir.root': curdir,
-        },
-    }
-app = cherrypy.tree.mount(Root(), SCRIPT_NAME, appconf)
-
-
-class NullRequest:
-    """A null HTTP request class, returning 204 and an empty body."""
-    
-    def __init__(self, local, remote, scheme="http"):
-        pass
-    
-    def close(self):
-        pass
-    
-    def run(self, method, path, query_string, protocol, headers, rfile):
-        cherrypy.response.status = "204 No Content"
-        cherrypy.response.header_list = [("Content-Type", 'text/html'),
-                                         ("Server", "Null CherryPy"),
-                                         ("Date", httputil.HTTPDate()),
-                                         ("Content-Length", "0"),
-                                         ]
-        cherrypy.response.body = [""]
-        return cherrypy.response
-
-
-class NullResponse:
-    pass
-
-
-class ABSession:
-    """A session of 'ab', the Apache HTTP server benchmarking tool.
-
-Example output from ab:
-
-This is ApacheBench, Version 2.0.40-dev <$Revision: 1.121.2.1 $> apache-2.0
-Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
-Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
-
-Benchmarking 127.0.0.1 (be patient)
-Completed 100 requests
-Completed 200 requests
-Completed 300 requests
-Completed 400 requests
-Completed 500 requests
-Completed 600 requests
-Completed 700 requests
-Completed 800 requests
-Completed 900 requests
-
-
-Server Software:        CherryPy/3.1beta
-Server Hostname:        127.0.0.1
-Server Port:            8080
-
-Document Path:          /static/index.html
-Document Length:        14 bytes
-
-Concurrency Level:      10
-Time taken for tests:   9.643867 seconds
-Complete requests:      1000
-Failed requests:        0
-Write errors:           0
-Total transferred:      189000 bytes
-HTML transferred:       14000 bytes
-Requests per second:    103.69 [#/sec] (mean)
-Time per request:       96.439 [ms] (mean)
-Time per request:       9.644 [ms] (mean, across all concurrent requests)
-Transfer rate:          19.08 [Kbytes/sec] received
-
-Connection Times (ms)
-              min  mean[+/-sd] median   max
-Connect:        0    0   2.9      0      10
-Processing:    20   94   7.3     90     130
-Waiting:        0   43  28.1     40     100
-Total:         20   95   7.3    100     130
-
-Percentage of the requests served within a certain time (ms)
-  50%    100
-  66%    100
-  75%    100
-  80%    100
-  90%    100
-  95%    100
-  98%    100
-  99%    110
- 100%    130 (longest request)
-Finished 1000 requests
-"""
-    
-    parse_patterns = [('complete_requests', 'Completed',
-                       r'^Complete requests:\s*(\d+)'),
-                      ('failed_requests', 'Failed',
-                       r'^Failed requests:\s*(\d+)'),
-                      ('requests_per_second', 'req/sec',
-                       r'^Requests per second:\s*([0-9.]+)'),
-                      ('time_per_request_concurrent', 'msec/req',
-                       r'^Time per request:\s*([0-9.]+).*concurrent requests\)$'),
-                      ('transfer_rate', 'KB/sec',
-                       r'^Transfer rate:\s*([0-9.]+)'),
-                      ]
-    
-    def __init__(self, path=SCRIPT_NAME + "/hello", requests=1000, concurrency=10):
-        self.path = path
-        self.requests = requests
-        self.concurrency = concurrency
-    
-    def args(self):
-        port = cherrypy.server.socket_port
-        assert self.concurrency > 0
-        assert self.requests > 0
-        # Don't use "localhost".
-        # Cf http://mail.python.org/pipermail/python-win32/2008-March/007050.html
-        return ("-k -n %s -c %s http://127.0.0.1:%s%s" %
-                (self.requests, self.concurrency, port, self.path))
-    
-    def run(self):
-        # Parse output of ab, setting attributes on self
-        try:
-            self.output = _cpmodpy.read_process(AB_PATH or "ab", self.args())
-        except:
-            print _cperror.format_exc()
-            raise
-        
-        for attr, name, pattern in self.parse_patterns:
-            val = re.search(pattern, self.output, re.MULTILINE)
-            if val:
-                val = val.group(1)
-                setattr(self, attr, val)
-            else:
-                setattr(self, attr, None)
-
-
-safe_threads = (25, 50, 100, 200, 400)
-if sys.platform in ("win32",):
-    # For some reason, ab crashes with > 50 threads on my Win2k laptop.
-    safe_threads = (10, 20, 30, 40, 50)
-
-
-def thread_report(path=SCRIPT_NAME + "/hello", concurrency=safe_threads):
-    sess = ABSession(path)
-    attrs, names, patterns = list(zip(*sess.parse_patterns))
-    avg = dict.fromkeys(attrs, 0.0)
-    
-    rows = [('threads',) + names]
-    for c in concurrency:
-        sess.concurrency = c
-        sess.run()
-        row = [c]
-        for attr in attrs:
-            val = float(getattr(sess, attr))
-            avg[attr] += float(val)
-            row.append(val)
-        rows.append(row)
-    
-    # Add a row of averages.
-    rows.append(["Average"] + [str(avg[attr] / len(concurrency)) for attr in attrs])
-    return rows
-
-def size_report(sizes=(10, 100, 1000, 10000, 100000, 100000000),
-               concurrency=50):
-    sess = ABSession(concurrency=concurrency)
-    attrs, names, patterns = list(zip(*sess.parse_patterns))
-    rows = [('bytes',) + names]
-    for sz in sizes:
-        sess.path = "%s/sizer?size=%s" % (SCRIPT_NAME, sz)
-        sess.run()
-        rows.append([sz] + [getattr(sess, attr) for attr in attrs])
-    return rows
-
-def print_report(rows):
-    widths = []
-    for i in range(len(rows[0])):
-        lengths = [len(str(row[i])) for row in rows]
-        widths.append(max(lengths))
-    for row in rows:
-        print("")
-        for i, val in enumerate(row):
-            print str(val).rjust(widths[i]), "|",
-    print("")
-
-
-def run_standard_benchmarks():
-    print("")
-    print("Client Thread Report (1000 requests, 14 byte response body, "
-           "%s server threads):" % cherrypy.server.thread_pool)
-    print_report(thread_report())
-    
-    print("")
-    print("Client Thread Report (1000 requests, 14 bytes via staticdir, "
-           "%s server threads):" % cherrypy.server.thread_pool)
-    print_report(thread_report("%s/static/index.html" % SCRIPT_NAME))
-    
-    print("")
-    print("Size Report (1000 requests, 50 client threads, "
-           "%s server threads):" % cherrypy.server.thread_pool)
-    print_report(size_report())
-
-
-#                         modpython and other WSGI                         #
-
-def startup_modpython(req=None):
-    """Start the CherryPy app server in 'serverless' mode (for modpython/WSGI)."""
-    if cherrypy.engine.state == cherrypy._cpengine.STOPPED:
-        if req:
-            if "nullreq" in req.get_options():
-                cherrypy.engine.request_class = NullRequest
-                cherrypy.engine.response_class = NullResponse
-            ab_opt = req.get_options().get("ab", "")
-            if ab_opt:
-                global AB_PATH
-                AB_PATH = ab_opt
-        cherrypy.engine.start()
-    if cherrypy.engine.state == cherrypy._cpengine.STARTING:
-        cherrypy.engine.wait()
-    return 0 # apache.OK
-
-
-def run_modpython(use_wsgi=False):
-    print("Starting mod_python...")
-    pyopts = []
-    
-    # Pass the null and ab=path options through Apache
-    if "--null" in opts:
-        pyopts.append(("nullreq", ""))
-    
-    if "--ab" in opts:
-        pyopts.append(("ab", opts["--ab"]))
-    
-    s = _cpmodpy.ModPythonServer
-    if use_wsgi:
-        pyopts.append(("wsgi.application", "cherrypy::tree"))
-        pyopts.append(("wsgi.startup", "cherrypy.test.benchmark::startup_modpython"))
-        handler = "modpython_gateway::handler"
-        s = s(port=8080, opts=pyopts, apache_path=APACHE_PATH, handler=handler)
-    else:
-        pyopts.append(("cherrypy.setup", "cherrypy.test.benchmark::startup_modpython"))
-        s = s(port=8080, opts=pyopts, apache_path=APACHE_PATH)
-    
-    try:
-        s.start()
-        run()
-    finally:
-        s.stop()
-
-
-
-if __name__ == '__main__':
-    longopts = ['cpmodpy', 'modpython', 'null', 'notests',
-                'help', 'ab=', 'apache=']
-    try:
-        switches, args = getopt.getopt(sys.argv[1:], "", longopts)
-        opts = dict(switches)
-    except getopt.GetoptError:
-        print(__doc__)
-        sys.exit(2)
-    
-    if "--help" in opts:
-        print(__doc__)
-        sys.exit(0)
-    
-    if "--ab" in opts:
-        AB_PATH = opts['--ab']
-    
-    if "--notests" in opts:
-        # Return without stopping the server, so that the pages
-        # can be tested from a standard web browser.
-        def run():
-            port = cherrypy.server.socket_port
-            print("You may now open http://127.0.0.1:%s%s/" %
-                   (port, SCRIPT_NAME))
-            
-            if "--null" in opts:
-                print("Using null Request object")
-    else:
-        def run():
-            end = time.time() - start
-            print("Started in %s seconds" % end)
-            if "--null" in opts:
-                print("\nUsing null Request object")
-            try:
-                try:
-                    run_standard_benchmarks()
-                except:
-                    print _cperror.format_exc()
-                    raise
-            finally:
-                cherrypy.engine.exit()
-    
-    print("Starting CherryPy app server...")
-    
-    class NullWriter(object):
-        """Suppresses the printing of socket errors."""
-        def write(self, data):
-            pass
-    sys.stderr = NullWriter()
-    
-    start = time.time()
-    
-    if "--cpmodpy" in opts:
-        run_modpython()
-    elif "--modpython" in opts:
-        run_modpython(use_wsgi=True)
-    else:
-        if "--null" in opts:
-            cherrypy.server.request_class = NullRequest
-            cherrypy.server.response_class = NullResponse
-        
-        cherrypy.engine.start_with_callback(run)
-        cherrypy.engine.block()
--- a/bundled/cherrypy/cherrypy/test/checkerdemo.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-"""Demonstration app for cherrypy.checker.
-
-This application is intentionally broken and badly designed.
-To demonstrate the output of the CherryPy Checker, simply execute
-this module.
-"""
-
-import os
-import cherrypy
-thisdir = os.path.dirname(os.path.abspath(__file__))
-
-class Root:
-    pass
-
-if __name__ == '__main__':
-    conf = {'/base': {'tools.staticdir.root': thisdir,
-                      # Obsolete key.
-                      'throw_errors': True,
-                      },
-            # This entry should be OK.
-            '/base/static': {'tools.staticdir.on': True,
-                        'tools.staticdir.dir': 'static'},
-            # Warn on missing folder.
-            '/base/js': {'tools.staticdir.on': True,
-                    'tools.staticdir.dir': 'js'},
-            # Warn on dir with an abs path even though we provide root.
-            '/base/static2': {'tools.staticdir.on': True,
-                         'tools.staticdir.dir': '/static'},
-            # Warn on dir with a relative path with no root.
-            '/static3': {'tools.staticdir.on': True,
-                         'tools.staticdir.dir': 'static'},
-            # Warn on unknown namespace
-            '/unknown': {'toobles.gzip.on': True},
-            # Warn special on cherrypy.<known ns>.*
-            '/cpknown': {'cherrypy.tools.encode.on': True},
-            # Warn on mismatched types
-            '/conftype': {'request.show_tracebacks': 14},
-            # Warn on unknown tool.
-            '/web': {'tools.unknown.on': True},
-            # Warn on server.* in app config.
-            '/app1': {'server.socket_host': '0.0.0.0'},
-            # Warn on 'localhost'
-            'global': {'server.socket_host': 'localhost'},
-            # Warn on '[name]'
-            '[/extra_brackets]': {},
-            }
-    cherrypy.quickstart(Root(), config=conf)
--- a/bundled/cherrypy/cherrypy/test/fcgi.conf	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-
-# Apache2 server conf file for testing CherryPy with mod_fcgid.
-
-DocumentRoot "C:\Python25\Lib\site-packages\cherrypy\test"
-ServerName 127.0.0.1
-Listen 8080
-LoadModule fastcgi_module modules/mod_fastcgi.dll
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "C:\\Python25\\Lib\\site-packages\\cherrypy\\test\\fastcgi.pyc" -host 127.0.0.1:4000
--- a/bundled/cherrypy/cherrypy/test/helper.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
-"""A library of helper functions for the CherryPy test suite.
-
-The actual script that runs the entire CP test suite is called
-"test.py" (in this folder); test.py calls this module as a library.
-
-Usage
-=====
-Each individual test_*.py module imports this module (helper),
-usually to make an instance of CPWebCase, and then call testmain().
-
-The CP test suite script (test.py) imports this module and calls
-run_test_suite, possibly more than once. CP applications may also
-import test.py (to use TestHarness), which then calls helper.py.
-"""
-
-# GREAT CARE has been taken to separate this module from test.py,
-# because different consumers of each have mutually-exclusive import
-# requirements. So don't go moving functions from here into test.py,
-# or vice-versa, unless you *really* know what you're doing.
-
-import datetime
-import os
-thisdir = os.path.abspath(os.path.dirname(__file__))
-import re
-import sys
-import time
-import warnings
-
-import cherrypy
-from cherrypy.lib import httputil, profiler
-from cherrypy.test import webtest
-
-
-class CPWebCase(webtest.WebCase):
-    
-    script_name = ""
-    scheme = "http"
-    
-    def prefix(self):
-        return self.script_name.rstrip("/")
-    
-    def base(self):
-        if ((self.scheme == "http" and self.PORT == 80) or
-            (self.scheme == "https" and self.PORT == 443)):
-            port = ""
-        else:
-            port = ":%s" % self.PORT
-        
-        return "%s://%s%s%s" % (self.scheme, self.HOST, port,
-                                self.script_name.rstrip("/"))
-    
-    def exit(self):
-        sys.exit()
-    
-    def getPage(self, url, headers=None, method="GET", body=None, protocol=None):
-        """Open the url. Return status, headers, body."""
-        if self.script_name:
-            url = httputil.urljoin(self.script_name, url)
-        return webtest.WebCase.getPage(self, url, headers, method, body, protocol)
-    
-    def skip(self, msg='skipped '):
-        sys.stderr.write(msg)
-    
-    def assertErrorPage(self, status, message=None, pattern=''):
-        """Compare the response body with a built in error page.
-        
-        The function will optionally look for the regexp pattern,
-        within the exception embedded in the error page."""
-        
-        # This will never contain a traceback
-        page = cherrypy._cperror.get_error_page(status, message=message)
-        
-        # First, test the response body without checking the traceback.
-        # Stick a match-all group (.*) in to grab the traceback.
-        esc = re.escape
-        epage = esc(page)
-        epage = epage.replace(esc('<pre id="traceback"></pre>'),
-                              esc('<pre id="traceback">') + '(.*)' + esc('</pre>'))
-        m = re.match(epage, self.body, re.DOTALL)
-        if not m:
-            self._handlewebError('Error page does not match; expected:\n' + page)
-            return
-        
-        # Now test the pattern against the traceback
-        if pattern is None:
-            # Special-case None to mean that there should be *no* traceback.
-            if m and m.group(1):
-                self._handlewebError('Error page contains traceback')
-        else:
-            if (m is None) or (
-                not re.search(re.escape(pattern),
-                              m.group(1))):
-                msg = 'Error page does not contain %s in traceback'
-                self._handlewebError(msg % repr(pattern))
-    
-    date_tolerance = 2
-    
-    def assertEqualDates(self, dt1, dt2, seconds=None):
-        """Assert abs(dt1 - dt2) is within Y seconds."""
-        if seconds is None:
-            seconds = self.date_tolerance
-        
-        if dt1 > dt2:
-            diff = dt1 - dt2
-        else:
-            diff = dt2 - dt1
-        if not diff < datetime.timedelta(seconds=seconds):
-            raise AssertionError('%r and %r are not within %r seconds.' %
-                                 (dt1, dt2, seconds))
-
-
-CPTestLoader = webtest.ReloadingTestLoader()
-CPTestRunner = webtest.TerseTestRunner(verbosity=2)
-
-
-def run_test_suite(moduleNames, conf, supervisor):
-    """Run the given test modules using the given supervisor and [global] conf.
-    
-    The 'supervisor' arg should be an object with 'start' and 'stop' methods.
-    See test/test.py.
-    """
-    # The Pybots automatic testing system needs the suite to exit
-    # with a non-zero value if there were any problems.
-    test_success = True
-    
-    for testmod in moduleNames:
-        cherrypy.config.reset()
-        cherrypy.config.update(conf)
-        setup_client(supervisor)
-        
-        if '.' in testmod:
-            package, test_name = testmod.rsplit('.', 1)
-            p = __import__(package, globals(), locals(), fromlist=[test_name])
-            m = getattr(p, test_name)
-        else:
-            m = __import__(testmod, globals(), locals())
-        suite = CPTestLoader.loadTestsFromName(testmod)
-        
-        setup = getattr(m, "setup_server", None)
-        if setup: supervisor.start(testmod)
-        try:
-            result = CPTestRunner.run(suite)
-            test_success &= result.wasSuccessful()
-        finally:
-            if setup: supervisor.stop()
-    
-    if test_success:
-        return 0
-    else:
-        return 1
-
-def setup_client(supervisor):
-    """Set up the WebCase classes to match the server's socket settings."""
-    webtest.WebCase.PORT = cherrypy.server.socket_port
-    webtest.WebCase.HOST = cherrypy.server.socket_host
-    if cherrypy.server.ssl_certificate:
-        CPWebCase.scheme = 'https'
-
-def testmain(conf=None):
-    """Run __main__ as a test module, with webtest debugging."""
-    # Comment me out to see ENGINE messages etc. when running a test standalone.
-    cherrypy.config.update({'environment': "test_suite"})
-    cherrypy.server.socket_host = '127.0.0.1'
-    
-    from cherrypy.test.test import LocalWSGISupervisor
-    supervisor = LocalWSGISupervisor(host=cherrypy.server.socket_host,
-                                     port=cherrypy.server.socket_port)
-    setup_client(supervisor)
-    supervisor.start('__main__')
-    try:
-        return webtest.main()
-    finally:
-        supervisor.stop()
-
-
-
-# --------------------------- Spawning helpers --------------------------- #
-
-
-class CPProcess(object):
-    
-    pid_file = os.path.join(thisdir, 'test.pid')
-    config_file = os.path.join(thisdir, 'test.conf')
-    config_template = """[global]
-server.socket_host: '%(host)s'
-server.socket_port: %(port)s
-checker.on: False
-log.screen: False
-log.error_file: r'%(error_log)s'
-log.access_file: r'%(access_log)s'
-%(ssl)s
-%(extra)s
-"""
-    error_log = os.path.join(thisdir, 'test.error.log')
-    access_log = os.path.join(thisdir, 'test.access.log')
-    
-    def __init__(self, wait=False, daemonize=False, ssl=False, socket_host=None, socket_port=None):
-        self.wait = wait
-        self.daemonize = daemonize
-        self.ssl = ssl
-        self.host = socket_host or cherrypy.server.socket_host
-        self.port = socket_port or cherrypy.server.socket_port
-    
-    def write_conf(self, extra=""):
-        if self.ssl:
-            serverpem = os.path.join(thisdir, 'test.pem')
-            ssl = """
-server.ssl_certificate: r'%s'
-server.ssl_private_key: r'%s'
-""" % (serverpem, serverpem)
-        else:
-            ssl = ""
-        
-        conf = self.config_template % {
-            'host': self.host,
-            'port': self.port,
-            'error_log': self.error_log,
-            'access_log': self.access_log,
-            'ssl': ssl,
-            'extra': extra,
-            }
-        f = open(self.config_file, 'wb')
-        f.write(conf)
-        f.close()
-    
-    def start(self, imports=None):
-        """Start cherryd in a subprocess."""
-        cherrypy._cpserver.wait_for_free_port(self.host, self.port)
-        
-        args = [sys.executable, os.path.join(thisdir, '..', 'cherryd'),
-                '-c', self.config_file, '-p', self.pid_file]
-        
-        if not isinstance(imports, (list, tuple)):
-            imports = [imports]
-        for i in imports:
-            if i:
-                args.append('-i')
-                args.append(i)
-        
-        if self.daemonize:
-            args.append('-d')
-        
-        if self.wait:
-            self.exit_code = os.spawnl(os.P_WAIT, sys.executable, *args)
-        else:
-            os.spawnl(os.P_NOWAIT, sys.executable, *args)
-            cherrypy._cpserver.wait_for_occupied_port(self.host, self.port)
-        
-        # Give the engine a wee bit more time to finish STARTING
-        if self.daemonize:
-            time.sleep(2)
-        else:
-            time.sleep(1)
-    
-    def get_pid(self):
-        return int(open(self.pid_file, 'rb').read())
-    
-    def join(self):
-        """Wait for the process to exit."""
-        try:
-            try:
-                # Mac, UNIX
-                os.wait()
-            except AttributeError:
-                # Windows
-                try:
-                    pid = self.get_pid()
-                except IOError:
-                    # Assume the subprocess deleted the pidfile on shutdown.
-                    pass
-                else:
-                    os.waitpid(pid, 0)
-        except OSError, x:
-            if x.args != (10, 'No child processes'):
-                raise
-
--- a/bundled/cherrypy/cherrypy/test/logtest.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-"""logtest, a unittest.TestCase helper for testing log output."""
-
-import sys
-import time
-
-import cherrypy
-
-
-try:
-    # On Windows, msvcrt.getch reads a single char without output.
-    import msvcrt
-    def getchar():
-        return msvcrt.getch()
-except ImportError:
-    # Unix getchr
-    import tty, termios
-    def getchar():
-        fd = sys.stdin.fileno()
-        old_settings = termios.tcgetattr(fd)
-        try:
-            tty.setraw(sys.stdin.fileno())
-            ch = sys.stdin.read(1)
-        finally:
-            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
-        return ch
-
-
-class LogCase(object):
-    """unittest.TestCase mixin for testing log messages.
-    
-    logfile: a filename for the desired log. Yes, I know modes are evil,
-        but it makes the test functions so much cleaner to set this once.
-    
-    lastmarker: the last marker in the log. This can be used to search for
-        messages since the last marker.
-    
-    markerPrefix: a string with which to prefix log markers. This should be
-        unique enough from normal log output to use for marker identification.
-    """
-    
-    logfile = None
-    lastmarker = None
-    markerPrefix = "test suite marker: "
-    
-    def _handleLogError(self, msg, data, marker, pattern):
-        print("")
-        print("    ERROR: %s" % msg)
-        
-        if not self.interactive:
-            raise self.failureException(msg)
-        
-        p = "    Show: [L]og [M]arker [P]attern; [I]gnore, [R]aise, or sys.e[X]it >> "
-        print p,
-        # ARGH
-        sys.stdout.flush()
-        while True:
-            i = getchar().upper()
-            if i not in "MPLIRX":
-                continue
-            print(i.upper())  # Also prints new line
-            if i == "L":
-                for x, line in enumerate(data):
-                    if (x + 1) % self.console_height == 0:
-                        # The \r and comma should make the next line overwrite
-                        print "<-- More -->\r",
-                        m = getchar().lower()
-                        # Erase our "More" prompt
-                        print "            \r",
-                        if m == "q":
-                            break
-                    print(line.rstrip())
-            elif i == "M":
-                print(repr(marker or self.lastmarker))
-            elif i == "P":
-                print(repr(pattern))
-            elif i == "I":
-                # return without raising the normal exception
-                return
-            elif i == "R":
-                raise self.failureException(msg)
-            elif i == "X":
-                self.exit()
-            print p,
-    
-    def exit(self):
-        sys.exit()
-    
-    def emptyLog(self):
-        """Overwrite self.logfile with 0 bytes."""
-        open(self.logfile, 'wb').write("")
-    
-    def markLog(self, key=None):
-        """Insert a marker line into the log and set self.lastmarker."""
-        if key is None:
-            key = str(time.time())
-        self.lastmarker = key
-        
-        open(self.logfile, 'ab+').write("%s%s\n" % (self.markerPrefix, key))
-    
-    def _read_marked_region(self, marker=None):
-        """Return lines from self.logfile in the marked region.
-        
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be returned.
-        """
-##        # Give the logger time to finish writing?
-##        time.sleep(0.5)
-        
-        logfile = self.logfile
-        marker = marker or self.lastmarker
-        if marker is None:
-            return open(logfile, 'rb').readlines()
-        
-        data = []
-        in_region = False
-        for line in open(logfile, 'rb'):
-            if in_region:
-                if (line.startswith(self.markerPrefix) and not marker in line):
-                    break
-                else:
-                    data.append(line)
-            elif marker in line:
-                in_region = True
-        return data
-    
-    def assertInLog(self, line, marker=None):
-        """Fail if the given (partial) line is not in the log.
-        
-        The log will be searched from the given marker to the next marker.
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be searched.
-        """
-        data = self._read_marked_region(marker)
-        for logline in data:
-            if line in logline:
-                return
-        msg = "%r not found in log" % line
-        self._handleLogError(msg, data, marker, line)
-    
-    def assertNotInLog(self, line, marker=None):
-        """Fail if the given (partial) line is in the log.
-        
-        The log will be searched from the given marker to the next marker.
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be searched.
-        """
-        data = self._read_marked_region(marker)
-        for logline in data:
-            if line in logline:
-                msg = "%r found in log" % line
-                self._handleLogError(msg, data, marker, line)
-    
-    def assertLog(self, sliceargs, lines, marker=None):
-        """Fail if log.readlines()[sliceargs] is not contained in 'lines'.
-        
-        The log will be searched from the given marker to the next marker.
-        If marker is None, self.lastmarker is used. If the log hasn't
-        been marked (using self.markLog), the entire log will be searched.
-        """
-        data = self._read_marked_region(marker)
-        if isinstance(sliceargs, int):
-            # Single arg. Use __getitem__ and allow lines to be str or list.
-            if isinstance(lines, (tuple, list)):
-                lines = lines[0]
-            if lines not in data[sliceargs]:
-                msg = "%r not found on log line %r" % (lines, sliceargs)
-                self._handleLogError(msg, [data[sliceargs]], marker, lines)
-        else:
-            # Multiple args. Use __getslice__ and require lines to be list.
-            if isinstance(lines, tuple):
-                lines = list(lines)
-            elif isinstance(lines, basestring):
-                raise TypeError("The 'lines' arg must be a list when "
-                                "'sliceargs' is a tuple.")
-            
-            start, stop = sliceargs
-            for line, logline in zip(lines, data[start:stop]):
-                if line not in logline:
-                    msg = "%r not found in log" % line
-                    self._handleLogError(msg, data[start:stop], marker, line)
-
--- a/bundled/cherrypy/cherrypy/test/modfcgid.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-"""Wrapper for mod_fcgid, for use as a CherryPy HTTP server when testing.
-
-To autostart fcgid, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl", "apache2ctl",
-or "httpd"--create a symlink to them if needed.
-
-You'll also need the WSGIServer from flup.servers.
-See http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
-    output is then truncated again by Apache. See test_core.testRanges.
-    This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
-    See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
-    CherryPy may set "304 Not modified" but Apache will write out
-    "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-    Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
-    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
-    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
-    mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
-    no response entity body. This isn't really a bug; it just differs from
-    the CherryPy default.
-"""
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import re
-import sys
-import time
-
-import cherrypy
-from cherrypy.process import plugins, servers
-from cherrypy.test import test
-
-
-def read_process(cmd, args=""):
-    pipein, pipeout = os.popen4("%s %s" % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r"(not recognized|No such file|not found)", firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-APACHE_PATH = "httpd"
-CONF_PATH = "fcgi.conf"
-
-conf_fcgid = """
-# Apache2 server conf file for testing CherryPy with mod_fcgid.
-
-DocumentRoot "%(root)s"
-ServerName 127.0.0.1
-Listen %(port)s
-LoadModule fastcgi_module modules/mod_fastcgi.dll
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "%(server)s" -host 127.0.0.1:4000
-"""
-
-class ModFCGISupervisor(test.LocalSupervisor):
-    
-    using_apache = True
-    using_wsgi = True
-    template = conf_fcgid
-    
-    def __str__(self):
-        return "FCGI Server on %s:%s" % (self.host, self.port)
-    
-    def start(self, modulename):
-        cherrypy.server.httpserver = servers.FlupFCGIServer(
-            application=cherrypy.tree, bindAddress=('127.0.0.1', 4000))
-        cherrypy.server.httpserver.bind_addr = ('127.0.0.1', 4000)
-        # For FCGI, we both start apache...
-        self.start_apache()
-        # ...and our local server
-        test.LocalServer.start(self, modulename)
-    
-    def start_apache(self):
-        fcgiconf = CONF_PATH
-        if not os.path.isabs(fcgiconf):
-            fcgiconf = os.path.join(curdir, fcgiconf)
-        
-        # Write the Apache conf file.
-        f = open(fcgiconf, 'wb')
-        try:
-            server = repr(os.path.join(curdir, 'fastcgi.pyc'))[1:-1]
-            output = self.template % {'port': self.port, 'root': curdir,
-                                      'server': server}
-            output = output.replace('\r\n', '\n')
-            f.write(output)
-        finally:
-            f.close()
-        
-        result = read_process(APACHE_PATH, "-k start -f %s" % fcgiconf)
-        if result:
-            print(result)
-    
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        read_process(APACHE_PATH, "-k stop")
-        test.LocalServer.stop(self)
-    
-    def sync_apps(self):
-        cherrypy.server.httpserver.fcgiserver.application = self.get_app()
-
--- a/bundled/cherrypy/cherrypy/test/modpy.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-"""Wrapper for mod_python, for use as a CherryPy HTTP server when testing.
-
-To autostart modpython, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl" or "apache2ctl"--
-create a symlink to them if needed.
-
-If you wish to test the WSGI interface instead of our _cpmodpy interface,
-you also need the 'modpython_gateway' module at:
-http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
-    output is then truncated again by Apache. See test_core.testRanges.
-    This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
-    See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
-    CherryPy may set "304 Not modified" but Apache will write out
-    "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-    Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
-    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
-    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
-    mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
-    no response entity body. This isn't really a bug; it just differs from
-    the CherryPy default.
-"""
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import re
-import time
-
-from cherrypy.test import test
-
-
-def read_process(cmd, args=""):
-    pipein, pipeout = os.popen4("%s %s" % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r"(not recognized|No such file|not found)", firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-APACHE_PATH = "httpd"
-CONF_PATH = "test_mp.conf"
-
-conf_modpython_gateway = """
-# Apache2 server conf file for testing CherryPy with modpython_gateway.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-SetHandler python-program
-PythonFixupHandler cherrypy.test.modpy::wsgisetup
-PythonOption testmod %(modulename)s
-PythonHandler modpython_gateway::handler
-PythonOption wsgi.application cherrypy::tree
-PythonOption socket_host %(host)s
-PythonDebug On
-"""
-
-conf_cpmodpy = """
-# Apache2 server conf file for testing CherryPy with _cpmodpy.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-SetHandler python-program
-PythonFixupHandler cherrypy.test.modpy::cpmodpysetup
-PythonHandler cherrypy._cpmodpy::handler
-PythonOption cherrypy.setup cherrypy.test.%(modulename)s::setup_server
-PythonOption socket_host %(host)s
-PythonDebug On
-"""
-
-class ModPythonSupervisor(test.Supervisor):
-    
-    using_apache = True
-    using_wsgi = False
-    template = None
-    
-    def __str__(self):
-        return "ModPython Server on %s:%s" % (self.host, self.port)
-    
-    def start(self, modulename):
-        mpconf = CONF_PATH
-        if not os.path.isabs(mpconf):
-            mpconf = os.path.join(curdir, mpconf)
-        
-        f = open(mpconf, 'wb')
-        try:
-            f.write(self.template %
-                    {'port': self.port, 'modulename': modulename,
-                     'host': self.host})
-        finally:
-            f.close()
-        
-        result = read_process(APACHE_PATH, "-k start -f %s" % mpconf)
-        if result:
-            print(result)
-    
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        read_process(APACHE_PATH, "-k stop")
-
-
-loaded = False
-def wsgisetup(req):
-    global loaded
-    if not loaded:
-        loaded = True
-        options = req.get_options()
-        
-        import cherrypy
-        cherrypy.config.update({
-            "log.error_file": os.path.join(curdir, "test.log"),
-            "environment": "test_suite",
-            "server.socket_host": options['socket_host'],
-            })
-        
-        modname = options['testmod']
-        mod = __import__(modname, globals(), locals(), [''])
-        mod.setup_server()
-        
-        cherrypy.server.unsubscribe()
-        cherrypy.engine.start()
-    from mod_python import apache
-    return apache.OK
-
-
-def cpmodpysetup(req):
-    global loaded
-    if not loaded:
-        loaded = True
-        options = req.get_options()
-        
-        import cherrypy
-        cherrypy.config.update({
-            "log.error_file": os.path.join(curdir, "test.log"),
-            "environment": "test_suite",
-            "server.socket_host": options['socket_host'],
-            })
-    from mod_python import apache
-    return apache.OK
-
--- a/bundled/cherrypy/cherrypy/test/modwsgi.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-"""Wrapper for mod_wsgi, for use as a CherryPy HTTP server.
-
-To autostart modwsgi, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl" or "apache2ctl"--
-create a symlink to them if needed.
-
-
-KNOWN BUGS
-==========
-
-##1. Apache processes Range headers automatically; CherryPy's truncated
-##    output is then truncated again by Apache. See test_core.testRanges.
-##    This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
-    See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-##4. Apache replaces status "reason phrases" automatically. For example,
-##    CherryPy may set "304 Not modified" but Apache will write out
-##    "304 Not Modified" (capital "M").
-##5. Apache does not allow custom error codes as per the spec.
-##6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-##    Request-URI too early.
-7. mod_wsgi will not read request bodies which use the "chunked"
-    transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
-    instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
-    mod_python's requestobject.c).
-8. When responding with 204 No Content, mod_wsgi adds a Content-Length
-    header for you.
-9. When an error is raised, mod_wsgi has no facility for printing a
-    traceback as the response content (it's sent to the Apache log instead).
-10. Startup and shutdown of Apache when running mod_wsgi seems slow.
-"""
-
-import os
-curdir = os.path.abspath(os.path.dirname(__file__))
-import re
-import sys
-import time
-
-import cherrypy
-from cherrypy.test import test, webtest
-
-
-def read_process(cmd, args=""):
-    pipein, pipeout = os.popen4("%s %s" % (cmd, args))
-    try:
-        firstline = pipeout.readline()
-        if (re.search(r"(not recognized|No such file|not found)", firstline,
-                      re.IGNORECASE)):
-            raise IOError('%s must be on your system path.' % cmd)
-        output = firstline + pipeout.read()
-    finally:
-        pipeout.close()
-    return output
-
-
-if sys.platform == 'win32':
-    APACHE_PATH = "httpd"
-else:
-    APACHE_PATH = "apache"
-
-CONF_PATH = "test_mw.conf"
-
-conf_modwsgi = r"""
-# Apache2 server conf file for testing CherryPy with modpython_gateway.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-
-AllowEncodedSlashes On
-LoadModule rewrite_module modules/mod_rewrite.so
-RewriteEngine on
-RewriteMap escaping int:escape
-
-LoadModule log_config_module modules/mod_log_config.so
-LogFormat "%%h %%l %%u %%t \"%%r\" %%>s %%b \"%%{Referer}i\" \"%%{User-agent}i\"" combined
-CustomLog "%(curdir)s/apache.access.log" combined
-ErrorLog "%(curdir)s/apache.error.log"
-LogLevel debug
-
-LoadModule wsgi_module modules/mod_wsgi.so
-LoadModule env_module modules/mod_env.so
-
-WSGIScriptAlias / "%(curdir)s/modwsgi.py"
-SetEnv testmod %(testmod)s
-"""
-
-
-class ModWSGISupervisor(test.Supervisor):
-    """Server Controller for ModWSGI and CherryPy."""
-    
-    using_apache = True
-    using_wsgi = True
-    template=conf_modwsgi
-    
-    def __str__(self):
-        return "ModWSGI Server on %s:%s" % (self.host, self.port)
-    
-    def start(self, modulename):
-        mpconf = CONF_PATH
-        if not os.path.isabs(mpconf):
-            mpconf = os.path.join(curdir, mpconf)
-        
-        f = open(mpconf, 'wb')
-        try:
-            output = (self.template %
-                      {'port': self.port, 'testmod': modulename,
-                       'curdir': curdir})
-            f.write(output)
-        finally:
-            f.close()
-        
-        result = read_process(APACHE_PATH, "-k start -f %s" % mpconf)
-        if result:
-            print(result)
-        
-        # Make a request so mod_wsgi starts up our app.
-        # If we don't, concurrent initial requests will 404.
-        cherrypy._cpserver.wait_for_occupied_port("127.0.0.1", self.port)
-        webtest.openURL('/ihopetheresnodefault', port=self.port)
-        time.sleep(1)
-    
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        read_process(APACHE_PATH, "-k stop")
-
-
-loaded = False
-def application(environ, start_response):
-    import cherrypy
-    global loaded
-    if not loaded:
-        loaded = True
-        modname = "cherrypy.test." + environ['testmod']
-        mod = __import__(modname, globals(), locals(), [''])
-        mod.setup_server()
-        
-        cherrypy.config.update({
-            "log.error_file": os.path.join(curdir, "test.error.log"),
-            "log.access_file": os.path.join(curdir, "test.access.log"),
-            "environment": "test_suite",
-            "engine.SIGHUP": None,
-            "engine.SIGTERM": None,
-            })
-    return cherrypy.tree(environ, start_response)
-
--- a/bundled/cherrypy/cherrypy/test/py25.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-"""Test module for Python 2.5-specific syntax, like the @-decorator syntax."""
-
-from cherrypy import expose, tools
-
-
-class ExposeExamples(object):
-    
-    @expose
-    def no_call(self):
-        return "Mr E. R. Bradshaw"
-    
-    @expose()
-    def call_empty(self):
-        return "Mrs. B.J. Smegma"
-    
-    @expose("call_alias")
-    def nesbitt(self):
-        return "Mr Nesbitt"
-    
-    @expose(["alias1", "alias2"])
-    def andrews(self):
-        return "Mr Ken Andrews"
-    
-    @expose(alias="alias3")
-    def watson(self):
-        return "Mr. and Mrs. Watson"
-
-
-class ToolExamples(object):
-    
-    @expose
-    @tools.response_headers(headers=[('Content-Type', 'application/data')])
-    def blah(self):
-        yield "blah"
-    # This is here to demonstrate that _cp_config = {...} overwrites
-    # the _cp_config attribute added by the Tool decorator. You have
-    # to write _cp_config[k] = v or _cp_config.update(...) instead.
-    blah._cp_config['response.stream'] = True
-
-
--- a/bundled/cherrypy/cherrypy/test/sessiondemo.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-#!/usr/bin/python
-"""A session demonstration app."""
-
-import calendar
-from datetime import datetime
-import sys
-import cherrypy
-from cherrypy.lib import sessions
-
-
-page = """
-<html>
-<head>
-<style type='text/css'>
-table { border-collapse: collapse; border: 1px solid #663333; }
-th { text-align: right; background-color: #663333; color: white; padding: 0.5em; }
-td { white-space: pre-wrap; font-family: monospace; padding: 0.5em; 
-     border: 1px solid #663333; }
-.warn { font-family: serif; color: #990000; }
-</style>
-<script type="text/javascript">
-<!--
-function twodigit(d) { return d < 10 ? "0" + d : d; }
-function formattime(t) {
-    var month = t.getUTCMonth() + 1;
-    var day = t.getUTCDate();
-    var year = t.getUTCFullYear();
-    var hours = t.getUTCHours();
-    var minutes = t.getUTCMinutes();
-    return (year + "/" + twodigit(month) + "/" + twodigit(day) + " " +
-            hours + ":" + twodigit(minutes) + " UTC");
-}
-
-function interval(s) {
-    // Return the given interval (in seconds) as an English phrase
-    var seconds = s %% 60;
-    s = Math.floor(s / 60);
-    var minutes = s %% 60;
-    s = Math.floor(s / 60);
-    var hours = s %% 24;
-    var v = twodigit(hours) + ":" + twodigit(minutes) + ":" + twodigit(seconds);
-    var days = Math.floor(s / 24);
-    if (days != 0) v = days + ' days, ' + v;
-    return v;
-}
-
-var fudge_seconds = 5;
-
-function init() {
-    // Set the content of the 'btime' cell.
-    var currentTime = new Date();
-    var bunixtime = Math.floor(currentTime.getTime() / 1000);
-    
-    var v = formattime(currentTime);
-    v += " (Unix time: " + bunixtime + ")";
-    
-    var diff = Math.abs(%(serverunixtime)s - bunixtime);
-    if (diff > fudge_seconds) v += "<p class='warn'>Browser and Server times disagree.</p>";
-    
-    document.getElementById('btime').innerHTML = v;
-    
-    // Warn if response cookie expires is not close to one hour in the future.
-    // Yes, we want this to happen when wit hit the 'Expire' link, too.
-    var expires = Date.parse("%(expires)s") / 1000;
-    var onehour = (60 * 60);
-    if (Math.abs(expires - (bunixtime + onehour)) > fudge_seconds) {
-        diff = Math.floor(expires - bunixtime);
-        if (expires > (bunixtime + onehour)) {
-            var msg = "Response cookie 'expires' date is " + interval(diff) + " in the future.";
-        } else {
-            var msg = "Response cookie 'expires' date is " + interval(0 - diff) + " in the past.";
-        }
-        document.getElementById('respcookiewarn').innerHTML = msg;
-    }
-}
-//-->
-</script>
-</head>
-
-<body onload='init()'>
-<h2>Session Demo</h2>
-<p>Reload this page. The session ID should not change from one reload to the next</p>
-<p><a href='../'>Index</a> | <a href='expire'>Expire</a> | <a href='regen'>Regenerate</a></p>
-<table>
-    <tr><th>Session ID:</th><td>%(sessionid)s<p class='warn'>%(changemsg)s</p></td></tr>
-    <tr><th>Request Cookie</th><td>%(reqcookie)s</td></tr>
-    <tr><th>Response Cookie</th><td>%(respcookie)s<p id='respcookiewarn' class='warn'></p></td></tr>
-    <tr><th>Session Data</th><td>%(sessiondata)s</td></tr>
-    <tr><th>Server Time</th><td id='stime'>%(servertime)s (Unix time: %(serverunixtime)s)</td></tr>
-    <tr><th>Browser Time</th><td id='btime'>&nbsp;</td></tr>
-    <tr><th>Cherrypy Version:</th><td>%(cpversion)s</td></tr>
-    <tr><th>Python Version:</th><td>%(pyversion)s</td></tr>
-</table>
-</body></html>
-"""
-
-class Root(object):
-    
-    def page(self):
-        changemsg = []
-        if cherrypy.session.id != cherrypy.session.originalid:
-            if cherrypy.session.originalid is None:
-                changemsg.append('Created new session because no session id was given.')
-            if cherrypy.session.missing:
-                changemsg.append('Created new session due to missing (expired or malicious) session.')
-            if cherrypy.session.regenerated:
-                changemsg.append('Application generated a new session.')
-        
-        try:
-            expires = cherrypy.response.cookie['session_id']['expires']
-        except KeyError:
-            expires = ''
-        
-        return page % {
-            'sessionid': cherrypy.session.id,
-            'changemsg': '<br>'.join(changemsg),
-            'respcookie': cherrypy.response.cookie.output(),
-            'reqcookie': cherrypy.request.cookie.output(),
-            'sessiondata': cherrypy.session.items(),
-            'servertime': datetime.utcnow().strftime("%Y/%m/%d %H:%M") + " UTC",
-            'serverunixtime': calendar.timegm(datetime.utcnow().timetuple()),
-            'cpversion': cherrypy.__version__,
-            'pyversion': sys.version,
-            'expires': expires,
-            }
-    
-    def index(self):
-        # Must modify data or the session will not be saved.
-        cherrypy.session['color'] = 'green'
-        return self.page()
-    index.exposed = True
-    
-    def expire(self):
-        sessions.expire()
-        return self.page()
-    expire.exposed = True
-    
-    def regen(self):
-        cherrypy.session.regenerate()
-        # Must modify data or the session will not be saved.
-        cherrypy.session['color'] = 'yellow'
-        return self.page()
-    regen.exposed = True
-
-if __name__ == '__main__':
-    cherrypy.config.update({
-        #'environment': 'production',
-        'log.screen': True,
-        'tools.sessions.on': True,
-        })
-    cherrypy.quickstart(Root())
-
Binary file bundled/cherrypy/cherrypy/test/static/dirback.jpg has changed
--- a/bundled/cherrypy/cherrypy/test/static/index.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-Hello, world
--- a/bundled/cherrypy/cherrypy/test/style.css	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-Dummy stylesheet
--- a/bundled/cherrypy/cherrypy/test/test.pem	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDBKo554mzIMY+AByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZ
-R9L4WtImEew05FY3Izerfm3MN3+MC0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Kn
-da+O6xldVSosu8Ev3z9VZ94iC/ZgKzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQAB
-AoGAWOCF0ZrWxn3XMucWq2LNwPKqlvVGwbIwX3cDmX22zmnM4Fy6arXbYh4XlyCj
-9+ofqRrxIFz5k/7tFriTmZ0xag5+Jdx+Kwg0/twiP7XCNKipFogwe1Hznw8OFAoT
-enKBdj2+/n2o0Bvo/tDB59m9L/538d46JGQUmJlzMyqYikECQQDyoq+8CtMNvE18
-8VgHcR/KtApxWAjj4HpaHYL637ATjThetUZkW92mgDgowyplthusxdNqhHWyv7E8
-tWNdYErZAkEAy85ShTR0M5aWmrE7o0r0SpWInAkNBH9aXQRRARFYsdBtNfRu6I0i
-0lvU9wiu3eF57FMEC86yViZ5UBnQfTu7vQJAVesj/Zt7pwaCDfdMa740OsxMUlyR
-MVhhGx4OLpYdPJ8qUecxGQKq13XZ7R1HGyNEY4bd2X80Smq08UFuATfC6QJAH8UB
-yBHtKz2GLIcELOg6PIYizW/7v3+6rlVF60yw7sb2vzpjL40QqIn4IKoR2DSVtOkb
-8FtAIX3N21aq0VrGYQJBAIPiaEc2AZ8Bq2GC4F3wOz/BxJ/izvnkiotR12QK4fh5
-yjZMhTjWCas5zwHR5PDjlD88AWGDMsZ1PicD4348xJQ=
------END RSA PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIDxTCCAy6gAwIBAgIJAI18BD7eQxlGMA0GCSqGSIb3DQEBBAUAMIGeMQswCQYD
-VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIERpZWdv
-MRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0MREwDwYDVQQLEwhkZXYtdGVzdDEW
-MBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4GCSqGSIb3DQEJARYRcmVtaUBjaGVy
-cnlweS5vcmcwHhcNMDYwOTA5MTkyMDIwWhcNMzQwMTI0MTkyMDIwWjCBnjELMAkG
-A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVNhbiBEaWVn
-bzEZMBcGA1UEChMQQ2hlcnJ5UHkgUHJvamVjdDERMA8GA1UECxMIZGV2LXRlc3Qx
-FjAUBgNVBAMTDUNoZXJyeVB5IFRlYW0xIDAeBgkqhkiG9w0BCQEWEXJlbWlAY2hl
-cnJ5cHkub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBKo554mzIMY+A
-ByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZR9L4WtImEew05FY3Izerfm3MN3+M
-C0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Knda+O6xldVSosu8Ev3z9VZ94iC/Zg
-KzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQABo4IBBzCCAQMwHQYDVR0OBBYEFDIQ
-2feb71tVZCWpU0qJ/Tw+wdtoMIHTBgNVHSMEgcswgciAFDIQ2feb71tVZCWpU0qJ
-/Tw+wdtooYGkpIGhMIGeMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5p
-YTESMBAGA1UEBxMJU2FuIERpZWdvMRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0
-MREwDwYDVQQLEwhkZXYtdGVzdDEWMBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4G
-CSqGSIb3DQEJARYRcmVtaUBjaGVycnlweS5vcmeCCQCNfAQ+3kMZRjAMBgNVHRME
-BTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBAL7AAQz7IePV48ZTAFHKr88ntPALsL5S
-8vHCZPNMevNkLTj3DYUw2BcnENxMjm1kou2F2BkvheBPNZKIhc6z4hAml3ed1xa2
-D7w6e6OTcstdK/+KrPDDHeOP1dhMWNs2JE1bNlfF1LiXzYKSXpe88eCKjCXsCT/T
-NluCaWQys3MS
------END CERTIFICATE-----
--- a/bundled/cherrypy/cherrypy/test/test.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,551 +0,0 @@
-"""The actual script that runs the entire CP test suite.
-
-There is a library of helper functions for the CherryPy test suite,
-called "helper.py" (in this folder); this module calls that as a library.
-"""
-
-# GREAT CARE has been taken to separate this module from helper.py,
-# because different consumers of each have mutually-exclusive import
-# requirements. So don't go moving functions from here into helper.py,
-# or vice-versa, unless you *really* know what you're doing.
-
-
-import getopt
-from httplib import HTTPSConnection
-import os
-localDir = os.path.dirname(__file__)
-serverpem = os.path.join(os.getcwd(), localDir, 'test.pem')
-import sys
-import warnings
-
-from cherrypy.lib import profiler
-
-
-class TestHarness(object):
-    """A test harness for the CherryPy framework and CherryPy applications."""
-    
-    def __init__(self, supervisor, tests, interactive=True):
-        """Constructor to populate the TestHarness instance.
-        
-        tests should be a list of module names (strings).
-        """
-        self.supervisor = supervisor
-        self.tests = tests
-        self.interactive = interactive
-    
-    def run(self, conf=None):
-        """Run the test harness (using the given [global] conf)."""
-        import cherrypy
-        v = sys.version.split()[0]
-        print("Python version used to run this test script: %s" % v)
-        print("CherryPy version: %s" % cherrypy.__version__)
-        if self.supervisor.scheme == "https":
-            ssl = " (ssl)"
-        else:
-            ssl = ""
-        print("HTTP server version: %s%s" % (self.supervisor.protocol, ssl))
-        print("PID: %s" % os.getpid())
-        print("")
-        
-        cherrypy.server.using_apache = self.supervisor.using_apache
-        cherrypy.server.using_wsgi = self.supervisor.using_wsgi
-        
-        if isinstance(conf, basestring):
-            parser = cherrypy.config._Parser()
-            conf = parser.dict_from_file(conf).get('global', {})
-        else:
-            conf = conf or {}
-        baseconf = conf.copy()
-        baseconf.update({'server.socket_host': self.supervisor.host,
-                         'server.socket_port': self.supervisor.port,
-                         'server.protocol_version': self.supervisor.protocol,
-                         'environment': "test_suite",
-                         })
-        if self.supervisor.scheme == "https":
-            baseconf['server.ssl_certificate'] = serverpem
-            baseconf['server.ssl_private_key'] = serverpem
-        
-        # helper must be imported lazily so the coverage tool
-        # can run against module-level statements within cherrypy.
-        # Also, we have to do "from cherrypy.test import helper",
-        # exactly like each test module does, because a relative import
-        # would stick a second instance of webtest in sys.modules,
-        # and we wouldn't be able to globally override the port anymore.
-        from cherrypy.test import helper, webtest
-        webtest.WebCase.interactive = self.interactive
-        if self.supervisor.scheme == "https":
-            webtest.WebCase.HTTP_CONN = HTTPSConnection
-        print("")
-        print("Running tests: %s" % self.supervisor)
-        
-        return helper.run_test_suite(self.tests, baseconf, self.supervisor)
-
-
-class Supervisor(object):
-    """Base class for modeling and controlling servers during testing."""
-    
-    def __init__(self, **kwargs):
-        for k, v in kwargs.iteritems():
-            setattr(self, k, v)
-
-
-class LocalSupervisor(Supervisor):
-    """Base class for modeling/controlling servers which run in the same process.
-    
-    When the server side runs in a different process, start/stop can dump all
-    state between each test module easily. When the server side runs in the
-    same process as the client, however, we have to do a bit more work to ensure
-    config and mounted apps are reset between tests.
-    """
-    
-    using_apache = False
-    using_wsgi = False
-    
-    def __init__(self, **kwargs):
-        for k, v in kwargs.iteritems():
-            setattr(self, k, v)
-        
-        import cherrypy
-        cherrypy.server.httpserver = self.httpserver_class
-        
-        engine = cherrypy.engine
-        if hasattr(engine, "signal_handler"):
-            engine.signal_handler.subscribe()
-        if hasattr(engine, "console_control_handler"):
-            engine.console_control_handler.subscribe()
-        #engine.subscribe('log', lambda msg, level: sys.stderr.write(msg + os.linesep))
-    
-    def start(self, modulename=None):
-        """Load and start the HTTP server."""
-        import cherrypy
-        
-        if modulename:
-            # Replace the Tree wholesale.
-            cherrypy.tree = cherrypy._cptree.Tree()
-            
-            # Unhook httpserver so cherrypy.server.start() creates a new
-            # one (with config from setup_server, if declared).
-            cherrypy.server.httpserver = None
-            cherrypy.tree = cherrypy._cptree.Tree()
-            
-            if '.' in modulename:
-                package, test_name = modulename.rsplit('.', 1)
-                p = __import__(package, globals(), locals(), fromlist=[test_name])
-                m = getattr(p, test_name)
-            else:
-                m = __import__(modulename, globals(), locals())
-            setup = getattr(m, "setup_server", None)
-            if setup:
-                setup()
-            self.teardown = getattr(m, "teardown_server", None)
-        
-        cherrypy.engine.start()
-        
-        self.sync_apps()
-    
-    def sync_apps(self):
-        """Tell the server about any apps which the setup functions mounted."""
-        pass
-    
-    def stop(self):
-        if self.teardown:
-            self.teardown()
-        import cherrypy
-        cherrypy.engine.exit()
-
-
-class NativeServerSupervisor(LocalSupervisor):
-    """Server supervisor for the builtin HTTP server."""
-    
-    httpserver_class = "cherrypy._cpnative_server.CPHTTPServer"
-    using_apache = False
-    using_wsgi = False
-    
-    def __str__(self):
-        return "Builtin HTTP Server on %s:%s" % (self.host, self.port)
-
-
-class LocalWSGISupervisor(LocalSupervisor):
-    """Server supervisor for the builtin WSGI server."""
-    
-    httpserver_class = "cherrypy._cpwsgi_server.CPWSGIServer"
-    using_apache = False
-    using_wsgi = True
-    
-    def __str__(self):
-        return "Builtin WSGI Server on %s:%s" % (self.host, self.port)
-    
-    def sync_apps(self):
-        """Hook a new WSGI app into the origin server."""
-        import cherrypy
-        cherrypy.server.httpserver.wsgi_app = self.get_app()
-    
-    def get_app(self):
-        """Obtain a new (decorated) WSGI app to hook into the origin server."""
-        import cherrypy
-        app = cherrypy.tree
-        if self.profile:
-            app = profiler.make_app(app, aggregate=False)
-        if self.conquer:
-            try:
-                import wsgiconq
-            except ImportError:
-                warnings.warn("Error importing wsgiconq. pyconquer will not run.")
-            else:
-                app = wsgiconq.WSGILogger(app, c_calls=True)
-        if self.validate:
-            try:
-                from wsgiref import validate
-            except ImportError:
-                warnings.warn("Error importing wsgiref. The validator will not run.")
-            else:
-                app = validate.validator(app)
-        
-        return app
-
-
-def get_cpmodpy_supervisor(**options):
-    from cherrypy.test import modpy
-    sup = modpy.ModPythonSupervisor(**options)
-    sup.template = modpy.conf_cpmodpy
-    return sup
-
-def get_modpygw_supervisor(**options):
-    from cherrypy.test import modpy
-    sup = modpy.ModPythonSupervisor(**options)
-    sup.template = modpy.conf_modpython_gateway
-    sup.using_wsgi = True
-    return sup
-
-def get_modwsgi_supervisor(**options):
-    from cherrypy.test import modwsgi
-    return modwsgi.ModWSGISupervisor(**options)
-
-def get_modfcgid_supervisor(**options):
-    from cherrypy.test import modfcgid
-    return modfcgid.ModFCGISupervisor(**options)
-
-def get_wsgi_u_supervisor(**options):
-    import cherrypy
-    cherrypy.server.wsgi_version = ('u', 0)
-    return LocalWSGISupervisor(**options)
-
-
-class CommandLineParser(object):
-    available_servers = {'wsgi': LocalWSGISupervisor,
-                         'wsgi_u': get_wsgi_u_supervisor,
-                         'native': NativeServerSupervisor,
-                         'cpmodpy': get_cpmodpy_supervisor,
-                         'modpygw': get_modpygw_supervisor,
-                         'modwsgi': get_modwsgi_supervisor,
-                         'modfcgid': get_modfcgid_supervisor,
-                         }
-    default_server = "wsgi"
-    
-    supervisor_factory = None
-    supervisor_options = {
-        'scheme': 'http',
-        'protocol': "HTTP/1.1",
-        'port': 8080,
-        'host': '127.0.0.1',
-        'profile': False,
-        'validate': False,
-        'conquer': False,
-        }
-    
-    cover = False
-    basedir = None
-    interactive = True
-    
-    shortopts = []
-    longopts = ['cover', 'profile', 'validate', 'conquer', 'dumb', '1.0',
-                'ssl', 'help', 'basedir=', 'port=', 'server=', 'host=']
-    
-    def __init__(self, available_tests, args=sys.argv[1:]):
-        """Constructor to populate the TestHarness instance.
-        
-        available_tests should be a list of module names (strings).
-        
-        args defaults to sys.argv[1:], but you can provide a different
-            set of args if you like.
-        """
-        self.available_tests = available_tests
-        self.supervisor_options = self.supervisor_options.copy()
-        
-        longopts = self.longopts[:]
-        longopts.extend(self.available_tests)
-        try:
-            opts, args = getopt.getopt(args, self.shortopts, longopts)
-        except getopt.GetoptError:
-            # print help information and exit
-            self.help()
-            sys.exit(2)
-        
-        self.tests = []
-        
-        for o, a in opts:
-            if o == '--help':
-                self.help()
-                sys.exit()
-            elif o == "--cover":
-                self.cover = True
-            elif o == "--profile":
-                self.supervisor_options['profile'] = True
-            elif o == "--validate":
-                self.supervisor_options['validate'] = True
-            elif o == "--conquer":
-                self.supervisor_options['conquer'] = True
-            elif o == "--dumb":
-                self.interactive = False
-            elif o == "--1.0":
-                self.supervisor_options['protocol'] = "HTTP/1.0"
-            elif o == "--ssl":
-                self.supervisor_options['scheme'] = "https"
-            elif o == "--basedir":
-                self.basedir = a
-            elif o == "--port":
-                self.supervisor_options['port'] = int(a)
-            elif o == "--host":
-                self.supervisor_options['host'] = a
-            elif o == "--server":
-                if a not in self.available_servers:
-                    print('Error: The --server argument must be one of %s.' %
-                          '|'.join(self.available_servers.keys()))
-                    sys.exit(2)
-                self.supervisor_factory = self.available_servers[a]
-            else:
-                o = o[2:]
-                if o in self.available_tests and o not in self.tests:
-                    self.tests.append(o)
-        
-        import cherrypy
-        if self.cover and self.supervisor_options['profile']:
-            # Print error message and exit
-            print('Error: you cannot run the profiler and the '
-                   'coverage tool at the same time.')
-            sys.exit(2)
-        
-        if not self.supervisor_factory:
-            self.supervisor_factory = self.available_servers[self.default_server]
-        
-        if not self.tests:
-            self.tests = self.available_tests[:]
-    
-    def help(self):
-        """Print help for test.py command-line options."""
-        
-        import cherrypy
-        print("""CherryPy Test Program
-    Usage:
-        test.py --help --server=* --host=%s --port=%s --1.0 --ssl --cover
-            --basedir=path --profile --validate --conquer --dumb --tests**
-    """ % (self.__class__.supervisor_options['host'],
-           self.__class__.supervisor_options['port']))
-        print('    * servers:')
-        for name in self.available_servers:
-            if name == self.default_server:
-                print('        --server=%s (default)' % name)
-            else:
-                print('        --server=%s' % name)
-        
-        print("""
-    --host=<name or IP addr>: use a host other than the default (%s).
-        Not yet available with mod_python servers.
-    --port=<int>: use a port other than the default (%s).
-    --1.0: use HTTP/1.0 servers instead of default HTTP/1.1.
-    
-    --cover: turn on code-coverage tool.
-    --basedir=path: display coverage stats for some path other than cherrypy.
-    
-    --profile: turn on WSGI profiling tool.
-    --validate: use wsgiref.validate (builtin in Python 2.5).
-    --conquer: use wsgiconq (which uses pyconquer) to trace calls.
-    --dumb: turn off the interactive output features.
-    """ % (self.__class__.supervisor_options['host'],
-           self.__class__.supervisor_options['port']))
-        
-        print('    ** tests:')
-        for name in self.available_tests:
-            print('        --' + name)
-    
-    def start_coverage(self):
-        """Start the coverage tool.
-        
-        To use this feature, you need to download 'coverage.py',
-        either Gareth Rees' original implementation:
-        http://www.garethrees.org/2001/12/04/python-coverage/
-        
-        or Ned Batchelder's enhanced version:
-        http://www.nedbatchelder.com/code/modules/coverage.html
-        
-        If neither module is found in PYTHONPATH,
-        coverage is silently(!) disabled.
-        """
-        try:
-            from coverage import the_coverage as coverage
-            c = os.path.join(os.path.dirname(__file__), "../lib/coverage.cache")
-            coverage.cache_default = c
-            if c and os.path.exists(c):
-                os.remove(c)
-            coverage.start()
-            import cherrypy
-            from cherrypy.lib import covercp
-            cherrypy.engine.subscribe('start', covercp.start)
-        except ImportError:
-            coverage = None
-        self.coverage = coverage
-    
-    def stop_coverage(self):
-        """Stop the coverage tool, save results, and report."""
-        import cherrypy
-        from cherrypy.lib import covercp
-        cherrypy.engine.unsubscribe('start', covercp.start)
-        if self.coverage:
-            self.coverage.save()
-            self.report_coverage()
-            print("run cherrypy/lib/covercp.py as a script to serve "
-                   "coverage results on port 8080")
-    
-    def report_coverage(self):
-        """Print a summary from the code coverage tool."""
-        
-        import cherrypy
-        basedir = self.basedir
-        if basedir is None:
-            # Assume we want to cover everything in "../../cherrypy/"
-            basedir = os.path.normpath(os.path.join(os.getcwd(), localDir, '../'))
-        else:
-            if not os.path.isabs(basedir):
-                basedir = os.path.normpath(os.path.join(os.getcwd(), basedir))
-        basedir = basedir.lower()
-        
-        self.coverage.get_ready()
-        morfs = [x for x in self.coverage.cexecuted
-                 if x.lower().startswith(basedir)]
-        
-        total_statements = 0
-        total_executed = 0
-        
-        print("")
-        sys.stdout.write("CODE COVERAGE (this might take a while)")
-        for morf in morfs:
-            sys.stdout.write(".")
-            sys.stdout.flush()
-##            name = os.path.split(morf)[1]
-            if morf.find('test') != -1:
-                continue
-            try:
-                _, statements, _, missing, readable  = self.coverage.analysis2(morf)
-                n = len(statements)
-                m = n - len(missing)
-                total_statements = total_statements + n
-                total_executed = total_executed + m
-            except KeyboardInterrupt:
-                raise
-            except:
-                # No, really! We truly want to ignore any other errors.
-                pass
-        
-        pc = 100.0
-        if total_statements > 0:
-            pc = 100.0 * total_executed / total_statements
-        
-        print("\nTotal: %s Covered: %s Percent: %2d%%"
-               % (total_statements, total_executed, pc))
-    
-    def run(self, conf=None):
-        """Run the test harness (using the given [global] conf)."""
-        conf = conf or {}
-        
-        # Start the coverage tool before importing cherrypy,
-        # so module-level global statements are covered.
-        if self.cover:
-            self.start_coverage()
-        
-        supervisor = self.supervisor_factory(**self.supervisor_options)
-        
-        if supervisor.using_apache and 'test_conn' in self.tests:
-            self.tests.remove('test_conn')
-        
-        h = TestHarness(supervisor, self.tests, self.interactive)
-        success = h.run(conf)
-        
-        if self.supervisor_options['profile']:
-            print("")
-            print("run /cherrypy/lib/profiler.py as a script to serve "
-                   "profiling results on port 8080")
-        
-        if self.cover:
-            self.stop_coverage()
-        
-        return success
-
-
-def prefer_parent_path():
-    # Place this __file__'s grandparent (../../) at the start of sys.path,
-    # so that all cherrypy/* imports are from this __file__'s package.
-    curpath = os.path.normpath(os.path.join(os.getcwd(), localDir))
-    grandparent = os.path.normpath(os.path.join(curpath, '../../'))
-    if grandparent not in sys.path:
-        sys.path.insert(0, grandparent)
-
-def run():
-    
-    prefer_parent_path()
-    
-    testList = [
-        'test_auth_basic',
-        'test_auth_digest',
-        'test_bus',
-        'test_proxy',
-        'test_caching',
-        'test_config',
-        'test_conn',
-        'test_core',
-        'test_tools',
-        'test_encoding',
-        'test_etags',
-        'test_http',
-        'test_httpauth',
-        'test_httplib',
-        'test_json',
-        'test_logging',
-        'test_objectmapping',
-        'test_dynamicobjectmapping',
-        'test_misc_tools',
-        'test_request_obj',
-        'test_static',
-        'test_tutorials',
-        'test_virtualhost',
-        'test_mime',
-        'test_session',
-        'test_sessionauthenticate',
-        'test_states',
-        'test_config_server',
-        'test_xmlrpc',
-        'test_wsgiapps',
-        'test_wsgi_ns',
-        'test_wsgi_vhost',
-        
-        # Run refleak test as late as possible to
-        # catch refleaks from all exercised tests.
-        'test_refleaks',
-    ]
-    
-    try:
-        import routes
-        testList.append('test_routes')
-    except ImportError:
-        pass
-    
-    clp = CommandLineParser(testList)
-    success = clp.run()
-    import cherrypy
-    if clp.interactive:
-        print("")
-        raw_input('hit enter')
-    sys.exit(success)
-
-
-if __name__ == '__main__':
-    run()
--- a/bundled/cherrypy/cherrypy/test/test_auth_basic.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-try:
-    from hashlib import md5
-except ImportError:
-    # Python 2.4 and earlier
-    from md5 import new as md5
-
-import cherrypy
-from cherrypy.lib import auth_basic
-
-def setup_server():
-    class Root:
-        def index(self):
-            return "This is public."
-        index.exposed = True
-
-    class BasicProtected:
-        def index(self):
-            return "Hello %s, you've been authorized." % cherrypy.request.login
-        index.exposed = True
-
-    class BasicProtected2:
-        def index(self):
-            return "Hello %s, you've been authorized." % cherrypy.request.login
-        index.exposed = True
-
-    userpassdict = {'xuser' : 'xpassword'}
-    userhashdict = {'xuser' : md5('xpassword').hexdigest()}
-
-    def checkpasshash(realm, user, password):
-        p = userhashdict.get(user)
-        return p and p == md5(password).hexdigest() or False
-
-    conf = {'/basic': {'tools.auth_basic.on': True,
-                       'tools.auth_basic.realm': 'wonderland',
-                       'tools.auth_basic.checkpassword': auth_basic.checkpassword_dict(userpassdict)},
-            '/basic2': {'tools.auth_basic.on': True,
-                        'tools.auth_basic.realm': 'wonderland',
-                        'tools.auth_basic.checkpassword': checkpasshash},
-           }
-
-    root = Root()
-    root.basic = BasicProtected()
-    root.basic2 = BasicProtected2()
-    cherrypy.tree.mount(root, config=conf)
-
-from cherrypy.test import helper
-
-class BasicAuthTest(helper.CPWebCase):
-
-    def testPublic(self):
-        self.getPage("/")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('This is public.')
-
-    def testBasic(self):
-        self.getPage("/basic/")
-        self.assertStatus(401)
-        self.assertHeader('WWW-Authenticate', 'Basic realm="wonderland"')
-
-        self.getPage('/basic/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3JX')])
-        self.assertStatus(401)
-
-        self.getPage('/basic/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3Jk')])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello xuser, you've been authorized.")
-
-    def testBasic2(self):
-        self.getPage("/basic2/")
-        self.assertStatus(401)
-        self.assertHeader('WWW-Authenticate', 'Basic realm="wonderland"')
-
-        self.getPage('/basic2/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3JX')])
-        self.assertStatus(401)
-
-        self.getPage('/basic2/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3Jk')])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello xuser, you've been authorized.")
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_auth_digest.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-
-import cherrypy
-from cherrypy.lib import auth_digest
-
-def setup_server():
-    class Root:
-        def index(self):
-            return "This is public."
-        index.exposed = True
-
-    class DigestProtected:
-        def index(self):
-            return "Hello %s, you've been authorized." % cherrypy.request.login
-        index.exposed = True
-
-    def fetch_users():
-        return {'test': 'test'}
-
-
-    get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain(fetch_users())
-    conf = {'/digest': {'tools.auth_digest.on': True,
-                        'tools.auth_digest.realm': 'localhost',
-                        'tools.auth_digest.get_ha1': get_ha1,
-                        'tools.auth_digest.key': 'a565c27146791cfb',
-                        'tools.auth_digest.debug': 'True'}}
-
-    root = Root()
-    root.digest = DigestProtected()
-    cherrypy.tree.mount(root, config=conf)
-
-from cherrypy.test import helper
-
-class DigestAuthTest(helper.CPWebCase):
-
-    def testPublic(self):
-        self.getPage("/")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('This is public.')
-
-    def testDigest(self):
-        self.getPage("/digest/")
-        self.assertStatus(401)
-
-        value = None
-        for k, v in self.headers:
-            if k.lower() == "www-authenticate":
-                if v.startswith("Digest"):
-                    value = v
-                    break
-
-        if value is None:
-            self._handlewebError("Digest authentification scheme was not found")
-
-        value = value[7:]
-        items = value.split(', ')
-        tokens = {}
-        for item in items:
-            key, value = item.split('=')
-            tokens[key.lower()] = value
-
-        missing_msg = "%s is missing"
-        bad_value_msg = "'%s' was expecting '%s' but found '%s'"
-        nonce = None
-        if 'realm' not in tokens:
-            self._handlewebError(missing_msg % 'realm')
-        elif tokens['realm'] != '"localhost"':
-            self._handlewebError(bad_value_msg % ('realm', '"localhost"', tokens['realm']))
-        if 'nonce' not in tokens:
-            self._handlewebError(missing_msg % 'nonce')
-        else:
-            nonce = tokens['nonce'].strip('"')
-        if 'algorithm' not in tokens:
-            self._handlewebError(missing_msg % 'algorithm')
-        elif tokens['algorithm'] != '"MD5"':
-            self._handlewebError(bad_value_msg % ('algorithm', '"MD5"', tokens['algorithm']))
-        if 'qop' not in tokens:
-            self._handlewebError(missing_msg % 'qop')
-        elif tokens['qop'] != '"auth"':
-            self._handlewebError(bad_value_msg % ('qop', '"auth"', tokens['qop']))
-
-        get_ha1 = auth_digest.get_ha1_dict_plain({'test' : 'test'})
-
-        # Test user agent response with a wrong value for 'realm'
-        base_auth = 'Digest username="test", realm="wrong realm", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
-        auth_header = base_auth % (nonce, '11111111111111111111111111111111', '00000001')
-        auth = auth_digest.HttpDigestAuthorization(auth_header, 'GET')
-        # calculate the response digest
-        ha1 = get_ha1(auth.realm, 'test')
-        response = auth.request_digest(ha1)
-        # send response with correct response digest, but wrong realm
-        auth_header = base_auth % (nonce, response, '00000001')
-        self.getPage('/digest/', [('Authorization', auth_header)])
-        self.assertStatus(401)
-
-        # Test that must pass
-        base_auth = 'Digest username="test", realm="localhost", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
-        auth_header = base_auth % (nonce, '11111111111111111111111111111111', '00000001')
-        auth = auth_digest.HttpDigestAuthorization(auth_header, 'GET')
-        # calculate the response digest
-        ha1 = get_ha1('localhost', 'test')
-        response = auth.request_digest(ha1)
-        # send response with correct response digest
-        auth_header = base_auth % (nonce, response, '00000001')
-        self.getPage('/digest/', [('Authorization', auth_header)])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello test, you've been authorized.")
-
-if __name__ == "__main__":
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_bus.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,265 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-try:
-    set
-except NameError:
-    from sets import Set as set
-import threading
-import time
-import unittest
-
-import cherrypy
-from cherrypy.process import wspbus
-
-
-msg = "Listener %d on channel %s: %s."
-
-
-class PublishSubscribeTests(unittest.TestCase):
-
-    def get_listener(self, channel, index):
-        def listener(arg=None):
-            self.responses.append(msg % (index, channel, arg))
-        return listener
-    
-    def test_builtin_channels(self):
-        b = wspbus.Bus()
-        
-        self.responses, expected = [], []
-        
-        for channel in b.listeners:
-            for index, priority in enumerate([100, 50, 0, 51]):
-                b.subscribe(channel, self.get_listener(channel, index), priority)
-        
-        for channel in b.listeners:
-            b.publish(channel)
-            expected.extend([msg % (i, channel, None) for i in (2, 1, 3, 0)])
-            b.publish(channel, arg=79347)
-            expected.extend([msg % (i, channel, 79347) for i in (2, 1, 3, 0)])
-        
-        self.assertEqual(self.responses, expected)
-    
-    def test_custom_channels(self):
-        b = wspbus.Bus()
-        
-        self.responses, expected = [], []
-        
-        custom_listeners = ('hugh', 'louis', 'dewey')
-        for channel in custom_listeners:
-            for index, priority in enumerate([None, 10, 60, 40]):
-                b.subscribe(channel, self.get_listener(channel, index), priority)
-        
-        for channel in custom_listeners:
-            b.publish(channel, 'ah so')
-            expected.extend([msg % (i, channel, 'ah so') for i in (1, 3, 0, 2)])
-            b.publish(channel)
-            expected.extend([msg % (i, channel, None) for i in (1, 3, 0, 2)])
-        
-        self.assertEqual(self.responses, expected)
-    
-    def test_listener_errors(self):
-        b = wspbus.Bus()
-        
-        self.responses, expected = [], []
-        channels = [c for c in b.listeners if c != 'log']
-        
-        for channel in channels:
-            b.subscribe(channel, self.get_listener(channel, 1))
-            # This will break since the lambda takes no args.
-            b.subscribe(channel, lambda: None, priority=20)
-        
-        for channel in channels:
-            self.assertRaises(wspbus.ChannelFailures, b.publish, channel, 123)
-            expected.append(msg % (1, channel, 123))
-        
-        self.assertEqual(self.responses, expected)
-
-
-class BusMethodTests(unittest.TestCase):
-    
-    def log(self, bus):
-        self._log_entries = []
-        def logit(msg, level):
-            self._log_entries.append(msg)
-        bus.subscribe('log', logit)
-    
-    def assertLog(self, entries):
-        self.assertEqual(self._log_entries, entries)
-    
-    def get_listener(self, channel, index):
-        def listener(arg=None):
-            self.responses.append(msg % (index, channel, arg))
-        return listener
-    
-    def test_start(self):
-        b = wspbus.Bus()
-        self.log(b)
-        
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('start', self.get_listener('start', index))
-        
-        b.start()
-        try:
-            # The start method MUST call all 'start' listeners.
-            self.assertEqual(set(self.responses),
-                             set([msg % (i, 'start', None) for i in range(num)]))
-            # The start method MUST move the state to STARTED
-            # (or EXITING, if errors occur)
-            self.assertEqual(b.state, b.states.STARTED)
-            # The start method MUST log its states.
-            self.assertLog(['Bus STARTING', 'Bus STARTED'])
-        finally:
-            # Exit so the atexit handler doesn't complain.
-            b.exit()
-    
-    def test_stop(self):
-        b = wspbus.Bus()
-        self.log(b)
-        
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('stop', self.get_listener('stop', index))
-        
-        b.stop()
-        
-        # The stop method MUST call all 'stop' listeners.
-        self.assertEqual(set(self.responses),
-                         set([msg % (i, 'stop', None) for i in range(num)]))
-        # The stop method MUST move the state to STOPPED
-        self.assertEqual(b.state, b.states.STOPPED)
-        # The stop method MUST log its states.
-        self.assertLog(['Bus STOPPING', 'Bus STOPPED'])
-    
-    def test_graceful(self):
-        b = wspbus.Bus()
-        self.log(b)
-        
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('graceful', self.get_listener('graceful', index))
-        
-        b.graceful()
-        
-        # The graceful method MUST call all 'graceful' listeners.
-        self.assertEqual(set(self.responses),
-                         set([msg % (i, 'graceful', None) for i in range(num)]))
-        # The graceful method MUST log its states.
-        self.assertLog(['Bus graceful'])
-    
-    def test_exit(self):
-        b = wspbus.Bus()
-        self.log(b)
-        
-        self.responses = []
-        num = 3
-        for index in range(num):
-            b.subscribe('stop', self.get_listener('stop', index))
-            b.subscribe('exit', self.get_listener('exit', index))
-        
-        b.exit()
-        
-        # The exit method MUST call all 'stop' listeners,
-        # and then all 'exit' listeners.
-        self.assertEqual(set(self.responses),
-                         set([msg % (i, 'stop', None) for i in range(num)] +
-                             [msg % (i, 'exit', None) for i in range(num)]))
-        # The exit method MUST move the state to EXITING
-        self.assertEqual(b.state, b.states.EXITING)
-        # The exit method MUST log its states.
-        self.assertLog(['Bus STOPPING', 'Bus STOPPED', 'Bus EXITING', 'Bus EXITED'])
-    
-    def test_wait(self):
-        b = wspbus.Bus()
-        
-        def f(method):
-            time.sleep(0.2)
-            getattr(b, method)()
-        
-        for method, states in [('start', [b.states.STARTED]),
-                               ('stop', [b.states.STOPPED]),
-                               ('start', [b.states.STARTING, b.states.STARTED]),
-                               ('exit', [b.states.EXITING]),
-                               ]:
-            threading.Thread(target=f, args=(method,)).start()
-            b.wait(states)
-            
-            # The wait method MUST wait for the given state(s).
-            if b.state not in states:
-                self.fail("State %r not in %r" % (b.state, states))
-    
-    def test_block(self):
-        b = wspbus.Bus()
-        self.log(b)
-        
-        def f():
-            time.sleep(0.2)
-            b.exit()
-        def g():
-            time.sleep(0.4)
-        threading.Thread(target=f).start()
-        threading.Thread(target=g).start()
-        self.assertEqual(len(threading.enumerate()), 3)
-        
-        b.block()
-        
-        # The block method MUST wait for the EXITING state.
-        self.assertEqual(b.state, b.states.EXITING)
-        # The block method MUST wait for ALL non-main threads to finish.
-        self.assertEqual(len(threading.enumerate()), 1)
-        self.assertLog(['Bus STOPPING', 'Bus STOPPED',
-                        'Bus EXITING', 'Bus EXITED',
-                        'Waiting for child threads to terminate...'])
-    
-    def test_start_with_callback(self):
-        b = wspbus.Bus()
-        self.log(b)
-        try:
-            events = []
-            def f(*args, **kwargs):
-                events.append(("f", args, kwargs))
-            def g():
-                events.append("g")
-            b.subscribe("start", g)
-            b.start_with_callback(f, (1, 3, 5), {"foo": "bar"})
-            # Give wait() time to run f()
-            time.sleep(0.2)
-            
-            # The callback method MUST wait for the STARTED state.
-            self.assertEqual(b.state, b.states.STARTED)
-            # The callback method MUST run after all start methods.
-            self.assertEqual(events, ["g", ("f", (1, 3, 5), {"foo": "bar"})])
-        finally:
-            b.exit()
-    
-    def test_log(self):
-        b = wspbus.Bus()
-        self.log(b)
-        self.assertLog([])
-        
-        # Try a normal message.
-        expected = []
-        for msg in ["O mah darlin'"] * 3 + ["Clementiiiiiiiine"]:
-            b.log(msg)
-            expected.append(msg)
-            self.assertLog(expected)
-        
-        # Try an error message
-        try:
-            foo
-        except NameError:
-            b.log("You are lost and gone forever", traceback=True)
-            lastmsg = self._log_entries[-1]
-            if "Traceback" not in lastmsg or "NameError" not in lastmsg:
-                self.fail("Last log message %r did not contain "
-                          "the expected traceback." % lastmsg)
-        else:
-            self.fail("NameError was not raised as expected.")
-
-
-if __name__ == "__main__":
-    unittest.main()
--- a/bundled/cherrypy/cherrypy/test/test_caching.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,335 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import datetime
-import gzip
-from itertools import count
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import sys
-import threading
-import time
-import urllib
-
-import cherrypy
-from cherrypy.lib import httputil
-
-gif_bytes = ('GIF89a\x01\x00\x01\x00\x82\x00\x01\x99"\x1e\x00\x00\x00\x00\x00'
-             '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
-             '\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x02\x03\x02\x08\t\x00;')
-
-
-def setup_server():
-    
-    class Root:
-        
-        _cp_config = {'tools.caching.on': True}
-        
-        def __init__(self):
-            self.counter = 0
-            self.control_counter = 0
-            self.longlock = threading.Lock()
-        
-        def index(self):
-            self.counter += 1
-            msg = "visit #%s" % self.counter
-            return msg
-        index.exposed = True
-        
-        def control(self):
-            self.control_counter += 1
-            return "visit #%s" % self.control_counter
-        control.exposed = True
-        
-        def a_gif(self):
-            cherrypy.response.headers['Last-Modified'] = httputil.HTTPDate()
-            return gif_bytes
-        a_gif.exposed = True
-        
-        def long_process(self, seconds='1'):
-            try:
-                self.longlock.acquire()
-                time.sleep(float(seconds))
-            finally:
-                self.longlock.release()
-            return 'success!'
-        long_process.exposed = True
-        
-        def clear_cache(self, path):
-            cherrypy._cache.store[cherrypy.request.base + path].clear()
-        clear_cache.exposed = True
-    
-    class VaryHeaderCachingServer(object):
-        
-        _cp_config = {'tools.caching.on': True,
-            'tools.response_headers.on': True,
-            'tools.response_headers.headers': [('Vary', 'Our-Varying-Header')],
-            }
-        
-        def __init__(self):
-            self.counter = count(1)
-        
-        def index(self):
-            return "visit #%s" % self.counter.next()
-        index.exposed = True
-    
-    class UnCached(object):
-        _cp_config = {'tools.expires.on': True,
-                      'tools.expires.secs': 60,
-                      'tools.staticdir.on': True,
-                      'tools.staticdir.dir': 'static',
-                      'tools.staticdir.root': curdir,
-                      }
-
-        def force(self):
-            cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
-            self._cp_config['tools.expires.force'] = True
-            self._cp_config['tools.expires.secs'] = 0
-            return "being forceful"
-        force.exposed = True
-        force._cp_config = {'tools.expires.secs': 0}
-
-        def dynamic(self):
-            cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
-            cherrypy.response.headers['Cache-Control'] = 'private'
-            return "D-d-d-dynamic!"
-        dynamic.exposed = True
-
-        def cacheable(self):
-            cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
-            return "Hi, I'm cacheable."
-        cacheable.exposed = True
-
-        def specific(self):
-            cherrypy.response.headers['Etag'] = 'need_this_to_make_me_cacheable'
-            return "I am being specific"
-        specific.exposed = True
-        specific._cp_config = {'tools.expires.secs': 86400}
-
-        class Foo(object):pass
-        
-        def wrongtype(self):
-            cherrypy.response.headers['Etag'] = 'need_this_to_make_me_cacheable'
-            return "Woops"
-        wrongtype.exposed = True
-        wrongtype._cp_config = {'tools.expires.secs': Foo()}
-    
-    cherrypy.tree.mount(Root())
-    cherrypy.tree.mount(UnCached(), "/expires")
-    cherrypy.tree.mount(VaryHeaderCachingServer(), "/varying_headers")
-    cherrypy.config.update({'tools.gzip.on': True})
-
-
-from cherrypy.test import helper
-
-class CacheTest(helper.CPWebCase):
-
-    def testCaching(self):
-        elapsed = 0.0
-        for trial in range(10):
-            self.getPage("/")
-            # The response should be the same every time,
-            # except for the Age response header.
-            self.assertBody('visit #1')
-            if trial != 0:
-                age = int(self.assertHeader("Age"))
-                self.assert_(age >= elapsed)
-                elapsed = age
-        
-        # POST, PUT, DELETE should not be cached.
-        self.getPage("/", method="POST")
-        self.assertBody('visit #2')
-        # Because gzip is turned on, the Vary header should always Vary for content-encoding
-        self.assertHeader('Vary', 'Accept-Encoding')
-        # The previous request should have invalidated the cache,
-        # so this request will recalc the response.
-        self.getPage("/", method="GET")
-        self.assertBody('visit #3')
-        # ...but this request should get the cached copy.
-        self.getPage("/", method="GET")
-        self.assertBody('visit #3')
-        self.getPage("/", method="DELETE")
-        self.assertBody('visit #4')
-        
-        # The previous request should have invalidated the cache,
-        # so this request will recalc the response.
-        self.getPage("/", method="GET", headers=[('Accept-Encoding', 'gzip')])
-        self.assertHeader('Content-Encoding', 'gzip')
-        self.assertHeader('Vary')
-        self.assertEqual(cherrypy.lib.encoding.decompress(self.body), "visit #5")
-        
-        # Now check that a second request gets the gzip header and gzipped body
-        # This also tests a bug in 3.0 to 3.0.2 whereby the cached, gzipped
-        # response body was being gzipped a second time.
-        self.getPage("/", method="GET", headers=[('Accept-Encoding', 'gzip')])
-        self.assertHeader('Content-Encoding', 'gzip')
-        self.assertEqual(cherrypy.lib.encoding.decompress(self.body), "visit #5")
-        
-        # Now check that a third request that doesn't accept gzip
-        # skips the cache (because the 'Vary' header denies it).
-        self.getPage("/", method="GET")
-        self.assertNoHeader('Content-Encoding')
-        self.assertBody('visit #6')
-    
-    def testVaryHeader(self):
-        self.getPage("/varying_headers/")
-        self.assertStatus("200 OK")
-        self.assertHeaderItemValue('Vary', 'Our-Varying-Header')
-        self.assertBody('visit #1')
-        
-        # Now check that different 'Vary'-fields don't evict each other.
-        # This test creates 2 requests with different 'Our-Varying-Header'
-        # and then tests if the first one still exists.
-        self.getPage("/varying_headers/", headers=[('Our-Varying-Header', 'request 2')])
-        self.assertStatus("200 OK")
-        self.assertBody('visit #2')
-        
-        self.getPage("/varying_headers/", headers=[('Our-Varying-Header', 'request 2')])
-        self.assertStatus("200 OK")
-        self.assertBody('visit #2')
-        
-        self.getPage("/varying_headers/")
-        self.assertStatus("200 OK")
-        self.assertBody('visit #1')
-        
-    def testExpiresTool(self):
-        # test setting an expires header
-        self.getPage("/expires/specific")
-        self.assertStatus("200 OK")
-        self.assertHeader("Expires")
-        
-        # test exceptions for bad time values
-        self.getPage("/expires/wrongtype")
-        self.assertStatus(500)
-        self.assertInBody("TypeError")
-        
-        # static content should not have "cache prevention" headers
-        self.getPage("/expires/index.html")
-        self.assertStatus("200 OK")
-        self.assertNoHeader("Pragma")
-        self.assertNoHeader("Cache-Control")
-        self.assertHeader("Expires")
-        
-        # dynamic content that sets indicators should not have
-        # "cache prevention" headers
-        self.getPage("/expires/cacheable")
-        self.assertStatus("200 OK")
-        self.assertNoHeader("Pragma")
-        self.assertNoHeader("Cache-Control")
-        self.assertHeader("Expires")
-        
-        self.getPage('/expires/dynamic')
-        self.assertBody("D-d-d-dynamic!")
-        # the Cache-Control header should be untouched
-        self.assertHeader("Cache-Control", "private")
-        self.assertHeader("Expires")
-        
-        # configure the tool to ignore indicators and replace existing headers
-        self.getPage("/expires/force")
-        self.assertStatus("200 OK")
-        # This also gives us a chance to test 0 expiry with no other headers
-        self.assertHeader("Pragma", "no-cache")
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            self.assertHeader("Cache-Control", "no-cache, must-revalidate")
-        self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-        
-        # static content should now have "cache prevention" headers
-        self.getPage("/expires/index.html")
-        self.assertStatus("200 OK")
-        self.assertHeader("Pragma", "no-cache")
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            self.assertHeader("Cache-Control", "no-cache, must-revalidate")
-        self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-        
-        # the cacheable handler should now have "cache prevention" headers
-        self.getPage("/expires/cacheable")
-        self.assertStatus("200 OK")
-        self.assertHeader("Pragma", "no-cache")
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            self.assertHeader("Cache-Control", "no-cache, must-revalidate")
-        self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-        
-        self.getPage('/expires/dynamic')
-        self.assertBody("D-d-d-dynamic!")
-        # dynamic sets Cache-Control to private but it should  be
-        # overwritten here ...
-        self.assertHeader("Pragma", "no-cache")
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            self.assertHeader("Cache-Control", "no-cache, must-revalidate")
-        self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-    
-    def testLastModified(self):
-        self.getPage("/a.gif")
-        self.assertStatus(200)
-        self.assertBody(gif_bytes)
-        lm1 = self.assertHeader("Last-Modified")
-        
-        # this request should get the cached copy.
-        self.getPage("/a.gif")
-        self.assertStatus(200)
-        self.assertBody(gif_bytes)
-        self.assertHeader("Age")
-        lm2 = self.assertHeader("Last-Modified")
-        self.assertEqual(lm1, lm2)
-        
-        # this request should match the cached copy, but raise 304.
-        self.getPage("/a.gif", [('If-Modified-Since', lm1)])
-        self.assertStatus(304)
-        self.assertNoHeader("Last-Modified")
-        if not getattr(cherrypy.server, "using_apache", False):
-            self.assertHeader("Age")
-    
-    def test_antistampede(self):
-        SECONDS = 4
-        # We MUST make an initial synchronous request in order to create the
-        # AntiStampedeCache object, and populate its selecting_headers,
-        # before the actual stampede.
-        self.getPage("/long_process?seconds=%d" % SECONDS)
-        self.assertBody('success!')
-        self.getPage("/clear_cache?path=" +
-            urllib.quote('/long_process?seconds=%d' % SECONDS, safe=''))
-        self.assertStatus(200)
-        sys.stdout.write("prepped... ")
-        sys.stdout.flush()
-        
-        start = datetime.datetime.now()
-        def run():
-            self.getPage("/long_process?seconds=%d" % SECONDS)
-            # The response should be the same every time
-            self.assertBody('success!')
-        ts = [threading.Thread(target=run) for i in xrange(100)]
-        for t in ts:
-            t.start()
-        for t in ts:
-            t.join()
-        self.assertEqualDates(start, datetime.datetime.now(),
-                              # Allow a second for our thread/TCP overhead etc.
-                              seconds=SECONDS + 1)
-    
-    def test_cache_control(self):
-        self.getPage("/control")
-        self.assertBody('visit #1')
-        self.getPage("/control")
-        self.assertBody('visit #1')
-        
-        self.getPage("/control", headers=[('Cache-Control', 'no-cache')])
-        self.assertBody('visit #2')
-        self.getPage("/control")
-        self.assertBody('visit #2')
-        
-        self.getPage("/control", headers=[('Pragma', 'no-cache')])
-        self.assertBody('visit #3')
-        self.getPage("/control")
-        self.assertBody('visit #3')
-        
-        time.sleep(1)
-        self.getPage("/control", headers=[('Cache-Control', 'max-age=0')])
-        self.assertBody('visit #4')
-        self.getPage("/control")
-        self.assertBody('visit #4')
-
-
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_config.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-"""Tests for the CherryPy configuration system."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os, sys
-localDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-import unittest
-
-import cherrypy
-
-def setup_server():
-    
-    class Root:
-        
-        _cp_config = {'foo': 'this',
-                      'bar': 'that'}
-        
-        def __init__(self):
-            cherrypy.config.namespaces['db'] = self.db_namespace
-        
-        def db_namespace(self, k, v):
-            if k == "scheme":
-                self.db = v
-        
-        # @cherrypy.expose(alias=('global_', 'xyz'))
-        def index(self, key):
-            return cherrypy.request.config.get(key, "None")
-        index = cherrypy.expose(index, alias=('global_', 'xyz'))
-        
-        def repr(self, key):
-            return repr(cherrypy.request.config.get(key, None))
-        repr.exposed = True
-        
-        def dbscheme(self):
-            return self.db
-        dbscheme.exposed = True
-        
-        def plain(self, x):
-            return x
-        plain.exposed = True
-        plain._cp_config = {'request.body.attempt_charsets': ['utf-16']}
-        
-        favicon_ico = cherrypy.tools.staticfile.handler(
-                        filename=os.path.join(localDir, '../favicon.ico'))
-    
-    class Foo:
-        
-        _cp_config = {'foo': 'this2',
-                      'baz': 'that2'}
-        
-        def index(self, key):
-            return cherrypy.request.config.get(key, "None")
-        index.exposed = True
-        nex = index
-        
-        def silly(self):
-            return 'Hello world'
-        silly.exposed = True
-        silly._cp_config = {'response.headers.X-silly': 'sillyval'}
-            
-        def bar(self, key):
-            return repr(cherrypy.request.config.get(key, None))
-        bar.exposed = True
-        bar._cp_config = {'foo': 'this3', 'bax': 'this4'}
-    
-    class Another:
-        
-        def index(self, key):
-            return str(cherrypy.request.config.get(key, "None"))
-        index.exposed = True
-    
-    
-    def raw_namespace(key, value):
-        if key == 'input.map':
-            handler = cherrypy.request.handler
-            def wrapper():
-                params = cherrypy.request.params
-                for name, coercer in list(value.items()):
-                    try:
-                        params[name] = coercer(params[name])
-                    except KeyError:
-                        pass
-                return handler()
-            cherrypy.request.handler = wrapper
-        elif key == 'output':
-            handler = cherrypy.request.handler
-            def wrapper():
-                # 'value' is a type (like int or str).
-                return value(handler())
-            cherrypy.request.handler = wrapper
-    
-    class Raw:
-        
-        _cp_config = {'raw.output': repr}
-        
-        def incr(self, num):
-            return num + 1
-        incr.exposed = True
-        incr._cp_config = {'raw.input.map': {'num': int}}
-    
-    ioconf = StringIO("""
-[/]
-neg: -1234
-filename: os.path.join(sys.prefix, "hello.py")
-thing1: cherrypy.lib.httputil.response_codes[404]
-thing2: __import__('cherrypy.tutorial', globals(), locals(), ['']).thing2
-complex: 3+2j
-ones: "11"
-twos: "22"
-stradd: %%(ones)s + %%(twos)s + "33"
-
-[/favicon.ico]
-tools.staticfile.filename = %r
-""" % os.path.join(localDir, 'static/dirback.jpg'))
-    
-    root = Root()
-    root.foo = Foo()
-    root.raw = Raw()
-    app = cherrypy.tree.mount(root, config=ioconf)
-    app.request_class.namespaces['raw'] = raw_namespace
-    
-    cherrypy.tree.mount(Another(), "/another")
-    cherrypy.config.update({'luxuryyacht': 'throatwobblermangrove',
-                            'db.scheme': r"sqlite///memory",
-                            })
-
-
-#                             Client-side code                             #
-
-from cherrypy.test import helper
-
-class ConfigTests(helper.CPWebCase):
-    
-    def testConfig(self):
-        tests = [
-            ('/',        'nex', 'None'),
-            ('/',        'foo', 'this'),
-            ('/',        'bar', 'that'),
-            ('/xyz',     'foo', 'this'),
-            ('/foo/',    'foo', 'this2'),
-            ('/foo/',    'bar', 'that'),
-            ('/foo/',    'bax', 'None'),
-            ('/foo/bar', 'baz', "'that2'"),
-            ('/foo/nex', 'baz', 'that2'),
-            # If 'foo' == 'this', then the mount point '/another' leaks into '/'.
-            ('/another/','foo', 'None'),
-        ]
-        for path, key, expected in tests:
-            self.getPage(path + "?key=" + key)
-            self.assertBody(expected)
-        
-        expectedconf = {
-            # From CP defaults
-            'tools.log_headers.on': False,
-            'tools.log_tracebacks.on': True,
-            'request.show_tracebacks': True,
-            'log.screen': False,
-            'environment': 'test_suite',
-            'engine.autoreload_on': False,
-            # From global config
-            'luxuryyacht': 'throatwobblermangrove',
-            # From Root._cp_config
-            'bar': 'that',
-            # From Foo._cp_config
-            'baz': 'that2',
-            # From Foo.bar._cp_config
-            'foo': 'this3',
-            'bax': 'this4',
-            }
-        for key, expected in expectedconf.items():
-            self.getPage("/foo/bar?key=" + key)
-            self.assertBody(repr(expected))
-    
-    def testUnrepr(self):
-        self.getPage("/repr?key=neg")
-        self.assertBody("-1234")
-        
-        self.getPage("/repr?key=filename")
-        self.assertBody(repr(os.path.join(sys.prefix, "hello.py")))
-        
-        self.getPage("/repr?key=thing1")
-        self.assertBody(repr(cherrypy.lib.httputil.response_codes[404]))
-        
-        if not getattr(cherrypy.server, "using_apache", False):
-            # The object ID's won't match up when using Apache, since the
-            # server and client are running in different processes.
-            self.getPage("/repr?key=thing2")
-            from cherrypy.tutorial import thing2
-            self.assertBody(repr(thing2))
-        
-        self.getPage("/repr?key=complex")
-        self.assertBody("(3+2j)")
-        
-        self.getPage("/repr?key=stradd")
-        self.assertBody(repr("112233"))
-
-    def testRespNamespaces(self):
-        self.getPage("/foo/silly")
-        self.assertHeader('X-silly', 'sillyval')
-        self.assertBody('Hello world')
-    
-    def testCustomNamespaces(self):
-        self.getPage("/raw/incr?num=12")
-        self.assertBody("13")
-        
-        self.getPage("/dbscheme")
-        self.assertBody(r"sqlite///memory")
-    
-    def testHandlerToolConfigOverride(self):
-        # Assert that config overrides tool constructor args. Above, we set
-        # the favicon in the page handler to be '../favicon.ico',
-        # but then overrode it in config to be './static/dirback.jpg'.
-        self.getPage("/favicon.ico")
-        self.assertBody(open(os.path.join(localDir, "static/dirback.jpg"),
-                             "rb").read())
-    
-    def test_request_body_namespace(self):
-        self.getPage("/plain", method='POST', headers=[
-            ('Content-Type', 'application/x-www-form-urlencoded'),
-            ('Content-Length', '13')],
-            body='\xff\xfex\x00=\xff\xfea\x00b\x00c\x00')
-        self.assertBody("abc")
-
-
-class VariableSubstitutionTests(unittest.TestCase):
-    
-    def test_config(self):
-        from textwrap import dedent
-    
-        # variable substitution with [DEFAULT]
-        conf = dedent("""
-        [DEFAULT]
-        dir = "/some/dir"
-        my.dir = %(dir)s + "/sub"
-
-        [my]
-        my.dir = %(dir)s + "/my/dir"
-        my.dir2 = %(my.dir)s + '/dir2'
-
-        """)
-
-        fp = StringIO(conf)
-
-        cherrypy.config.update(fp)
-        self.assertEqual(cherrypy.config["my"]["my.dir"], "/some/dir/my/dir")
-        self.assertEqual(cherrypy.config["my"]["my.dir2"], "/some/dir/my/dir/dir2")
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_config_server.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-"""Tests for the CherryPy configuration system."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os, sys
-localDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import socket
-import time
-
-import cherrypy
-
-def setup_server():
-    
-    class Root:
-        def index(self):
-            return cherrypy.request.wsgi_environ['SERVER_PORT']
-        index.exposed = True
-        
-        def upload(self, file):
-            return "Size: %s" % len(file.file.read())
-        upload.exposed = True
-        
-        def tinyupload(self):
-            return cherrypy.request.body.read()
-        tinyupload.exposed = True
-        tinyupload._cp_config = {'request.body.maxbytes': 100}
-    
-    cherrypy.tree.mount(Root())
-    
-    cherrypy.config.update({
-        'server.socket_host': '0.0.0.0',
-        'server.socket_port': 9876,
-        'server.max_request_body_size': 200,
-        'server.max_request_header_size': 500,
-        'server.socket_timeout': 0.5,
-        
-        # Test explicit server.instance
-        'server.2.instance': 'cherrypy._cpwsgi_server.CPWSGIServer',
-        'server.2.socket_port': 9877,
-        
-        # Test non-numeric <servername>
-        # Also test default server.instance = builtin server
-        'server.yetanother.socket_port': 9878,
-        })
-
-
-#                             Client-side code                             #
-
-from cherrypy.test import helper
-
-class ServerConfigTests(helper.CPWebCase):
-    
-    PORT = 9876
-    
-    def testBasicConfig(self):
-        self.getPage("/")
-        self.assertBody(str(self.PORT))
-    
-    def testAdditionalServers(self):
-        if self.scheme == 'https':
-            return self.skip("not available under ssl")
-        self.PORT = 9877
-        self.getPage("/")
-        self.assertBody(str(self.PORT))
-        self.PORT = 9878
-        self.getPage("/")
-        self.assertBody(str(self.PORT))
-    
-    def testMaxRequestSizePerHandler(self):
-        if getattr(cherrypy.server, "using_apache", False):
-            return self.skip("skipped due to known Apache differences... ")
-        
-        self.getPage('/tinyupload', method="POST",
-                     headers=[('Content-Type', 'text/plain'),
-                              ('Content-Length', '100')],
-                     body="x" * 100)
-        self.assertStatus(200)
-        self.assertBody("x" * 100)
-        
-        self.getPage('/tinyupload', method="POST",
-                     headers=[('Content-Type', 'text/plain'),
-                              ('Content-Length', '101')],
-                     body="x" * 101)
-        self.assertStatus(413)
-    
-    def testMaxRequestSize(self):
-        if getattr(cherrypy.server, "using_apache", False):
-            return self.skip("skipped due to known Apache differences... ")
-        
-        for size in (500, 5000, 50000):
-            self.getPage("/", headers=[('From', "x" * 500)])
-            self.assertStatus(413)
-        
-        # Test for http://www.cherrypy.org/ticket/421
-        # (Incorrect border condition in readline of SizeCheckWrapper).
-        # This hangs in rev 891 and earlier.
-        lines256 = "x" * 248
-        self.getPage("/",
-                     headers=[('Host', '%s:%s' % (self.HOST, self.PORT)),
-                              ('From', lines256)])
-        
-        # Test upload
-        body = '\r\n'.join([
-            '--x',
-            'Content-Disposition: form-data; name="file"; filename="hello.txt"',
-            'Content-Type: text/plain',
-            '',
-            '%s',
-            '--x--'])
-        partlen = 200 - len(body)
-        b = body % ("x" * partlen)
-        h = [("Content-type", "multipart/form-data; boundary=x"),
-             ("Content-Length", "%s" % len(b))]
-        self.getPage('/upload', h, "POST", b)
-        self.assertBody('Size: %d' % partlen)
-        
-        b = body % ("x" * 200)
-        h = [("Content-type", "multipart/form-data; boundary=x"),
-             ("Content-Length", "%s" % len(b))]
-        self.getPage('/upload', h, "POST", b)
-        self.assertStatus(413)
-
-
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_conn.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,687 +0,0 @@
-"""Tests for TCP connection handling, including proper and timely close."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-from httplib import HTTPConnection, HTTPSConnection, NotConnected, BadStatusLine
-import urllib
-import socket
-import sys
-import time
-timeout = 1
-
-
-import cherrypy
-from cherrypy.test import webtest
-from cherrypy import _cperror
-
-
-pov = 'pPeErRsSiIsStTeEnNcCeE oOfF vViIsSiIoOnN'
-
-def setup_server():
-    
-    def raise500():
-        raise cherrypy.HTTPError(500)
-    
-    class Root:
-        
-        def index(self):
-            return pov
-        index.exposed = True
-        page1 = index
-        page2 = index
-        page3 = index
-        
-        def hello(self):
-            return "Hello, world!"
-        hello.exposed = True
-        
-        def timeout(self, t):
-            return str(cherrypy.server.httpserver.timeout)
-        timeout.exposed = True
-        
-        def stream(self, set_cl=False):
-            if set_cl:
-                cherrypy.response.headers['Content-Length'] = 10
-            
-            def content():
-                for x in range(10):
-                    yield str(x)
-            
-            return content()
-        stream.exposed = True
-        stream._cp_config = {'response.stream': True}
-        
-        def error(self, code=500):
-            raise cherrypy.HTTPError(code)
-        error.exposed = True
-        
-        def upload(self):
-            if not cherrypy.request.method == 'POST':
-                raise AssertionError("'POST' != request.method %r" %
-                                     cherrypy.request.method)
-            return "thanks for '%s'" % cherrypy.request.body.read()
-        upload.exposed = True
-        
-        def custom(self, response_code):
-            cherrypy.response.status = response_code
-            return "Code = %s" % response_code
-        custom.exposed = True
-        
-        def err_before_read(self):
-            return "ok"
-        err_before_read.exposed = True
-        err_before_read._cp_config = {'hooks.on_start_resource': raise500}
-
-        def one_megabyte_of_a(self):
-            return ["a" * 1024] * 1024
-        one_megabyte_of_a.exposed = True
-    
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({
-        'server.max_request_body_size': 1001,
-        'server.socket_timeout': timeout,
-        })
-
-
-from cherrypy.test import helper
-
-class ConnectionCloseTests(helper.CPWebCase):
-    
-    def test_HTTP11(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        self.persistent = True
-        
-        # Make the first request and assert there's no "Connection: close".
-        self.getPage("/")
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertNoHeader("Connection")
-        
-        # Make another request on the same connection.
-        self.getPage("/page1")
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertNoHeader("Connection")
-        
-        # Test client-side close.
-        self.getPage("/page2", headers=[("Connection", "close")])
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertHeader("Connection", "close")
-        
-        # Make another request on the same connection, which should error.
-        self.assertRaises(NotConnected, self.getPage, "/")
-    
-    def test_Streaming_no_len(self):
-        self._streaming(set_cl=False)
-    
-    def test_Streaming_with_len(self):
-        self._streaming(set_cl=True)
-    
-    def _streaming(self, set_cl):
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            self.PROTOCOL = "HTTP/1.1"
-            
-            self.persistent = True
-            
-            # Make the first request and assert there's no "Connection: close".
-            self.getPage("/")
-            self.assertStatus('200 OK')
-            self.assertBody(pov)
-            self.assertNoHeader("Connection")
-            
-            # Make another, streamed request on the same connection.
-            if set_cl:
-                # When a Content-Length is provided, the content should stream
-                # without closing the connection.
-                self.getPage("/stream?set_cl=Yes")
-                self.assertHeader("Content-Length")
-                self.assertNoHeader("Connection", "close")
-                self.assertNoHeader("Transfer-Encoding")
-                
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-            else:
-                # When no Content-Length response header is provided,
-                # streamed output will either close the connection, or use
-                # chunked encoding, to determine transfer-length.
-                self.getPage("/stream")
-                self.assertNoHeader("Content-Length")
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-                
-                chunked_response = False
-                for k, v in self.headers:
-                    if k.lower() == "transfer-encoding":
-                        if str(v) == "chunked":
-                            chunked_response = True
-                
-                if chunked_response:
-                    self.assertNoHeader("Connection", "close")
-                else:
-                    self.assertHeader("Connection", "close")
-                    
-                    # Make another request on the same connection, which should error.
-                    self.assertRaises(NotConnected, self.getPage, "/")
-                
-                # Try HEAD. See http://www.cherrypy.org/ticket/864.
-                self.getPage("/stream", method='HEAD')
-                self.assertStatus('200 OK')
-                self.assertBody('')
-                self.assertNoHeader("Transfer-Encoding")
-        else:
-            self.PROTOCOL = "HTTP/1.0"
-            
-            self.persistent = True
-            
-            # Make the first request and assert Keep-Alive.
-            self.getPage("/", headers=[("Connection", "Keep-Alive")])
-            self.assertStatus('200 OK')
-            self.assertBody(pov)
-            self.assertHeader("Connection", "Keep-Alive")
-            
-            # Make another, streamed request on the same connection.
-            if set_cl:
-                # When a Content-Length is provided, the content should
-                # stream without closing the connection.
-                self.getPage("/stream?set_cl=Yes",
-                             headers=[("Connection", "Keep-Alive")])
-                self.assertHeader("Content-Length")
-                self.assertHeader("Connection", "Keep-Alive")
-                self.assertNoHeader("Transfer-Encoding")
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-            else:
-                # When a Content-Length is not provided,
-                # the server should close the connection.
-                self.getPage("/stream", headers=[("Connection", "Keep-Alive")])
-                self.assertStatus('200 OK')
-                self.assertBody('0123456789')
-                
-                self.assertNoHeader("Content-Length")
-                self.assertNoHeader("Connection", "Keep-Alive")
-                self.assertNoHeader("Transfer-Encoding")
-                
-                # Make another request on the same connection, which should error.
-                self.assertRaises(NotConnected, self.getPage, "/")
-    
-    def test_HTTP10_KeepAlive(self):
-        self.PROTOCOL = "HTTP/1.0"
-        if self.scheme == "https":
-            self.HTTP_CONN = HTTPSConnection
-        else:
-            self.HTTP_CONN = HTTPConnection
-        
-        # Test a normal HTTP/1.0 request.
-        self.getPage("/page2")
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        # Apache, for example, may emit a Connection header even for HTTP/1.0
-##        self.assertNoHeader("Connection")
-        
-        # Test a keep-alive HTTP/1.0 request.
-        self.persistent = True
-        
-        self.getPage("/page3", headers=[("Connection", "Keep-Alive")])
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertHeader("Connection", "Keep-Alive")
-        
-        # Remove the keep-alive header again.
-        self.getPage("/page3")
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        # Apache, for example, may emit a Connection header even for HTTP/1.0
-##        self.assertNoHeader("Connection")
-
-
-class PipelineTests(helper.CPWebCase):
-    
-    def test_HTTP11_Timeout(self):
-        # If we timeout without sending any data,
-        # the server will close the conn with a 408.
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        # Connect but send nothing.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.auto_open = False
-        conn.connect()
-        
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-        
-        # The request should have returned 408 already.
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 408)
-        conn.close()
-        
-        # Connect but send half the headers only.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.auto_open = False
-        conn.connect()
-        conn.send('GET /hello HTTP/1.1')
-        conn.send(("Host: %s" % self.HOST).encode('ascii'))
-        
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-        
-        # The conn should have already sent 408.
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 408)
-        conn.close()
-    
-    def test_HTTP11_Timeout_after_request(self):
-        # If we timeout after at least one request has succeeded,
-        # the server will close the conn without 408.
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        # Make an initial request
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest("GET", "/timeout?t=%s" % timeout, skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody(str(timeout))
-        
-        # Make a second request on the same socket
-        conn._output('GET /hello HTTP/1.1')
-        conn._output("Host: %s" % self.HOST)
-        conn._send_output()
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody("Hello, world!")
-        
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-        
-        # Make another request on the same socket, which should error
-        conn._output('GET /hello HTTP/1.1')
-        conn._output("Host: %s" % self.HOST)
-        conn._send_output()
-        response = conn.response_class(conn.sock, method="GET")
-        try:
-            response.begin()
-        except:
-            if not isinstance(sys.exc_info()[1],
-                              (socket.error, BadStatusLine)):
-                self.fail("Writing to timed out socket didn't fail"
-                          " as it should have: %s" % sys.exc_info()[1])
-        else:
-            if response.status != 408:
-                self.fail("Writing to timed out socket didn't fail"
-                          " as it should have: %s" %
-                          response.read())
-        
-        conn.close()
-        
-        # Make another request on a new socket, which should work
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest("GET", "/", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody(pov)
-
-        
-        # Make another request on the same socket,
-        # but timeout on the headers
-        conn.send('GET /hello HTTP/1.1')
-        # Wait for our socket timeout
-        time.sleep(timeout * 2)
-        response = conn.response_class(conn.sock, method="GET")
-        try:
-            response.begin()
-        except:
-            if not isinstance(sys.exc_info()[1],
-                              (socket.error, BadStatusLine)):
-                self.fail("Writing to timed out socket didn't fail"
-                          " as it should have: %s" % sys.exc_info()[1])
-        else:
-            self.fail("Writing to timed out socket didn't fail"
-                      " as it should have: %s" %
-                      response.read())
-        
-        conn.close()
-        
-        # Retry the request on a new connection, which should work
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest("GET", "/", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 200)
-        self.body = response.read()
-        self.assertBody(pov)
-        conn.close()
-    
-    def test_HTTP11_pipelining(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        # Test pipelining. httplib doesn't support this directly.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        
-        # Put request 1
-        conn.putrequest("GET", "/hello", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.endheaders()
-        
-        for trial in range(5):
-            # Put next request
-            conn._output('GET /hello HTTP/1.1')
-            conn._output("Host: %s" % self.HOST)
-            conn._send_output()
-            
-            # Retrieve previous response
-            response = conn.response_class(conn.sock, method="GET")
-            response.begin()
-            body = response.read()
-            self.assertEqual(response.status, 200)
-            self.assertEqual(body, "Hello, world!")
-        
-        # Retrieve final response
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        body = response.read()
-        self.assertEqual(response.status, 200)
-        self.assertEqual(body, "Hello, world!")
-        
-        conn.close()
-    
-    def test_100_Continue(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        self.persistent = True
-        conn = self.HTTP_CONN
-        
-        # Try a page without an Expect request header first.
-        # Note that httplib's response.begin automatically ignores
-        # 100 Continue responses, so we must manually check for it.
-        conn.putrequest("POST", "/upload", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.putheader("Content-Type", "text/plain")
-        conn.putheader("Content-Length", "4")
-        conn.endheaders()
-        conn.send("d'oh")
-        response = conn.response_class(conn.sock, method="POST")
-        version, status, reason = response._read_status()
-        self.assertNotEqual(status, 100)
-        conn.close()
-        
-        # Now try a page with an Expect header...
-        conn.connect()
-        conn.putrequest("POST", "/upload", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.putheader("Content-Type", "text/plain")
-        conn.putheader("Content-Length", "17")
-        conn.putheader("Expect", "100-continue")
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method="POST")
-        
-        # ...assert and then skip the 100 response
-        version, status, reason = response._read_status()
-        self.assertEqual(status, 100)
-        while True:
-            line = response.fp.readline().strip()
-            if line:
-                self.fail("100 Continue should not output any headers. Got %r" % line)
-            else:
-                break
-        
-        # ...send the body
-        conn.send("I am a small file")
-        
-        # ...get the final response
-        response.begin()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus(200)
-        self.assertBody("thanks for 'I am a small file'")
-        conn.close()
-
-
-class ConnectionTests(helper.CPWebCase):
-    
-    def test_readall_or_close(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        if self.scheme == "https":
-            self.HTTP_CONN = HTTPSConnection
-        else:
-            self.HTTP_CONN = HTTPConnection
-        
-        # Test a max of 0 (the default) and then reset to what it was above.
-        old_max = cherrypy.server.max_request_body_size
-        for new_max in (0, old_max):
-            cherrypy.server.max_request_body_size = new_max
-            
-            self.persistent = True
-            conn = self.HTTP_CONN
-            
-            # Get a POST page with an error
-            conn.putrequest("POST", "/err_before_read", skip_host=True)
-            conn.putheader("Host", self.HOST)
-            conn.putheader("Content-Type", "text/plain")
-            conn.putheader("Content-Length", "1000")
-            conn.putheader("Expect", "100-continue")
-            conn.endheaders()
-            response = conn.response_class(conn.sock, method="POST")
-            
-            # ...assert and then skip the 100 response
-            version, status, reason = response._read_status()
-            self.assertEqual(status, 100)
-            while True:
-                skip = response.fp.readline().strip()
-                if not skip:
-                    break
-            
-            # ...send the body
-            conn.send("x" * 1000)
-            
-            # ...get the final response
-            response.begin()
-            self.status, self.headers, self.body = webtest.shb(response)
-            self.assertStatus(500)
-            
-            # Now try a working page with an Expect header...
-            conn._output('POST /upload HTTP/1.1')
-            conn._output("Host: %s" % self.HOST)
-            conn._output("Content-Type: text/plain")
-            conn._output("Content-Length: 17")
-            conn._output("Expect: 100-continue")
-            conn._send_output()
-            response = conn.response_class(conn.sock, method="POST")
-            
-            # ...assert and then skip the 100 response
-            version, status, reason = response._read_status()
-            self.assertEqual(status, 100)
-            while True:
-                skip = response.fp.readline().strip()
-                if not skip:
-                    break
-            
-            # ...send the body
-            conn.send("I am a small file")
-            
-            # ...get the final response
-            response.begin()
-            self.status, self.headers, self.body = webtest.shb(response)
-            self.assertStatus(200)
-            self.assertBody("thanks for 'I am a small file'")
-            conn.close()
-    
-    def test_No_Message_Body(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        # Set our HTTP_CONN to an instance so it persists between requests.
-        self.persistent = True
-        
-        # Make the first request and assert there's no "Connection: close".
-        self.getPage("/")
-        self.assertStatus('200 OK')
-        self.assertBody(pov)
-        self.assertNoHeader("Connection")
-        
-        # Make a 204 request on the same connection.
-        self.getPage("/custom/204")
-        self.assertStatus(204)
-        self.assertNoHeader("Content-Length")
-        self.assertBody("")
-        self.assertNoHeader("Connection")
-        
-        # Make a 304 request on the same connection.
-        self.getPage("/custom/304")
-        self.assertStatus(304)
-        self.assertNoHeader("Content-Length")
-        self.assertBody("")
-        self.assertNoHeader("Connection")
-    
-    def test_Chunked_Encoding(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        if (hasattr(self, 'harness') and
-            "modpython" in self.harness.__class__.__name__.lower()):
-            # mod_python forbids chunked encoding
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        # Set our HTTP_CONN to an instance so it persists between requests.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        
-        # Try a normal chunked request (with extensions)
-        body = ("8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n"
-                "Content-Type: application/json\r\n"
-                "\r\n")
-        conn.putrequest("POST", "/upload", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.putheader("Transfer-Encoding", "chunked")
-        conn.putheader("Trailer", "Content-Type")
-        # Note that this is somewhat malformed:
-        # we shouldn't be sending Content-Length.
-        # RFC 2616 says the server should ignore it.
-        conn.putheader("Content-Length", "3")
-        conn.endheaders()
-        conn.send(body)
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus('200 OK')
-        self.assertBody("thanks for 'xx\r\nxxxxyyyyy'")
-        
-        # Try a chunked request that exceeds server.max_request_body_size.
-        # Note that the delimiters and trailer are included.
-        body = "3e3\r\n" + ("x" * 995) + "\r\n0\r\n\r\n"
-        conn.putrequest("POST", "/upload", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.putheader("Transfer-Encoding", "chunked")
-        conn.putheader("Content-Type", "text/plain")
-        # Chunked requests don't need a content-length
-##        conn.putheader("Content-Length", len(body))
-        conn.endheaders()
-        conn.send(body)
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus(413)
-        conn.close()
-    
-    def test_Content_Length(self):
-        # Try a non-chunked request where Content-Length exceeds
-        # server.max_request_body_size. Assert error before body send.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest("POST", "/upload", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.putheader("Content-Type", "text/plain")
-        conn.putheader("Content-Length", "9999")
-        conn.endheaders()
-        response = conn.getresponse()
-        self.status, self.headers, self.body = webtest.shb(response)
-        self.assertStatus(413)
-        self.assertBody("")
-        conn.close()
-    
-    def test_598(self):
-        remote_data_conn = urllib.urlopen('%s://%s:%s/one_megabyte_of_a/' %
-                                          (self.scheme, self.HOST, self.PORT,))
-        buf = remote_data_conn.read(512)
-        time.sleep(timeout * 0.6)
-        remaining = (1024 * 1024) - 512
-        while remaining:
-            data = remote_data_conn.read(remaining)
-            if not data:
-                break
-            else:
-                buf += data
-            remaining -= len(data)
-       
-        self.assertEqual(len(buf), 1024 * 1024)
-        self.assertEqual(buf, "a" * 1024 * 1024)
-        self.assertEqual(remaining, 0)
-        remote_data_conn.close()
-
-
-class BadRequestTests(helper.CPWebCase):
-    
-    def test_No_CRLF(self):
-        self.persistent = True
-        
-        conn = self.HTTP_CONN
-        conn.send('GET /hello HTTP/1.1\n\n')
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.body = response.read()
-        self.assertBody("HTTP requires CRLF terminators")
-        conn.close()
-        
-        conn.connect()
-        conn.send('GET /hello HTTP/1.1\r\n\n')
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.body = response.read()
-        self.assertBody("HTTP requires CRLF terminators")
-        conn.close()
-
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_core.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,611 +0,0 @@
-"""Basic tests for the CherryPy core: request handling."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os
-localDir = os.path.dirname(__file__)
-import sys
-import types
-from httplib import IncompleteRead
-
-import cherrypy
-from cherrypy import _cptools, tools
-from cherrypy.lib import httputil, static
-
-
-favicon_path = os.path.join(os.getcwd(), localDir, "../favicon.ico")
-
-def setup_server():
-    class Root:
-        
-        def index(self):
-            return "hello"
-        index.exposed = True
-        
-        favicon_ico = tools.staticfile.handler(filename=favicon_path)
-        
-        def defct(self, newct):
-            newct = "text/%s" % newct
-            cherrypy.config.update({'tools.response_headers.on': True,
-                                    'tools.response_headers.headers':
-                                    [('Content-Type', newct)]})
-        defct.exposed = True
-        
-        def baseurl(self, path_info, relative=None):
-            return cherrypy.url(path_info, relative=bool(relative))
-        baseurl.exposed = True
-    
-    root = Root()
-    
-    
-    class TestType(type):
-        """Metaclass which automatically exposes all functions in each subclass,
-        and adds an instance of the subclass as an attribute of root.
-        """
-        def __init__(cls, name, bases, dct):
-            type.__init__(cls, name, bases, dct)
-            for value in dct.itervalues():
-                if isinstance(value, types.FunctionType):
-                    value.exposed = True
-            setattr(root, name.lower(), cls())
-    class Test(object):
-        __metaclass__ = TestType
-    
-    
-    class URL(Test):
-        
-        _cp_config = {'tools.trailing_slash.on': False}
-        
-        def index(self, path_info, relative=None):
-            if relative != 'server':
-                relative = bool(relative)
-            return cherrypy.url(path_info, relative=relative)
-        
-        def leaf(self, path_info, relative=None):
-            if relative != 'server':
-                relative = bool(relative)
-            return cherrypy.url(path_info, relative=relative)
-
-
-    class Status(Test):
-        
-        def index(self):
-            return "normal"
-        
-        def blank(self):
-            cherrypy.response.status = ""
-        
-        # According to RFC 2616, new status codes are OK as long as they
-        # are between 100 and 599.
-        
-        # Here is an illegal code...
-        def illegal(self):
-            cherrypy.response.status = 781
-            return "oops"
-        
-        # ...and here is an unknown but legal code.
-        def unknown(self):
-            cherrypy.response.status = "431 My custom error"
-            return "funky"
-        
-        # Non-numeric code
-        def bad(self):
-            cherrypy.response.status = "error"
-            return "bad news"
-
-
-    class Redirect(Test):
-        
-        class Error:
-            _cp_config = {"tools.err_redirect.on": True,
-                          "tools.err_redirect.url": "/errpage",
-                          "tools.err_redirect.internal": False,
-                          }
-            
-            def index(self):
-                raise NameError("redirect_test")
-            index.exposed = True
-        error = Error()
-        
-        def index(self):
-            return "child"
-        
-        def by_code(self, code):
-            raise cherrypy.HTTPRedirect("somewhere else", code)
-        by_code._cp_config = {'tools.trailing_slash.extra': True}
-        
-        def nomodify(self):
-            raise cherrypy.HTTPRedirect("", 304)
-        
-        def proxy(self):
-            raise cherrypy.HTTPRedirect("proxy", 305)
-        
-        def stringify(self):
-            return str(cherrypy.HTTPRedirect("/"))
-        
-        def fragment(self, frag):
-            raise cherrypy.HTTPRedirect("/some/url#%s" % frag)
-    
-    def login_redir():
-        if not getattr(cherrypy.request, "login", None):
-            raise cherrypy.InternalRedirect("/internalredirect/login")
-    tools.login_redir = _cptools.Tool('before_handler', login_redir)
-    
-    def redir_custom():
-        raise cherrypy.InternalRedirect("/internalredirect/custom_err")
-    
-    class InternalRedirect(Test):
-        
-        def index(self):
-            raise cherrypy.InternalRedirect("/")
-        
-        def choke(self):
-            return 3 / 0
-        choke.exposed = True
-        choke._cp_config = {'hooks.before_error_response': redir_custom}
-        
-        def relative(self, a, b):
-            raise cherrypy.InternalRedirect("cousin?t=6")
-        
-        def cousin(self, t):
-            assert cherrypy.request.prev.closed
-            return cherrypy.request.prev.query_string
-        
-        def petshop(self, user_id):
-            if user_id == "parrot":
-                # Trade it for a slug when redirecting
-                raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=slug')
-            elif user_id == "terrier":
-                # Trade it for a fish when redirecting
-                raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=fish')
-            else:
-                # This should pass the user_id through to getImagesByUser
-                raise cherrypy.InternalRedirect(
-                    '/image/getImagesByUser?user_id=%s' % str(user_id))
-        
-        # We support Python 2.3, but the @-deco syntax would look like this:
-        # @tools.login_redir()
-        def secure(self):
-            return "Welcome!"
-        secure = tools.login_redir()(secure)
-        # Since calling the tool returns the same function you pass in,
-        # you could skip binding the return value, and just write:
-        # tools.login_redir()(secure)
-        
-        def login(self):
-            return "Please log in"
-        
-        def custom_err(self):
-            return "Something went horribly wrong."
-        
-        def early_ir(self, arg):
-            return "whatever"
-        early_ir._cp_config = {'hooks.before_request_body': redir_custom}
-    
-    
-    class Image(Test):
-        
-        def getImagesByUser(self, user_id):
-            return "0 images for %s" % user_id
-
-
-    class Flatten(Test):
-        
-        def as_string(self):
-            return "content"
-        
-        def as_list(self):
-            return ["con", "tent"]
-        
-        def as_yield(self):
-            yield "content"
-        
-        def as_dblyield(self):
-            yield self.as_yield()
-        as_dblyield._cp_config = {'tools.flatten.on': True}
-        
-        def as_refyield(self):
-            for chunk in self.as_yield():
-                yield chunk
-    
-    
-    class Ranges(Test):
-        
-        def get_ranges(self, bytes):
-            return repr(httputil.get_ranges('bytes=%s' % bytes, 8))
-        
-        def slice_file(self):
-            path = os.path.join(os.getcwd(), os.path.dirname(__file__))
-            return static.serve_file(os.path.join(path, "static/index.html"))
-
-
-    class Cookies(Test):
-        
-        def single(self, name):
-            cookie = cherrypy.request.cookie[name]
-            # Python2's SimpleCookie.__setitem__ won't take unicode keys.
-            cherrypy.response.cookie[str(name)] = cookie.value
-        
-        def multiple(self, names):
-            for name in names:
-                cookie = cherrypy.request.cookie[name]
-                # Python2's SimpleCookie.__setitem__ won't take unicode keys.
-                cherrypy.response.cookie[str(name)] = cookie.value
-
-
-    if sys.version_info >= (2, 5):
-        from cherrypy.test import py25
-        Root.expose_dec = py25.ExposeExamples()
-    
-    cherrypy.tree.mount(root)
-
-
-#                             Client-side code                             #
-
-from cherrypy.test import helper
-
-class CoreRequestHandlingTest(helper.CPWebCase):
-
-    def testStatus(self):
-        self.getPage("/status/")
-        self.assertBody('normal')
-        self.assertStatus(200)
-        
-        self.getPage("/status/blank")
-        self.assertBody('')
-        self.assertStatus(200)
-        
-        self.getPage("/status/illegal")
-        self.assertStatus(500)
-        msg = "Illegal response status from server (781 is out of range)."
-        self.assertErrorPage(500, msg)
-        
-        if not getattr(cherrypy.server, 'using_apache', False):
-            self.getPage("/status/unknown")
-            self.assertBody('funky')
-            self.assertStatus(431)
-        
-        self.getPage("/status/bad")
-        self.assertStatus(500)
-        msg = "Illegal response status from server ('error' is non-numeric)."
-        self.assertErrorPage(500, msg)
-    
-    def testSlashes(self):
-        # Test that requests for index methods without a trailing slash
-        # get redirected to the same URI path with a trailing slash.
-        # Make sure GET params are preserved.
-        self.getPage("/redirect?id=3")
-        self.assertStatus(301)
-        self.assertInBody("<a href='%s/redirect/?id=3'>"
-                          "%s/redirect/?id=3</a>" % (self.base(), self.base()))
-        
-        if self.prefix():
-            # Corner case: the "trailing slash" redirect could be tricky if
-            # we're using a virtual root and the URI is "/vroot" (no slash).
-            self.getPage("")
-            self.assertStatus(301)
-            self.assertInBody("<a href='%s/'>%s/</a>" %
-                              (self.base(), self.base()))
-        
-        # Test that requests for NON-index methods WITH a trailing slash
-        # get redirected to the same URI path WITHOUT a trailing slash.
-        # Make sure GET params are preserved.
-        self.getPage("/redirect/by_code/?code=307")
-        self.assertStatus(301)
-        self.assertInBody("<a href='%s/redirect/by_code?code=307'>"
-                          "%s/redirect/by_code?code=307</a>"
-                          % (self.base(), self.base()))
-        
-        # If the trailing_slash tool is off, CP should just continue
-        # as if the slashes were correct. But it needs some help
-        # inside cherrypy.url to form correct output.
-        self.getPage('/url?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/leaf/?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-    
-    def testRedirect(self):
-        self.getPage("/redirect/")
-        self.assertBody('child')
-        self.assertStatus(200)
-        
-        self.getPage("/redirect/by_code?code=300")
-        self.assertMatchesBody(r"<a href='(.*)somewhere else'>\1somewhere else</a>")
-        self.assertStatus(300)
-        
-        self.getPage("/redirect/by_code?code=301")
-        self.assertMatchesBody(r"<a href='(.*)somewhere else'>\1somewhere else</a>")
-        self.assertStatus(301)
-        
-        self.getPage("/redirect/by_code?code=302")
-        self.assertMatchesBody(r"<a href='(.*)somewhere else'>\1somewhere else</a>")
-        self.assertStatus(302)
-        
-        self.getPage("/redirect/by_code?code=303")
-        self.assertMatchesBody(r"<a href='(.*)somewhere else'>\1somewhere else</a>")
-        self.assertStatus(303)
-        
-        self.getPage("/redirect/by_code?code=307")
-        self.assertMatchesBody(r"<a href='(.*)somewhere else'>\1somewhere else</a>")
-        self.assertStatus(307)
-        
-        self.getPage("/redirect/nomodify")
-        self.assertBody('')
-        self.assertStatus(304)
-        
-        self.getPage("/redirect/proxy")
-        self.assertBody('')
-        self.assertStatus(305)
-        
-        # HTTPRedirect on error
-        self.getPage("/redirect/error/")
-        self.assertStatus(('302 Found', '303 See Other'))
-        self.assertInBody('/errpage')
-        
-        # Make sure str(HTTPRedirect()) works.
-        self.getPage("/redirect/stringify", protocol="HTTP/1.0")
-        self.assertStatus(200)
-        self.assertBody("(['%s/'], 302)" % self.base())
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            self.getPage("/redirect/stringify", protocol="HTTP/1.1")
-            self.assertStatus(200)
-            self.assertBody("(['%s/'], 303)" % self.base())
-        
-        # check that #fragments are handled properly
-        # http://skrb.org/ietf/http_errata.html#location-fragments
-        frag = "foo"
-        self.getPage("/redirect/fragment/%s" % frag)
-        self.assertMatchesBody(r"<a href='(.*)\/some\/url\#%s'>\1\/some\/url\#%s</a>" % (frag, frag))
-        loc = self.assertHeader('Location')
-        assert loc.endswith("#%s" % frag)
-        self.assertStatus(('302 Found', '303 See Other'))
-    
-    def test_InternalRedirect(self):
-        # InternalRedirect
-        self.getPage("/internalredirect/")
-        self.assertBody('hello')
-        self.assertStatus(200)
-        
-        # Test passthrough
-        self.getPage("/internalredirect/petshop?user_id=Sir-not-appearing-in-this-film")
-        self.assertBody('0 images for Sir-not-appearing-in-this-film')
-        self.assertStatus(200)
-        
-        # Test args
-        self.getPage("/internalredirect/petshop?user_id=parrot")
-        self.assertBody('0 images for slug')
-        self.assertStatus(200)
-        
-        # Test POST
-        self.getPage("/internalredirect/petshop", method="POST",
-                     body="user_id=terrier")
-        self.assertBody('0 images for fish')
-        self.assertStatus(200)
-        
-        # Test ir before body read
-        self.getPage("/internalredirect/early_ir", method="POST",
-                     body="arg=aha!")
-        self.assertBody("Something went horribly wrong.")
-        self.assertStatus(200)
-        
-        self.getPage("/internalredirect/secure")
-        self.assertBody('Please log in')
-        self.assertStatus(200)
-        
-        # Relative path in InternalRedirect.
-        # Also tests request.prev.
-        self.getPage("/internalredirect/relative?a=3&b=5")
-        self.assertBody("a=3&b=5")
-        self.assertStatus(200)
-        
-        # InternalRedirect on error
-        self.getPage("/internalredirect/choke")
-        self.assertStatus(200)
-        self.assertBody("Something went horribly wrong.")
-    
-    def testFlatten(self):
-        for url in ["/flatten/as_string", "/flatten/as_list",
-                    "/flatten/as_yield", "/flatten/as_dblyield",
-                    "/flatten/as_refyield"]:
-            self.getPage(url)
-            self.assertBody('content')
-    
-    def testRanges(self):
-        self.getPage("/ranges/get_ranges?bytes=3-6")
-        self.assertBody("[(3, 7)]")
-        
-        # Test multiple ranges and a suffix-byte-range-spec, for good measure.
-        self.getPage("/ranges/get_ranges?bytes=2-4,-1")
-        self.assertBody("[(2, 5), (7, 8)]")
-        
-        # Get a partial file.
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')])
-            self.assertStatus(206)
-            self.assertHeader("Content-Type", "text/html;charset=utf-8")
-            self.assertHeader("Content-Range", "bytes 2-5/14")
-            self.assertBody("llo,")
-            
-            # What happens with overlapping ranges (and out of order, too)?
-            self.getPage("/ranges/slice_file", [('Range', 'bytes=4-6,2-5')])
-            self.assertStatus(206)
-            ct = self.assertHeader("Content-Type")
-            expected_type = "multipart/byteranges; boundary="
-            self.assert_(ct.startswith(expected_type))
-            boundary = ct[len(expected_type):]
-            expected_body = ("\r\n--%s\r\n"
-                             "Content-type: text/html\r\n"
-                             "Content-range: bytes 4-6/14\r\n"
-                             "\r\n"
-                             "o, \r\n"
-                             "--%s\r\n"
-                             "Content-type: text/html\r\n"
-                             "Content-range: bytes 2-5/14\r\n"
-                             "\r\n"
-                             "llo,\r\n"
-                             "--%s--\r\n" % (boundary, boundary, boundary))
-            self.assertBody(expected_body)
-            self.assertHeader("Content-Length")
-            
-            # Test "416 Requested Range Not Satisfiable"
-            self.getPage("/ranges/slice_file", [('Range', 'bytes=2300-2900')])
-            self.assertStatus(416)
-            # "When this status code is returned for a byte-range request,
-            # the response SHOULD include a Content-Range entity-header
-            # field specifying the current length of the selected resource"
-            self.assertHeader("Content-Range", "bytes */14")
-        elif cherrypy.server.protocol_version == "HTTP/1.0":
-            # Test Range behavior with HTTP/1.0 request
-            self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')])
-            self.assertStatus(200)
-            self.assertBody("Hello, world\r\n")
-    
-    def testFavicon(self):
-        # favicon.ico is served by staticfile.
-        icofilename = os.path.join(localDir, "../favicon.ico")
-        icofile = open(icofilename, "rb")
-        data = icofile.read()
-        icofile.close()
-        
-        self.getPage("/favicon.ico")
-        self.assertBody(data)
-    
-    def testCookies(self):
-        if sys.version_info >= (2, 5):
-            header_value = lambda x: x
-        else:
-            header_value = lambda x: x+';'
-        
-        self.getPage("/cookies/single?name=First",
-                     [('Cookie', 'First=Dinsdale;')])
-        self.assertHeader('Set-Cookie', header_value('First=Dinsdale'))
-        
-        self.getPage("/cookies/multiple?names=First&names=Last",
-                     [('Cookie', 'First=Dinsdale; Last=Piranha;'),
-                      ])
-        self.assertHeader('Set-Cookie', header_value('First=Dinsdale'))
-        self.assertHeader('Set-Cookie', header_value('Last=Piranha'))
-        
-        self.getPage("/cookies/single?name=Something-With:Colon",
-            [('Cookie', 'Something-With:Colon=some-value')])
-        self.assertStatus(400)
-    
-    def testDefaultContentType(self):
-        self.getPage('/')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.getPage('/defct/plain')
-        self.getPage('/')
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-        self.getPage('/defct/html')
-    
-    def test_cherrypy_url(self):
-        # Input relative to current
-        self.getPage('/url/leaf?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/?path_info=page1')
-        self.assertBody('%s/url/page1' % self.base())
-        # Other host header
-        host = 'www.mydomain.example'
-        self.getPage('/url/leaf?path_info=page1',
-                     headers=[('Host', host)])
-        self.assertBody('%s://%s/url/page1' % (self.scheme, host))
-        
-        # Input is 'absolute'; that is, relative to script_name
-        self.getPage('/url/leaf?path_info=/page1')
-        self.assertBody('%s/page1' % self.base())
-        self.getPage('/url/?path_info=/page1')
-        self.assertBody('%s/page1' % self.base())
-        
-        # Single dots
-        self.getPage('/url/leaf?path_info=./page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/leaf?path_info=other/./page1')
-        self.assertBody('%s/url/other/page1' % self.base())
-        self.getPage('/url/?path_info=/other/./page1')
-        self.assertBody('%s/other/page1' % self.base())
-        
-        # Double dots
-        self.getPage('/url/leaf?path_info=../page1')
-        self.assertBody('%s/page1' % self.base())
-        self.getPage('/url/leaf?path_info=other/../page1')
-        self.assertBody('%s/url/page1' % self.base())
-        self.getPage('/url/leaf?path_info=/other/../page1')
-        self.assertBody('%s/page1' % self.base())
-        
-        # Output relative to current path or script_name
-        self.getPage('/url/?path_info=page1&relative=True')
-        self.assertBody('page1')
-        self.getPage('/url/leaf?path_info=/page1&relative=True')
-        self.assertBody('../page1')
-        self.getPage('/url/leaf?path_info=page1&relative=True')
-        self.assertBody('page1')
-        self.getPage('/url/leaf?path_info=leaf/page1&relative=True')
-        self.assertBody('leaf/page1')
-        self.getPage('/url/leaf?path_info=../page1&relative=True')
-        self.assertBody('../page1')
-        self.getPage('/url/?path_info=other/../page1&relative=True')
-        self.assertBody('page1')
-        
-        # Output relative to /
-        self.getPage('/baseurl?path_info=ab&relative=True')
-        self.assertBody('ab')
-        # Output relative to /
-        self.getPage('/baseurl?path_info=/ab&relative=True')
-        self.assertBody('ab')
-        
-        # absolute-path references ("server-relative")
-        # Input relative to current
-        self.getPage('/url/leaf?path_info=page1&relative=server')
-        self.assertBody('/url/page1')
-        self.getPage('/url/?path_info=page1&relative=server')
-        self.assertBody('/url/page1')
-        # Input is 'absolute'; that is, relative to script_name
-        self.getPage('/url/leaf?path_info=/page1&relative=server')
-        self.assertBody('/page1')
-        self.getPage('/url/?path_info=/page1&relative=server')
-        self.assertBody('/page1')
-    
-    def test_expose_decorator(self):
-        if not sys.version_info >= (2, 5):
-            return self.skip("skipped (Python 2.5+ only) ")
-        
-        # Test @expose
-        self.getPage("/expose_dec/no_call")
-        self.assertStatus(200)
-        self.assertBody("Mr E. R. Bradshaw")
-        
-        # Test @expose()
-        self.getPage("/expose_dec/call_empty")
-        self.assertStatus(200)
-        self.assertBody("Mrs. B.J. Smegma")
-        
-        # Test @expose("alias")
-        self.getPage("/expose_dec/call_alias")
-        self.assertStatus(200)
-        self.assertBody("Mr Nesbitt")
-        # Does the original name work?
-        self.getPage("/expose_dec/nesbitt")
-        self.assertStatus(200)
-        self.assertBody("Mr Nesbitt")
-        
-        # Test @expose(["alias1", "alias2"])
-        self.getPage("/expose_dec/alias1")
-        self.assertStatus(200)
-        self.assertBody("Mr Ken Andrews")
-        self.getPage("/expose_dec/alias2")
-        self.assertStatus(200)
-        self.assertBody("Mr Ken Andrews")
-        # Does the original name work?
-        self.getPage("/expose_dec/andrews")
-        self.assertStatus(200)
-        self.assertBody("Mr Ken Andrews")
-        
-        # Test @expose(alias="alias")
-        self.getPage("/expose_dec/alias3")
-        self.assertStatus(200)
-        self.assertBody("Mr. and Mrs. Watson")
-
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_dynamicobjectmapping.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,302 +0,0 @@
-from cherrypy.test import test
-from cherrypy._cptree import Application
-test.prefer_parent_path()
-
-import cherrypy
-
-script_names = ["", "/foo", "/users/fred/blog", "/corp/blog"]
-
-def setup_server():
-    class SubSubRoot:
-        def index(self):
-            return "SubSubRoot index"
-        index.exposed = True
-
-        def default(self, *args):
-            return "SubSubRoot default"
-        default.exposed = True
-
-        def handler(self):
-            return "SubSubRoot handler"
-        handler.exposed = True
-
-        def dispatch(self):
-            return "SubSubRoot dispatch"
-        dispatch.exposed = True
-
-    subsubnodes = {
-        '1': SubSubRoot(),
-        '2': SubSubRoot(),
-    }
-
-    class SubRoot:
-        def index(self):
-            return "SubRoot index"
-        index.exposed = True
-
-        def default(self, *args):
-            return "SubRoot %s" % (args,)
-        default.exposed = True
-
-        def handler(self):
-            return "SubRoot handler"
-        handler.exposed = True
-
-        def _cp_dispatch(self, vpath):
-            return subsubnodes.get(vpath[0], None)
-
-    subnodes = {
-        '1': SubRoot(),
-        '2': SubRoot(),
-    }
-    class Root:
-        def index(self):
-            return "index"
-        index.exposed = True
-
-        def default(self, *args):
-            return "default %s" % (args,)
-        default.exposed = True
-
-        def handler(self):
-            return "handler"
-        handler.exposed = True
-
-        def _cp_dispatch(self, vpath):
-            return subnodes.get(vpath[0])
-
-    #--------------------------------------------------------------------------
-    # DynamicNodeAndMethodDispatcher example.
-    # This example exposes a fairly naive HTTP api
-    class User(object):
-        def __init__(self, id, name):
-            self.id = id
-            self.name = name
-
-        def __unicode__(self):
-            return unicode(self.name)
-
-    user_lookup = {
-        1: User(1, 'foo'),
-        2: User(2, 'bar'),
-    }
-
-    def make_user(name, id=None):
-        if not id:
-            id = max(*user_lookup.keys()) + 1
-        user_lookup[id] = User(id, name)
-        return id
-
-    class UserContainerNode(object):
-        exposed = True
-
-        def POST(self, name):
-            """
-            Allow the creation of a new Object
-            """
-            return "POST %d" % make_user(name)
-
-        def GET(self):
-            keys = user_lookup.keys()
-            keys.sort()
-            return unicode(keys)
-
-        def dynamic_dispatch(self, vpath):
-            try:
-                id = int(vpath[0])
-            except ValueError:
-                return None
-            return UserInstanceNode(id)
-
-    class UserInstanceNode(object):
-        exposed = True
-        def __init__(self, id):
-            self.id = id
-            self.user = user_lookup.get(id, None)
-
-            # For all but PUT methods there MUST be a valid user identified
-            # by self.id
-            if not self.user and cherrypy.request.method != 'PUT':
-                raise cherrypy.HTTPError(404)
-
-        def GET(self, *args, **kwargs):
-            """
-            Return the appropriate representation of the instance.
-            """
-            return unicode(self.user)
-
-        def POST(self, name):
-            """
-            Update the fields of the user instance.
-            """
-            self.user.name = name
-            return "POST %d" % self.user.id
-
-        def PUT(self, name):
-            """
-            Create a new user with the specified id, or edit it if it already exists
-            """
-            if self.user:
-                # Edit the current user
-                self.user.name = name
-                return "PUT %d" % self.user.id
-            else:
-                # Make a new user with said attributes.
-                return "PUT %d" % make_user(name, self.id)
-
-        def DELETE(self):
-            """
-            Delete the user specified at the id.
-            """
-            id = self.user.id
-            del user_lookup[self.user.id]
-            del self.user
-            return "DELETE %d" % id
-
-
-    Root.users = UserContainerNode()
-
-    md = cherrypy.dispatch.MethodDispatcher('dynamic_dispatch')
-    for url in script_names:
-        conf = {'/': {
-                    'user': (url or "/").split("/")[-2],
-                },
-                '/users': {
-                    'request.dispatch': md
-                },
-            }
-        cherrypy.tree.mount(Root(), url, conf)
-
-
-from cherrypy.test import helper
-
-class DynamicObjectMappingTest(helper.CPWebCase):
-
-    def testObjectMapping(self):
-        for url in script_names:
-            prefix = self.script_name = url
-
-            self.getPage('/')
-            self.assertBody('index')
-
-            self.getPage('/handler')
-            self.assertBody('handler')
-
-            # Dynamic dispatch will succeed here for the subnodes
-            # so the subroot gets called
-            self.getPage('/1/')
-            self.assertBody('SubRoot index')
-
-            self.getPage('/2/')
-            self.assertBody('SubRoot index')
-
-            self.getPage('/1/handler')
-            self.assertBody('SubRoot handler')
-
-            self.getPage('/2/handler')
-            self.assertBody('SubRoot handler')
-
-            # Dynamic dispatch will fail here for the subnodes
-            # so the default gets called
-            self.getPage('/asdf/')
-            self.assertBody("default ('asdf',)")
-
-            self.getPage('/asdf/asdf')
-            self.assertBody("default ('asdf', 'asdf')")
-
-            self.getPage('/asdf/handler')
-            self.assertBody("default ('asdf', 'handler')")
-
-            # Dynamic dispatch will succeed here for the subsubnodes
-            # so the subsubroot gets called
-            self.getPage('/1/1/')
-            self.assertBody('SubSubRoot index')
-
-            self.getPage('/2/2/')
-            self.assertBody('SubSubRoot index')
-
-            self.getPage('/1/1/handler')
-            self.assertBody('SubSubRoot handler')
-
-            self.getPage('/2/2/handler')
-            self.assertBody('SubSubRoot handler')
-
-            self.getPage('/2/2/dispatch')
-            self.assertBody('SubSubRoot dispatch')
-
-            # The exposed dispatch will not be called as a dispatch
-            # method.
-            self.getPage('/2/2/foo/foo')
-            self.assertBody("SubSubRoot default")
-
-            # Dynamic dispatch will fail here for the subsubnodes
-            # so the SubRoot gets called
-            self.getPage('/1/asdf/')
-            self.assertBody("SubRoot ('asdf',)")
-
-            self.getPage('/1/asdf/asdf')
-            self.assertBody("SubRoot ('asdf', 'asdf')")
-
-            self.getPage('/1/asdf/handler')
-            self.assertBody("SubRoot ('asdf', 'handler')")
-
-    def testMethodDispatch(self):
-        # GET acts like a container
-        self.getPage("/users")
-        self.assertBody("[1, 2]")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        # POST to the container URI allows creation
-        self.getPage("/users", method="POST", body="name=baz")
-        self.assertBody("POST 3")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        # POST to a specific instanct URI results in a 404
-        # as the resource does not exit.
-        self.getPage("/users/5", method="POST", body="name=baz")
-        self.assertStatus(404)
-
-        # PUT to a specific instanct URI results in creation
-        self.getPage("/users/5", method="PUT", body="name=boris")
-        self.assertBody("PUT 5")
-        self.assertHeader('Allow', 'DELETE, GET, HEAD, POST, PUT')
-
-        # GET acts like a container
-        self.getPage("/users")
-        self.assertBody("[1, 2, 3, 5]")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-        test_cases = (
-            (1, 'foo', 'fooupdated', 'DELETE, GET, HEAD, POST, PUT'),
-            (2, 'bar', 'barupdated', 'DELETE, GET, HEAD, POST, PUT'),
-            (3, 'baz', 'bazupdated', 'DELETE, GET, HEAD, POST, PUT'),
-            (5, 'boris', 'borisupdated', 'DELETE, GET, HEAD, POST, PUT'),
-        )
-        for id, name, updatedname, headers in test_cases:
-            self.getPage("/users/%d" % id)
-            self.assertBody(name)
-            self.assertHeader('Allow', headers)
-
-            # Make sure POSTs update already existings resources
-            self.getPage("/users/%d" % id, method='POST', body="name=%s" % updatedname)
-            self.assertBody("POST %d" % id)
-            self.assertHeader('Allow', headers)
-
-            # Make sure PUTs Update already existing resources.
-            self.getPage("/users/%d" % id, method='PUT', body="name=%s" % updatedname)
-            self.assertBody("PUT %d" % id)
-            self.assertHeader('Allow', headers)
-
-            # Make sure DELETES Remove already existing resources.
-            self.getPage("/users/%d" % id, method='DELETE')
-            self.assertBody("DELETE %d" % id)
-            self.assertHeader('Allow', headers)
-
-
-        # GET acts like a container
-        self.getPage("/users")
-        self.assertBody("[]")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_encoding.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,345 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import gzip
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-from httplib import IncompleteRead
-import sys
-
-import cherrypy
-
-europoundUnicode = u'\x80\xa3'
-sing = u"\u6bdb\u6cfd\u4e1c: Sing, Little Birdie?"
-sing8 = sing.encode('utf-8')
-sing16 = sing.encode('utf-16')
-
-
-def setup_server():
-    class Root:
-        def index(self, param):
-            assert param == europoundUnicode, "%r != %r" % (param, europoundUnicode)
-            yield europoundUnicode
-        index.exposed = True
-        
-        def mao_zedong(self):
-            return sing
-        mao_zedong.exposed = True
-        
-        def utf8(self):
-            return sing8
-        utf8.exposed = True
-        utf8._cp_config = {'tools.encode.encoding': 'utf-8'}
-        
-        def reqparams(self, *args, **kwargs):
-            return ', '.join([": ".join((k, v)).encode('utf8')
-                              for k, v in cherrypy.request.params.items()])
-        reqparams.exposed = True
-    
-    class GZIP:
-        def index(self):
-            yield "Hello, world"
-        index.exposed = True
-        
-        def noshow(self):
-            # Test for ticket #147, where yield showed no exceptions (content-
-            # encoding was still gzip even though traceback wasn't zipped).
-            raise IndexError()
-            yield "Here be dragons"
-        noshow.exposed = True
-        # Turn encoding off so the gzip tool is the one doing the collapse.
-        noshow._cp_config = {'tools.encode.on': False}
-        
-        def noshow_stream(self):
-            # Test for ticket #147, where yield showed no exceptions (content-
-            # encoding was still gzip even though traceback wasn't zipped).
-            raise IndexError()
-            yield "Here be dragons"
-        noshow_stream.exposed = True
-        noshow_stream._cp_config = {'response.stream': True}
-    
-    class Decode:
-        def extra_charset(self, *args, **kwargs):
-            return ', '.join([": ".join((k, v)).encode('utf8')
-                              for k, v in cherrypy.request.params.items()])
-        extra_charset.exposed = True
-        extra_charset._cp_config = {
-            'tools.decode.on': True,
-            'tools.decode.default_encoding': [u'utf-16'],
-            }
-        
-        def force_charset(self, *args, **kwargs):
-            return ', '.join([": ".join((k, v)).encode('utf8')
-                              for k, v in cherrypy.request.params.items()])
-        force_charset.exposed = True
-        force_charset._cp_config = {
-            'tools.decode.on': True,
-            'tools.decode.encoding': u'utf-16',
-            }
-    
-    root = Root()
-    root.gzip = GZIP()
-    root.decode = Decode()
-    cherrypy.tree.mount(root, config={'/gzip': {'tools.gzip.on': True}})
-
-
-
-from cherrypy.test import helper
-
-
-class EncodingTests(helper.CPWebCase):
-    
-    def test_query_string_decoding(self):
-        europoundUtf8 = europoundUnicode.encode('utf-8')
-        self.getPage('/?param=' + europoundUtf8)
-        self.assertBody(europoundUtf8)
-        
-        # Encoded utf8 query strings MUST be parsed correctly.
-        # Here, q is the POUND SIGN U+00A3 encoded in utf8 and then %HEX
-        self.getPage("/reqparams?q=%C2%A3")
-        # The return value will be encoded as utf8.
-        self.assertBody("q: \xc2\xa3")
-        
-        # Query strings that are incorrectly encoded MUST raise 404.
-        # Here, q is the POUND SIGN U+00A3 encoded in latin1 and then %HEX
-        self.getPage("/reqparams?q=%A3")
-        self.assertStatus(404)
-        self.assertErrorPage(404, 
-            "The given query string could not be processed. Query "
-            "strings for this resource must be encoded with 'utf8'.")
-    
-    def test_urlencoded_decoding(self):
-        # Test the decoding of an application/x-www-form-urlencoded entity.
-        europoundUtf8 = europoundUnicode.encode('utf-8')
-        body="param=" + europoundUtf8
-        self.getPage('/', method='POST',
-                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertBody(europoundUtf8)
-        
-        # Encoded utf8 entities MUST be parsed and decoded correctly.
-        # Here, q is the POUND SIGN U+00A3 encoded in utf8
-        body = "q=\xc2\xa3"
-        self.getPage('/reqparams', method='POST',
-                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertBody("q: \xc2\xa3")
-        
-        # ...and in utf16, which is not in the default attempt_charsets list:
-        body = "\xff\xfeq\x00=\xff\xfe\xa3\x00"
-        self.getPage('/reqparams', method='POST',
-                     headers=[("Content-Type", "application/x-www-form-urlencoded;charset=utf-16"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertBody("q: \xc2\xa3")
-        
-        # Entities that are incorrectly encoded MUST raise 400.
-        # Here, q is the POUND SIGN U+00A3 encoded in utf16, but
-        # the Content-Type incorrectly labels it utf-8.
-        body = "\xff\xfeq\x00=\xff\xfe\xa3\x00"
-        self.getPage('/reqparams', method='POST',
-                     headers=[("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertStatus(400)
-        self.assertErrorPage(400, 
-            "The request entity could not be decoded. The following charsets "
-            "were attempted: [u'utf-8']")
-    
-    def test_decode_tool(self):
-        # An extra charset should be tried first, and succeed if it matches.
-        # Here, we add utf-16 as a charset and pass a utf-16 body.
-        body = "\xff\xfeq\x00=\xff\xfe\xa3\x00"
-        self.getPage('/decode/extra_charset', method='POST',
-                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertBody("q: \xc2\xa3")
-        
-        # An extra charset should be tried first, and continue to other default
-        # charsets if it doesn't match.
-        # Here, we add utf-16 as a charset but still pass a utf-8 body.
-        body = "q=\xc2\xa3"
-        self.getPage('/decode/extra_charset', method='POST',
-                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertBody("q: \xc2\xa3")
-        
-        # An extra charset should error if force is True and it doesn't match.
-        # Here, we force utf-16 as a charset but still pass a utf-8 body.
-        body = "q=\xc2\xa3"
-        self.getPage('/decode/force_charset', method='POST',
-                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertErrorPage(400, 
-            "The request entity could not be decoded. The following charsets "
-            "were attempted: [u'utf-16']")
-    
-    def test_multipart_decoding(self):
-        # Test the decoding of a multipart entity when the charset (utf16) is
-        # explicitly given.
-        body='\r\n'.join(['--X',
-                          'Content-Type: text/plain;charset=utf-16',
-                          'Content-Disposition: form-data; name="text"',
-                          '',
-                          '\xff\xfea\x00b\x00\x1c c\x00',
-                          '--X',
-                          'Content-Type: text/plain;charset=utf-16',
-                          'Content-Disposition: form-data; name="submit"',
-                          '',
-                          '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
-                          '--X--'])
-        self.getPage('/reqparams', method='POST',
-                     headers=[("Content-Type", "multipart/form-data;boundary=X"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertBody("text: ab\xe2\x80\x9cc, submit: Create")
-    
-    def test_multipart_decoding_no_charset(self):
-        # Test the decoding of a multipart entity when the charset (utf8) is
-        # NOT explicitly given, but is in the list of charsets to attempt.
-        body='\r\n'.join(['--X',
-                          'Content-Disposition: form-data; name="text"',
-                          '',
-                          '\xe2\x80\x9c',
-                          '--X',
-                          'Content-Disposition: form-data; name="submit"',
-                          '',
-                          'Create',
-                          '--X--'])
-        self.getPage('/reqparams', method='POST',
-                     headers=[("Content-Type", "multipart/form-data;boundary=X"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertBody("text: \xe2\x80\x9c, submit: Create")
-    
-    def test_multipart_decoding_no_successful_charset(self):
-        # Test the decoding of a multipart entity when the charset (utf16) is
-        # NOT explicitly given, and is NOT in the list of charsets to attempt.
-        body='\r\n'.join(['--X',
-                          'Content-Disposition: form-data; name="text"',
-                          '',
-                          '\xff\xfea\x00b\x00\x1c c\x00',
-                          '--X',
-                          'Content-Disposition: form-data; name="submit"',
-                          '',
-                          '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
-                          '--X--'])
-        self.getPage('/reqparams', method='POST',
-                     headers=[("Content-Type", "multipart/form-data;boundary=X"),
-                              ("Content-Length", str(len(body))),
-                              ],
-                     body=body),
-        self.assertStatus(400)
-        self.assertErrorPage(400, 
-            "The request entity could not be decoded. The following charsets "
-            "were attempted: [u'us-ascii', u'utf-8']")
-    
-    def testEncoding(self):
-        # Default encoding should be utf-8
-        self.getPage('/mao_zedong')
-        self.assertBody(sing8)
-        
-        # Ask for utf-16.
-        self.getPage('/mao_zedong', [('Accept-Charset', 'utf-16')])
-        self.assertHeader('Content-Type', 'text/html;charset=utf-16')
-        self.assertBody(sing16)
-        
-        # Ask for multiple encodings. ISO-8859-1 should fail, and utf-16
-        # should be produced.
-        self.getPage('/mao_zedong', [('Accept-Charset',
-                                      'iso-8859-1;q=1, utf-16;q=0.5')])
-        self.assertBody(sing16)
-        
-        # The "*" value should default to our default_encoding, utf-8
-        self.getPage('/mao_zedong', [('Accept-Charset', '*;q=1, utf-7;q=.2')])
-        self.assertBody(sing8)
-        
-        # Only allow iso-8859-1, which should fail and raise 406.
-        self.getPage('/mao_zedong', [('Accept-Charset', 'iso-8859-1, *;q=0')])
-        self.assertStatus("406 Not Acceptable")
-        self.assertInBody("Your client sent this Accept-Charset header: "
-                          "iso-8859-1, *;q=0. We tried these charsets: "
-                          "iso-8859-1.")
-        
-        # Ask for x-mac-ce, which should be unknown. See ticket #569.
-        self.getPage('/mao_zedong', [('Accept-Charset',
-                                      'us-ascii, ISO-8859-1, x-mac-ce')])
-        self.assertStatus("406 Not Acceptable")
-        self.assertInBody("Your client sent this Accept-Charset header: "
-                          "us-ascii, ISO-8859-1, x-mac-ce. We tried these "
-                          "charsets: ISO-8859-1, us-ascii, x-mac-ce.")
-        
-        # Test the 'encoding' arg to encode.
-        self.getPage('/utf8')
-        self.assertBody(sing8)
-        self.getPage('/utf8', [('Accept-Charset', 'us-ascii, ISO-8859-1')])
-        self.assertStatus("406 Not Acceptable")
-    
-    def testGzip(self):
-        zbuf = StringIO()
-        zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=9)
-        zfile.write("Hello, world")
-        zfile.close()
-        
-        self.getPage('/gzip/', headers=[("Accept-Encoding", "gzip")])
-        self.assertInBody(zbuf.getvalue()[:3])
-        self.assertHeader("Vary", "Accept-Encoding")
-        self.assertHeader("Content-Encoding", "gzip")
-        
-        # Test when gzip is denied.
-        self.getPage('/gzip/', headers=[("Accept-Encoding", "identity")])
-        self.assertHeader("Vary", "Accept-Encoding")
-        self.assertNoHeader("Content-Encoding")
-        self.assertBody("Hello, world")
-        
-        self.getPage('/gzip/', headers=[("Accept-Encoding", "gzip;q=0")])
-        self.assertHeader("Vary", "Accept-Encoding")
-        self.assertNoHeader("Content-Encoding")
-        self.assertBody("Hello, world")
-        
-        self.getPage('/gzip/', headers=[("Accept-Encoding", "*;q=0")])
-        self.assertStatus(406)
-        self.assertNoHeader("Content-Encoding")
-        self.assertErrorPage(406, "identity, gzip")
-        
-        # Test for ticket #147
-        self.getPage('/gzip/noshow', headers=[("Accept-Encoding", "gzip")])
-        self.assertNoHeader('Content-Encoding')
-        self.assertStatus(500)
-        self.assertErrorPage(500, pattern="IndexError\n")
-        
-        # In this case, there's nothing we can do to deliver a
-        # readable page, since 1) the gzip header is already set,
-        # and 2) we may have already written some of the body.
-        # The fix is to never stream yields when using gzip.
-        if (cherrypy.server.protocol_version == "HTTP/1.0" or
-            getattr(cherrypy.server, "using_apache", False)):
-            self.getPage('/gzip/noshow_stream',
-                         headers=[("Accept-Encoding", "gzip")])
-            self.assertHeader('Content-Encoding', 'gzip')
-            self.assertInBody('\x1f\x8b\x08\x00')
-        else:
-            # The wsgiserver will simply stop sending data, and the HTTP client
-            # will error due to an incomplete chunk-encoded stream.
-            self.assertRaises((ValueError, IncompleteRead), self.getPage,
-                              '/gzip/noshow_stream',
-                              headers=[("Accept-Encoding", "gzip")])
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_etags.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-
-
-def setup_server():
-    class Root:
-        def resource(self):
-            return "Oh wah ta goo Siam."
-        resource.exposed = True
-        
-        def fail(self, code):
-            code = int(code)
-            if 300 <= code <= 399:
-                raise cherrypy.HTTPRedirect([], code)
-            else:
-                raise cherrypy.HTTPError(code)
-        fail.exposed = True
-        
-        def unicoded(self):
-            return u'I am a \u1ee4nicode string.'
-        unicoded.exposed = True
-        unicoded._cp_config = {'tools.encode.on': True}
-    
-    conf = {'/': {'tools.etags.on': True,
-                  'tools.etags.autotags': True,
-                  }}
-    cherrypy.tree.mount(Root(), config=conf)
-
-from cherrypy.test import helper
-
-class ETagTest(helper.CPWebCase):
-    
-    def test_etags(self):
-        self.getPage("/resource")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('Oh wah ta goo Siam.')
-        etag = self.assertHeader('ETag')
-        
-        # Test If-Match (both valid and invalid)
-        self.getPage("/resource", headers=[('If-Match', etag)])
-        self.assertStatus("200 OK")
-        self.getPage("/resource", headers=[('If-Match', "*")])
-        self.assertStatus("200 OK")
-        self.getPage("/resource", headers=[('If-Match', "*")], method="POST")
-        self.assertStatus("200 OK")
-        self.getPage("/resource", headers=[('If-Match', "a bogus tag")])
-        self.assertStatus("412 Precondition Failed")
-        
-        # Test If-None-Match (both valid and invalid)
-        self.getPage("/resource", headers=[('If-None-Match', etag)])
-        self.assertStatus(304)
-        self.getPage("/resource", method='POST', headers=[('If-None-Match', etag)])
-        self.assertStatus("412 Precondition Failed")
-        self.getPage("/resource", headers=[('If-None-Match', "*")])
-        self.assertStatus(304)
-        self.getPage("/resource", headers=[('If-None-Match', "a bogus tag")])
-        self.assertStatus("200 OK")
-    
-    def test_errors(self):
-        self.getPage("/resource")
-        self.assertStatus(200)
-        etag = self.assertHeader('ETag')
-        
-        # Test raising errors in page handler
-        self.getPage("/fail/412", headers=[('If-Match', etag)])
-        self.assertStatus(412)
-        self.getPage("/fail/304", headers=[('If-Match', etag)])
-        self.assertStatus(304)
-        self.getPage("/fail/412", headers=[('If-None-Match', "*")])
-        self.assertStatus(412)
-        self.getPage("/fail/304", headers=[('If-None-Match', "*")])
-        self.assertStatus(304)
-    
-    def test_unicode_body(self):
-        self.getPage("/unicoded")
-        self.assertStatus(200)
-        etag1 = self.assertHeader('ETag')
-        self.getPage("/unicoded", headers=[('If-Match', etag1)])
-        self.assertStatus(200)
-        self.assertHeader('ETag', etag1)
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_http.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,182 +0,0 @@
-"""Tests for managing HTTP issues (malformed requests, etc)."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-from httplib import HTTPConnection, HTTPSConnection
-import cherrypy
-import mimetypes
-
-
-def encode_multipart_formdata(files):
-    """Return (content_type, body) ready for httplib.HTTP instance.
-    
-    files: a sequence of (name, filename, value) tuples for multipart uploads.
-    """
-    BOUNDARY = '________ThIs_Is_tHe_bouNdaRY_$'
-    L = []
-    for key, filename, value in files:
-        L.append('--' + BOUNDARY)
-        L.append('Content-Disposition: form-data; name="%s"; filename="%s"' %
-                 (key, filename))
-        ct = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-        L.append('Content-Type: %s' % ct)
-        L.append('')
-        L.append(value)
-    L.append('--' + BOUNDARY + '--')
-    L.append('')
-    body = '\r\n'.join(L)
-    content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
-    return content_type, body
-
-
-def setup_server():
-    
-    class Root:
-        def index(self, *args, **kwargs):
-            return "Hello world!"
-        index.exposed = True
-        
-        def no_body(self, *args, **kwargs):
-            return "Hello world!"
-        no_body.exposed = True
-        no_body._cp_config = {'request.process_request_body': False}
-        
-        def post_multipart(self, file):
-            """Return a summary ("a * 65536\nb * 65536") of the uploaded file."""
-            contents = file.file.read()
-            summary = []
-            curchar = ""
-            count = 0
-            for c in contents:
-                if c == curchar:
-                    count += 1
-                else:
-                    if count:
-                        summary.append("%s * %d" % (curchar, count))
-                    count = 1
-                    curchar = c
-            if count:
-                summary.append("%s * %d" % (curchar, count))
-            return ", ".join(summary)
-        post_multipart.exposed = True
-    
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({'server.max_request_body_size': 30000000})
-
-
-from cherrypy.test import helper
-
-class HTTPTests(helper.CPWebCase):
-    
-    def test_no_content_length(self):
-        # "The presence of a message-body in a request is signaled by the
-        # inclusion of a Content-Length or Transfer-Encoding header field in
-        # the request's message-headers."
-        # 
-        # Send a message with neither header and no body. Even though
-        # the request is of method POST, this should be OK because we set
-        # request.process_request_body to False for our handler.
-        if self.scheme == "https":
-            c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-        else:
-            c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        c.request("POST", "/no_body")
-        response = c.getresponse()
-        self.body = response.fp.read()
-        self.status = str(response.status)
-        self.assertStatus(200)
-        self.assertBody('Hello world!')
-        
-        # Now send a message that has no Content-Length, but does send a body.
-        # Verify that CP times out the socket and responds
-        # with 411 Length Required.
-        if self.scheme == "https":
-            c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-        else:
-            c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        c.request("POST", "/")
-        response = c.getresponse()
-        self.body = response.fp.read()
-        self.status = str(response.status)
-        self.assertStatus(411)
-    
-    def test_post_multipart(self):
-        alphabet = "abcdefghijklmnopqrstuvwxyz"
-        # generate file contents for a large post
-        contents = "".join([c * 65536 for c in alphabet])
-        
-        # encode as multipart form data
-        files=[('file', 'file.txt', contents)]
-        content_type, body = encode_multipart_formdata(files)
-        
-        # post file
-        if self.scheme == 'https':
-            c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-        else:
-            c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        c.putrequest('POST', '/post_multipart')
-        c.putheader('Content-Type', content_type)
-        c.putheader('Content-Length', str(len(body)))
-        c.endheaders()
-        c.send(body)
-        
-        response = c.getresponse()
-        self.body = response.fp.read()
-        self.status = str(response.status)
-        self.assertStatus(200)
-        self.assertBody(", ".join(["%s * 65536" % c for c in alphabet]))
-
-    def test_malformed_request_line(self):
-        if getattr(cherrypy.server, "using_apache", False):
-            return self.skip("skipped due to known Apache differences...")
-        
-        # Test missing version in Request-Line
-        if self.scheme == 'https':
-            c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-        else:
-            c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        c._output('GET /')
-        c._send_output()
-        response = c.response_class(c.sock, strict=c.strict, method='GET')
-        response.begin()
-        self.assertEqual(response.status, 400)
-        self.assertEqual(response.fp.read(22), "Malformed Request-Line")
-        c.close()
-    
-    def test_malformed_header(self):
-        if self.scheme == 'https':
-            c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-        else:
-            c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        c.putrequest('GET', '/')
-        c.putheader('Content-Type', 'text/plain')
-        # See http://www.cherrypy.org/ticket/941 
-        c._output('Re, 1.2.3.4#015#012')
-        c.endheaders()
-        
-        response = c.getresponse()
-        self.status = str(response.status)
-        self.assertStatus(400)
-        self.body = response.fp.read()
-        self.assertBody("Illegal header line.")
-    
-    def test_http_over_https(self):
-        if self.scheme != 'https':
-            return self.skip("skipped (not running HTTPS)... ")
-        
-        # Try connecting without SSL.
-        conn = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-        conn.putrequest("GET", "/", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 400)
-        self.body = response.read()
-        self.assertBody("The client sent a plain HTTP request, but this "
-                        "server only speaks HTTPS on this port.")
-
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_httpauth.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-try:
-    # Python 2.5+
-    from hashlib import md5, sha1 as sha
-except ImportError:
-    from md5 import new as md5
-    from sha import new as sha
-
-import cherrypy
-from cherrypy.lib import httpauth
-
-def setup_server():
-    class Root:
-        def index(self):
-            return "This is public."
-        index.exposed = True
-
-    class DigestProtected:
-        def index(self):
-            return "Hello %s, you've been authorized." % cherrypy.request.login
-        index.exposed = True
-
-    class BasicProtected:
-        def index(self):
-            return "Hello %s, you've been authorized." % cherrypy.request.login
-        index.exposed = True
-
-    class BasicProtected2:
-        def index(self):
-            return "Hello %s, you've been authorized." % cherrypy.request.login
-        index.exposed = True
-
-    def fetch_users():
-        return {'test': 'test'}
-
-    def sha_password_encrypter(password):
-        return sha(password).hexdigest()
-    
-    def fetch_password(username):
-        return sha('test').hexdigest()
-
-    conf = {'/digest': {'tools.digest_auth.on': True,
-                        'tools.digest_auth.realm': 'localhost',
-                        'tools.digest_auth.users': fetch_users},
-            '/basic': {'tools.basic_auth.on': True,
-                       'tools.basic_auth.realm': 'localhost',
-                       'tools.basic_auth.users': {'test': md5('test').hexdigest()}},
-            '/basic2': {'tools.basic_auth.on': True,
-                        'tools.basic_auth.realm': 'localhost',
-                        'tools.basic_auth.users': fetch_password,
-                        'tools.basic_auth.encrypt': sha_password_encrypter}}
-            
-    root = Root()
-    root.digest = DigestProtected()
-    root.basic = BasicProtected()
-    root.basic2 = BasicProtected2()
-    cherrypy.tree.mount(root, config=conf)
-
-from cherrypy.test import helper
-
-class HTTPAuthTest(helper.CPWebCase):
-
-    def testPublic(self):
-        self.getPage("/")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('This is public.')
-
-    def testBasic(self):
-        self.getPage("/basic/")
-        self.assertStatus(401)
-        self.assertHeader('WWW-Authenticate', 'Basic realm="localhost"')
-
-        self.getPage('/basic/', [('Authorization', 'Basic dGVzdDp0ZX60')])
-        self.assertStatus(401)
-        
-        self.getPage('/basic/', [('Authorization', 'Basic dGVzdDp0ZXN0')])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello test, you've been authorized.")
-
-    def testBasic2(self):
-        self.getPage("/basic2/")
-        self.assertStatus(401)
-        self.assertHeader('WWW-Authenticate', 'Basic realm="localhost"')
-
-        self.getPage('/basic2/', [('Authorization', 'Basic dGVzdDp0ZX60')])
-        self.assertStatus(401)
-        
-        self.getPage('/basic2/', [('Authorization', 'Basic dGVzdDp0ZXN0')])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello test, you've been authorized.")
-
-    def testDigest(self):
-        self.getPage("/digest/")
-        self.assertStatus(401)
-        
-        value = None
-        for k, v in self.headers:
-            if k.lower() == "www-authenticate":
-                if v.startswith("Digest"):
-                    value = v
-                    break
-
-        if value is None:
-            self._handlewebError("Digest authentification scheme was not found")
-
-        value = value[7:]
-        items = value.split(', ')
-        tokens = {}
-        for item in items:
-            key, value = item.split('=')
-            tokens[key.lower()] = value
-            
-        missing_msg = "%s is missing"
-        bad_value_msg = "'%s' was expecting '%s' but found '%s'"
-        nonce = None
-        if 'realm' not in tokens:
-            self._handlewebError(missing_msg % 'realm')
-        elif tokens['realm'] != '"localhost"':
-            self._handlewebError(bad_value_msg % ('realm', '"localhost"', tokens['realm']))
-        if 'nonce' not in tokens:
-            self._handlewebError(missing_msg % 'nonce')
-        else:
-            nonce = tokens['nonce'].strip('"')
-        if 'algorithm' not in tokens:
-            self._handlewebError(missing_msg % 'algorithm')
-        elif tokens['algorithm'] != '"MD5"':
-            self._handlewebError(bad_value_msg % ('algorithm', '"MD5"', tokens['algorithm']))
-        if 'qop' not in tokens:
-            self._handlewebError(missing_msg % 'qop')
-        elif tokens['qop'] != '"auth"':
-            self._handlewebError(bad_value_msg % ('qop', '"auth"', tokens['qop']))
-
-        # Test a wrong 'realm' value
-        base_auth = 'Digest username="test", realm="wrong realm", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
-        auth = base_auth % (nonce, '', '00000001')
-        params = httpauth.parseAuthorization(auth)
-        response = httpauth._computeDigestResponse(params, 'test')
-        
-        auth = base_auth % (nonce, response, '00000001')
-        self.getPage('/digest/', [('Authorization', auth)])
-        self.assertStatus(401)
-
-        # Test that must pass
-        base_auth = 'Digest username="test", realm="localhost", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
-        auth = base_auth % (nonce, '', '00000001')
-        params = httpauth.parseAuthorization(auth)
-        response = httpauth._computeDigestResponse(params, 'test')
-        
-        auth = base_auth % (nonce, response, '00000001')
-        self.getPage('/digest/', [('Authorization', auth)])
-        self.assertStatus('200 OK')
-        self.assertBody("Hello test, you've been authorized.")
-            
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_httplib.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-"""Tests for cherrypy/lib/httputil.py."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import unittest
-from cherrypy.lib import httputil
-
-
-class UtilityTests(unittest.TestCase):
-    
-    def test_urljoin(self):
-        # Test all slash+atom combinations for SCRIPT_NAME and PATH_INFO
-        self.assertEqual(httputil.urljoin("/sn/", "/pi/"), "/sn/pi/")
-        self.assertEqual(httputil.urljoin("/sn/", "/pi"), "/sn/pi")
-        self.assertEqual(httputil.urljoin("/sn/", "/"), "/sn/")
-        self.assertEqual(httputil.urljoin("/sn/", ""), "/sn/")
-        self.assertEqual(httputil.urljoin("/sn", "/pi/"), "/sn/pi/")
-        self.assertEqual(httputil.urljoin("/sn", "/pi"), "/sn/pi")
-        self.assertEqual(httputil.urljoin("/sn", "/"), "/sn/")
-        self.assertEqual(httputil.urljoin("/sn", ""), "/sn")
-        self.assertEqual(httputil.urljoin("/", "/pi/"), "/pi/")
-        self.assertEqual(httputil.urljoin("/", "/pi"), "/pi")
-        self.assertEqual(httputil.urljoin("/", "/"), "/")
-        self.assertEqual(httputil.urljoin("/", ""), "/")
-        self.assertEqual(httputil.urljoin("", "/pi/"), "/pi/")
-        self.assertEqual(httputil.urljoin("", "/pi"), "/pi")
-        self.assertEqual(httputil.urljoin("", "/"), "/")
-        self.assertEqual(httputil.urljoin("", ""), "/")
-
-if __name__ == '__main__':
-    unittest.main()
--- a/bundled/cherrypy/cherrypy/test/test_json.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-from cherrypy.test import test, helper
-test.prefer_parent_path()
-
-import cherrypy
-
-from cherrypy.lib.jsontools import json
-if json is None:
-    print "skipped (simplejson not found) "
-else:
-    def setup_server():
-        class Root(object):
-            def plain(self):
-                return 'hello'
-            plain.exposed = True
-
-            def json_string(self):
-                return 'hello'
-            json_string.exposed = True
-            json_string._cp_config = {'tools.json_out.on': True}
-
-            def json_list(self):
-                return ['a', 'b', 42]
-            json_list.exposed = True
-            json_list._cp_config = {'tools.json_out.on': True}
-
-            def json_dict(self):
-                return {'answer': 42}
-            json_dict.exposed = True
-            json_dict._cp_config = {'tools.json_out.on': True}
-
-            def json_post(self):
-                if cherrypy.request.json == [13, 'c']:
-                    return 'ok'
-                else:
-                    return 'nok'
-            json_post.exposed = True
-            json_post._cp_config = {'tools.json_in.on': True}
-
-        root = Root()
-        cherrypy.tree.mount(root)
-
-    class JsonTest(helper.CPWebCase):
-        def test_json_output(self):
-            self.getPage("/plain")
-            self.assertBody("hello")
-
-            self.getPage("/json_string")
-            self.assertBody('"hello"')
-
-            self.getPage("/json_list")
-            self.assertBody('["a", "b", 42]')
-
-            self.getPage("/json_dict")
-            self.assertBody('{"answer": 42}')
-
-        def test_json_input(self):
-            body = '[13, "c"]'
-            headers = [('Content-Type', 'application/json'),
-                       ('Content-Length', str(len(body)))]
-            self.getPage("/json_post", method="POST", headers=headers, body=body)
-            self.assertBody('ok')
-            
-            body = '[13, "c"]'
-            headers = [('Content-Type', 'text/plain'),
-                       ('Content-Length', str(len(body)))]
-            self.getPage("/json_post", method="POST", headers=headers, body=body)
-            self.assertStatus(415, 'Expected an application/json content type')
-            
-            body = '[13, -]'
-            headers = [('Content-Type', 'application/json'),
-                       ('Content-Length', str(len(body)))]
-            self.getPage("/json_post", method="POST", headers=headers, body=body)
-            self.assertStatus(400, 'Invalid JSON document')
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_logging.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-"""Basic tests for the CherryPy core: request handling."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os
-localDir = os.path.dirname(__file__)
-
-import cherrypy
-
-access_log = os.path.join(localDir, "access.log")
-error_log = os.path.join(localDir, "error.log")
-
-# Some unicode strings.
-tartaros = u'\u03a4\u1f71\u03c1\u03c4\u03b1\u03c1\u03bf\u03c2'
-erebos = u'\u0388\u03c1\u03b5\u03b2\u03bf\u03c2.com'
-
-
-def setup_server():
-    class Root:
-        
-        def index(self):
-            return "hello"
-        index.exposed = True
-        
-        def uni_code(self):
-            cherrypy.request.login = tartaros
-            cherrypy.request.remote.name = erebos
-        uni_code.exposed = True
-        
-        def slashes(self):
-            cherrypy.request.request_line = r'GET /slashed\path HTTP/1.1'
-        slashes.exposed = True
-        
-        def whitespace(self):
-            # User-Agent = "User-Agent" ":" 1*( product | comment )
-            # comment    = "(" *( ctext | quoted-pair | comment ) ")"
-            # ctext      = <any TEXT excluding "(" and ")">
-            # TEXT       = <any OCTET except CTLs, but including LWS>
-            # LWS        = [CRLF] 1*( SP | HT )
-            cherrypy.request.headers['User-Agent'] = 'Browzuh (1.0\r\n\t\t.3)'
-        whitespace.exposed = True
-        
-        def as_string(self):
-            return "content"
-        as_string.exposed = True
-        
-        def as_yield(self):
-            yield "content"
-        as_yield.exposed = True
-        
-        def error(self):
-            raise ValueError()
-        error.exposed = True
-        error._cp_config = {'tools.log_tracebacks.on': True}
-    
-    root = Root()
-
-
-    cherrypy.config.update({'log.error_file': error_log,
-                            'log.access_file': access_log,
-                            })
-    cherrypy.tree.mount(root)
-
-
-
-from cherrypy.test import helper, logtest
-
-class AccessLogTests(helper.CPWebCase, logtest.LogCase):
-    
-    logfile = access_log
-    
-    def testNormalReturn(self):
-        self.markLog()
-        self.getPage("/as_string",
-                     headers=[('Referer', 'http://www.cherrypy.org/'),
-                              ('User-Agent', 'Mozilla/5.0')])
-        self.assertBody('content')
-        self.assertStatus(200)
-        
-        intro = '%s - - [' % self.interface()
-        
-        self.assertLog(-1, intro)
-        
-        if [k for k, v in self.headers if k.lower() == 'content-length']:
-            self.assertLog(-1, '] "GET %s/as_string HTTP/1.1" 200 7 '
-                           '"http://www.cherrypy.org/" "Mozilla/5.0"'
-                           % self.prefix())
-        else:
-            self.assertLog(-1, '] "GET %s/as_string HTTP/1.1" 200 - '
-                           '"http://www.cherrypy.org/" "Mozilla/5.0"'
-                           % self.prefix())
-    
-    def testNormalYield(self):
-        self.markLog()
-        self.getPage("/as_yield")
-        self.assertBody('content')
-        self.assertStatus(200)
-        
-        intro = '%s - - [' % self.interface()
-        
-        self.assertLog(-1, intro)
-        if [k for k, v in self.headers if k.lower() == 'content-length']:
-            self.assertLog(-1, '] "GET %s/as_yield HTTP/1.1" 200 7 "" ""' %
-                           self.prefix())
-        else:
-            self.assertLog(-1, '] "GET %s/as_yield HTTP/1.1" 200 - "" ""'
-                           % self.prefix())
-    
-    def testEscapedOutput(self):
-        # Test unicode in access log pieces.
-        self.markLog()
-        self.getPage("/uni_code")
-        self.assertStatus(200)
-        self.assertLog(-1, repr(tartaros.encode('utf8'))[1:-1])
-        # Test the erebos value. Included inline for your enlightenment.
-        # Note the 'r' prefix--those backslashes are literals.
-        self.assertLog(-1, r'\xce\x88\xcf\x81\xce\xb5\xce\xb2\xce\xbf\xcf\x82')
-        
-        # Test backslashes in output.
-        self.markLog()
-        self.getPage("/slashes")
-        self.assertStatus(200)
-        self.assertLog(-1, r'"GET /slashed\\path HTTP/1.1"')
-        
-        # Test whitespace in output.
-        self.markLog()
-        self.getPage("/whitespace")
-        self.assertStatus(200)
-        # Again, note the 'r' prefix.
-        self.assertLog(-1, r'"Browzuh (1.0\r\n\t\t.3)"')
-
-
-class ErrorLogTests(helper.CPWebCase, logtest.LogCase):
-    
-    logfile = error_log
-    
-    def testTracebacks(self):
-        # Test that tracebacks get written to the error log.
-        self.markLog()
-        ignore = helper.webtest.ignored_exceptions
-        ignore.append(ValueError)
-        try:
-            self.getPage("/error")
-            self.assertInBody("raise ValueError()")
-            self.assertLog(0, 'HTTP Traceback (most recent call last):')
-            self.assertLog(-3, 'raise ValueError()')
-        finally:
-            ignore.pop()
-
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_mime.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-"""Tests for various MIME issues, including the safe_multipart Tool."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-
-
-def setup_server():
-    
-    class Root:
-        
-        def multipart(self, parts):
-            return repr(parts)
-        multipart.exposed = True
-        
-        def flashupload(self, Filedata, Upload, Filename):
-            return ("Upload: %r, Filename: %r, Filedata: %r" %
-                    (Upload, Filename, Filedata.file.read()))
-        flashupload.exposed = True
-    
-    cherrypy.config.update({'server.max_request_body_size': 0})
-    cherrypy.tree.mount(Root())
-
-
-#                             Client-side code                             #
-
-from cherrypy.test import helper
-
-class MultipartTest(helper.CPWebCase):
-    
-    def test_multipart(self):
-        text_part = u"This is the text version"
-        html_part = u"""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
- <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
-</head>
-<body bgcolor="#ffffff" text="#000000">
-
-This is the <strong>HTML</strong> version
-</body>
-</html>
-"""
-        body = '\r\n'.join([
-            "--123456789",
-            "Content-Type: text/plain; charset='ISO-8859-1'",
-            "Content-Transfer-Encoding: 7bit",
-            "",
-            text_part,
-            "--123456789",
-            "Content-Type: text/html; charset='ISO-8859-1'",
-            "",
-            html_part,
-            "--123456789--"])
-        headers = [
-            ('Content-Type', 'multipart/mixed; boundary=123456789'),
-            ('Content-Length', len(body)),
-            ]
-        self.getPage('/multipart', headers, "POST", body)
-        self.assertBody(repr([text_part, html_part]))
-
-
-class SafeMultipartHandlingTest(helper.CPWebCase):
-    
-    def test_Flash_Upload(self):
-        headers = [
-            ('Accept', 'text/*'),
-            ('Content-Type', 'multipart/form-data; '
-                 'boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'),
-            ('User-Agent', 'Shockwave Flash'),
-            ('Host', 'www.example.com:8080'),
-            ('Content-Length', '499'),
-            ('Connection', 'Keep-Alive'),
-            ('Cache-Control', 'no-cache'),
-            ]
-        filedata = ('<?xml version="1.0" encoding="UTF-8"?>\r\n'
-                    '<projectDescription>\r\n'
-                    '</projectDescription>\r\n')
-        body = (
-            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
-            'Content-Disposition: form-data; name="Filename"\r\n'
-            '\r\n'
-            '.project\r\n'
-            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
-            'Content-Disposition: form-data; '
-                'name="Filedata"; filename=".project"\r\n'
-            'Content-Type: application/octet-stream\r\n'
-            '\r\n'
-            + filedata + 
-            '\r\n'
-            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
-            'Content-Disposition: form-data; name="Upload"\r\n'
-            '\r\n'
-            'Submit Query\r\n'
-            # Flash apps omit the trailing \r\n on the last line:
-            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--'
-            )
-        self.getPage('/flashupload', headers, "POST", body)
-        self.assertBody("Upload: u'Submit Query', Filename: u'.project', "
-                        "Filedata: %r" % filedata)
-
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_misc_tools.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os
-localDir = os.path.dirname(__file__)
-logfile = os.path.join(localDir, "test_misc_tools.log")
-
-import cherrypy
-from cherrypy import tools
-
-
-def setup_server():
-    class Root:
-        def index(self):
-            yield "Hello, world"
-        index.exposed = True
-        h = [("Content-Language", "en-GB"), ('Content-Type', 'text/plain')]
-        tools.response_headers(headers=h)(index)
-        
-        def other(self):
-            return "salut"
-        other.exposed = True
-        other._cp_config = {
-            'tools.response_headers.on': True,
-            'tools.response_headers.headers': [("Content-Language", "fr"),
-                                               ('Content-Type', 'text/plain')],
-            'tools.log_hooks.on': True,
-            }
-    
-    
-    class Accept:
-        _cp_config = {'tools.accept.on': True}
-        
-        def index(self):
-            return '<a href="feed">Atom feed</a>'
-        index.exposed = True
-        
-        # In Python 2.4+, we could use a decorator instead:
-        # @tools.accept('application/atom+xml')
-        def feed(self):
-            return """<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns="http://www.w3.org/2005/Atom">
-    <title>Unknown Blog</title>
-</feed>"""
-        feed.exposed = True
-        feed._cp_config = {'tools.accept.media': 'application/atom+xml'}
-        
-        def select(self):
-            # We could also write this: mtype = cherrypy.lib.accept.accept(...)
-            mtype = tools.accept.callable(['text/html', 'text/plain'])
-            if mtype == 'text/html':
-                return "<h2>Page Title</h2>"
-            else:
-                return "PAGE TITLE"
-        select.exposed = True
-    
-    class Referer:
-        def accept(self):
-            return "Accepted!"
-        accept.exposed = True
-        reject = accept
-    
-    class AutoVary:
-        def index(self):
-            # Read a header directly with 'get'
-            ae = cherrypy.request.headers.get('Accept-Encoding')
-            # Read a header directly with '__getitem__'
-            cl = cherrypy.request.headers['Host']
-            # Read a header directly with '__contains__'
-            hasif = 'If-Modified-Since' in cherrypy.request.headers
-            # Read a header directly with 'has_key'
-            has = cherrypy.request.headers.has_key('Range')
-            # Call a lib function
-            mtype = tools.accept.callable(['text/html', 'text/plain'])
-            return "Hello, world!"
-        index.exposed = True
-    
-    conf = {'/referer': {'tools.referer.on': True,
-                         'tools.referer.pattern': r'http://[^/]*example\.com',
-                         },
-            '/referer/reject': {'tools.referer.accept': False,
-                                'tools.referer.accept_missing': True,
-                                },
-            '/autovary': {'tools.autovary.on': True},
-            }
-    
-    root = Root()
-    root.referer = Referer()
-    root.accept = Accept()
-    root.autovary = AutoVary()
-    cherrypy.tree.mount(root, config=conf)
-    cherrypy.config.update({'log.error_file': logfile})
-
-
-from cherrypy.test import helper
-
-class ResponseHeadersTest(helper.CPWebCase):
-
-    def testResponseHeadersDecorator(self):
-        self.getPage('/')
-        self.assertHeader("Content-Language", "en-GB")
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-
-    def testResponseHeaders(self):
-        self.getPage('/other')
-        self.assertHeader("Content-Language", "fr")
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-
-
-class RefererTest(helper.CPWebCase):
-    
-    def testReferer(self):
-        self.getPage('/referer/accept')
-        self.assertErrorPage(403, 'Forbidden Referer header.')
-        
-        self.getPage('/referer/accept',
-                     headers=[('Referer', 'http://www.example.com/')])
-        self.assertStatus(200)
-        self.assertBody('Accepted!')
-        
-        # Reject
-        self.getPage('/referer/reject')
-        self.assertStatus(200)
-        self.assertBody('Accepted!')
-        
-        self.getPage('/referer/reject',
-                     headers=[('Referer', 'http://www.example.com/')])
-        self.assertErrorPage(403, 'Forbidden Referer header.')
-
-
-class AcceptTest(helper.CPWebCase):
-    
-    def test_Accept_Tool(self):
-        # Test with no header provided
-        self.getPage('/accept/feed')
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-        
-        # Specify exact media type
-        self.getPage('/accept/feed', headers=[('Accept', 'application/atom+xml')])
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-        
-        # Specify matching media range
-        self.getPage('/accept/feed', headers=[('Accept', 'application/*')])
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-        
-        # Specify all media ranges
-        self.getPage('/accept/feed', headers=[('Accept', '*/*')])
-        self.assertStatus(200)
-        self.assertInBody('<title>Unknown Blog</title>')
-        
-        # Specify unacceptable media types
-        self.getPage('/accept/feed', headers=[('Accept', 'text/html')])
-        self.assertErrorPage(406,
-                             "Your client sent this Accept header: text/html. "
-                             "But this resource only emits these media types: "
-                             "application/atom+xml.")
-        
-        # Test resource where tool is 'on' but media is None (not set).
-        self.getPage('/accept/')
-        self.assertStatus(200)
-        self.assertBody('<a href="feed">Atom feed</a>')
-    
-    def test_accept_selection(self):
-        # Try both our expected media types
-        self.getPage('/accept/select', [('Accept', 'text/html')])
-        self.assertStatus(200)
-        self.assertBody('<h2>Page Title</h2>')
-        self.getPage('/accept/select', [('Accept', 'text/plain')])
-        self.assertStatus(200)
-        self.assertBody('PAGE TITLE')
-        self.getPage('/accept/select', [('Accept', 'text/plain, text/*;q=0.5')])
-        self.assertStatus(200)
-        self.assertBody('PAGE TITLE')
-        
-        # text/* and */* should prefer text/html since it comes first
-        # in our 'media' argument to tools.accept
-        self.getPage('/accept/select', [('Accept', 'text/*')])
-        self.assertStatus(200)
-        self.assertBody('<h2>Page Title</h2>')
-        self.getPage('/accept/select', [('Accept', '*/*')])
-        self.assertStatus(200)
-        self.assertBody('<h2>Page Title</h2>')
-        
-        # Try unacceptable media types
-        self.getPage('/accept/select', [('Accept', 'application/xml')])
-        self.assertErrorPage(406,
-                             "Your client sent this Accept header: application/xml. "
-                             "But this resource only emits these media types: "
-                             "text/html, text/plain.")
-
-
-class AutoVaryTest(helper.CPWebCase):
-
-    def testAutoVary(self):
-        self.getPage('/autovary/')
-        self.assertHeader(
-            "Vary", 'Accept, Accept-Charset, Accept-Encoding, Host, If-Modified-Since, Range')
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_objectmapping.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,394 +0,0 @@
-from cherrypy.test import test
-from cherrypy._cptree import Application
-test.prefer_parent_path()
-
-import cherrypy
-
-
-script_names = ["", "/foo", "/users/fred/blog", "/corp/blog"]
-
-def setup_server():
-    class Root:
-        def index(self, name="world"):
-            return name
-        index.exposed = True
-        
-        def foobar(self):
-            return "bar"
-        foobar.exposed = True
-        
-        def default(self, *params, **kwargs):
-            return "default:" + repr(params)
-        default.exposed = True
-        
-        def other(self):
-            return "other"
-        other.exposed = True
-        
-        def extra(self, *p):
-            return repr(p)
-        extra.exposed = True
-        
-        def redirect(self):
-            raise cherrypy.HTTPRedirect('dir1/', 302)
-        redirect.exposed = True
-        
-        def notExposed(self):
-            return "not exposed"
-        
-        def confvalue(self):
-            return cherrypy.request.config.get("user")
-        confvalue.exposed = True
-        
-        def redirect_via_url(self, path):
-            raise cherrypy.HTTPRedirect(cherrypy.url(path))
-        redirect_via_url.exposed = True
-    
-    def mapped_func(self, ID=None):
-        return "ID is %s" % ID
-    mapped_func.exposed = True
-    setattr(Root, "Von B\xfclow", mapped_func)
-    
-    
-    class Exposing:
-        def base(self):
-            return "expose works!"
-        cherrypy.expose(base)
-        cherrypy.expose(base, "1")
-        cherrypy.expose(base, "2")
-    
-    class ExposingNewStyle(object):
-        def base(self):
-            return "expose works!"
-        cherrypy.expose(base)
-        cherrypy.expose(base, "1")
-        cherrypy.expose(base, "2")
-    
-    
-    class Dir1:
-        def index(self):
-            return "index for dir1"
-        index.exposed = True
-        
-        def myMethod(self):
-            return "myMethod from dir1, path_info is:" + repr(cherrypy.request.path_info)
-        myMethod.exposed = True
-        myMethod._cp_config = {'tools.trailing_slash.extra': True}
-        
-        def default(self, *params):
-            return "default for dir1, param is:" + repr(params)
-        default.exposed = True
-
-
-    class Dir2:
-        def index(self):
-            return "index for dir2, path is:" + cherrypy.request.path_info
-        index.exposed = True
-        
-        def script_name(self):
-            return cherrypy.tree.script_name()
-        script_name.exposed = True
-        
-        def cherrypy_url(self):
-            return cherrypy.url("/extra")
-        cherrypy_url.exposed = True
-        
-        def posparam(self, *vpath):
-            return "/".join(vpath)
-        posparam.exposed = True
-    
-    
-    class Dir3:
-        def default(self):
-            return "default for dir3, not exposed"
-    
-    class Dir4:
-        def index(self):
-            return "index for dir4, not exposed"
-    
-    class DefNoIndex:
-        def default(self, *args):
-            raise cherrypy.HTTPRedirect("contact")
-        default.exposed = True
-    
-    # MethodDispatcher code
-    class ByMethod:
-        exposed = True
-        
-        def __init__(self, *things):
-            self.things = list(things)
-        
-        def GET(self):
-            return repr(self.things)
-        
-        def POST(self, thing):
-            self.things.append(thing)
-    
-    class Collection:
-        default = ByMethod('a', 'bit')
-    
-    Root.exposing = Exposing()
-    Root.exposingnew = ExposingNewStyle()
-    Root.dir1 = Dir1()
-    Root.dir1.dir2 = Dir2()
-    Root.dir1.dir2.dir3 = Dir3()
-    Root.dir1.dir2.dir3.dir4 = Dir4()
-    Root.defnoindex = DefNoIndex()
-    Root.bymethod = ByMethod('another')
-    Root.collection = Collection()
-    
-    d = cherrypy.dispatch.MethodDispatcher()
-    for url in script_names:
-        conf = {'/': {'user': (url or "/").split("/")[-2]},
-                '/bymethod': {'request.dispatch': d},
-                '/collection': {'request.dispatch': d},
-                }
-        cherrypy.tree.mount(Root(), url, conf)
-    
-    
-    class Isolated:
-        def index(self):
-            return "made it!"
-        index.exposed = True
-    
-    cherrypy.tree.mount(Isolated(), "/isolated")
-    
-    class AnotherApp:
-        
-        exposed = True
-        
-        def GET(self):
-            return "milk"
-    
-    cherrypy.tree.mount(AnotherApp(), "/app", {'/': {'request.dispatch': d}})
-
-
-from cherrypy.test import helper
-
-class ObjectMappingTest(helper.CPWebCase):
-    
-    def testObjectMapping(self):
-        for url in script_names:
-            prefix = self.script_name = url
-            
-            self.getPage('/')
-            self.assertBody('world')
-            
-            self.getPage("/dir1/myMethod")
-            self.assertBody("myMethod from dir1, path_info is:'/dir1/myMethod'")
-            
-            self.getPage("/this/method/does/not/exist")
-            self.assertBody("default:('this', 'method', 'does', 'not', 'exist')")
-            
-            self.getPage("/extra/too/much")
-            self.assertBody("('too', 'much')")
-            
-            self.getPage("/other")
-            self.assertBody('other')
-            
-            self.getPage("/notExposed")
-            self.assertBody("default:('notExposed',)")
-            
-            self.getPage("/dir1/dir2/")
-            self.assertBody('index for dir2, path is:/dir1/dir2/')
-            
-            # Test omitted trailing slash (should be redirected by default).
-            self.getPage("/dir1/dir2")
-            self.assertStatus(301)
-            self.assertHeader('Location', '%s/dir1/dir2/' % self.base())
-            
-            # Test extra trailing slash (should be redirected if configured).
-            self.getPage("/dir1/myMethod/")
-            self.assertStatus(301)
-            self.assertHeader('Location', '%s/dir1/myMethod' % self.base())
-            
-            # Test that default method must be exposed in order to match.
-            self.getPage("/dir1/dir2/dir3/dir4/index")
-            self.assertBody("default for dir1, param is:('dir2', 'dir3', 'dir4', 'index')")
-            
-            # Test *vpath when default() is defined but not index()
-            # This also tests HTTPRedirect with default.
-            self.getPage("/defnoindex")
-            self.assertStatus((302, 303))
-            self.assertHeader('Location', '%s/contact' % self.base())
-            self.getPage("/defnoindex/")
-            self.assertStatus((302, 303))
-            self.assertHeader('Location', '%s/defnoindex/contact' % self.base())
-            self.getPage("/defnoindex/page")
-            self.assertStatus((302, 303))
-            self.assertHeader('Location', '%s/defnoindex/contact' % self.base())
-            
-            self.getPage("/redirect")
-            self.assertStatus('302 Found')
-            self.assertHeader('Location', '%s/dir1/' % self.base())
-            
-            if not getattr(cherrypy.server, "using_apache", False):
-                # Test that we can use URL's which aren't all valid Python identifiers
-                # This should also test the %XX-unquoting of URL's.
-                self.getPage("/Von%20B%fclow?ID=14")
-                self.assertBody("ID is 14")
-                
-                # Test that %2F in the path doesn't get unquoted too early;
-                # that is, it should not be used to separate path components.
-                # See ticket #393.
-                self.getPage("/page%2Fname")
-                self.assertBody("default:('page/name',)")
-            
-            self.getPage("/dir1/dir2/script_name")
-            self.assertBody(url)
-            self.getPage("/dir1/dir2/cherrypy_url")
-            self.assertBody("%s/extra" % self.base())
-            
-            # Test that configs don't overwrite each other from diferent apps
-            self.getPage("/confvalue")
-            self.assertBody((url or "/").split("/")[-2])
-        
-        self.script_name = ""
-        
-        # Test absoluteURI's in the Request-Line
-        self.getPage('http://%s:%s/' % (self.interface(), self.PORT))
-        self.assertBody('world')
-        
-        self.getPage('http://%s:%s/abs/?service=http://192.168.0.1/x/y/z' %
-                     (self.interface(), self.PORT))
-        self.assertBody("default:('abs',)")
-        
-        self.getPage('/rel/?service=http://192.168.120.121:8000/x/y/z')
-        self.assertBody("default:('rel',)")
-        
-        # Test that the "isolated" app doesn't leak url's into the root app.
-        # If it did leak, Root.default() would answer with
-        #   "default:('isolated', 'doesnt', 'exist')".
-        self.getPage("/isolated/")
-        self.assertStatus("200 OK")
-        self.assertBody("made it!")
-        self.getPage("/isolated/doesnt/exist")
-        self.assertStatus("404 Not Found")
-        
-        # Make sure /foobar maps to Root.foobar and not to the app
-        # mounted at /foo. See http://www.cherrypy.org/ticket/573
-        self.getPage("/foobar")
-        self.assertBody("bar")
-    
-    def test_redir_using_url(self):
-        for url in script_names:
-            prefix = self.script_name = url
-            
-            # Test the absolute path to the parent (leading slash)
-            self.getPage('/redirect_via_url?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-            
-            # Test the relative path to the parent (no leading slash)
-            self.getPage('/redirect_via_url?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-            
-            # Test the absolute path to the parent (leading slash)
-            self.getPage('/redirect_via_url/?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-            
-            # Test the relative path to the parent (no leading slash)
-            self.getPage('/redirect_via_url/?path=./')
-            self.assertStatus(('302 Found', '303 See Other'))
-            self.assertHeader('Location', '%s/' % self.base())
-    
-    def testPositionalParams(self):
-        self.getPage("/dir1/dir2/posparam/18/24/hut/hike")
-        self.assertBody("18/24/hut/hike")
-        
-        # intermediate index methods should not receive posparams;
-        # only the "final" index method should do so.
-        self.getPage("/dir1/dir2/5/3/sir")
-        self.assertBody("default for dir1, param is:('dir2', '5', '3', 'sir')")
-        
-        # test that extra positional args raises an 404 Not Found
-        # See http://www.cherrypy.org/ticket/733.
-        self.getPage("/dir1/dir2/script_name/extra/stuff")
-        self.assertStatus(404)
-    
-    def testExpose(self):
-        # Test the cherrypy.expose function/decorator
-        self.getPage("/exposing/base")
-        self.assertBody("expose works!")
-        
-        self.getPage("/exposing/1")
-        self.assertBody("expose works!")
-        
-        self.getPage("/exposing/2")
-        self.assertBody("expose works!")
-        
-        self.getPage("/exposingnew/base")
-        self.assertBody("expose works!")
-        
-        self.getPage("/exposingnew/1")
-        self.assertBody("expose works!")
-        
-        self.getPage("/exposingnew/2")
-        self.assertBody("expose works!")
-    
-    def testMethodDispatch(self):
-        self.getPage("/bymethod")
-        self.assertBody("['another']")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-        
-        self.getPage("/bymethod", method="HEAD")
-        self.assertBody("")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-        
-        self.getPage("/bymethod", method="POST", body="thing=one")
-        self.assertBody("")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-        
-        self.getPage("/bymethod")
-        self.assertBody("['another', u'one']")
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-        
-        self.getPage("/bymethod", method="PUT")
-        self.assertErrorPage(405)
-        self.assertHeader('Allow', 'GET, HEAD, POST')
-        
-        # Test default with posparams
-        self.getPage("/collection/silly", method="POST")
-        self.getPage("/collection", method="GET")
-        self.assertBody("['a', 'bit', 'silly']")
-        
-        # Test custom dispatcher set on app root (see #737).
-        self.getPage("/app")
-        self.assertBody("milk")
-
-    def testTreeMounting(self):
-        class Root(object):
-            def hello(self):
-                return "Hello world!"
-            hello.exposed = True
-        
-        # When mounting an application instance, 
-        # we can't specify a different script name in the call to mount.
-        a = Application(Root(), '/somewhere')
-        self.assertRaises(ValueError, cherrypy.tree.mount, a, '/somewhereelse')
-        
-        # When mounting an application instance...
-        a = Application(Root(), '/somewhere')
-        # ...we MUST allow in identical script name in the call to mount...
-        cherrypy.tree.mount(a, '/somewhere')
-        self.getPage('/somewhere/hello')
-        self.assertStatus(200)
-        # ...and MUST allow a missing script_name.
-        del cherrypy.tree.apps['/somewhere']
-        cherrypy.tree.mount(a)
-        self.getPage('/somewhere/hello')
-        self.assertStatus(200)
-        
-        # In addition, we MUST be able to create an Application using
-        # script_name == None for access to the wsgi_environ.
-        a = Application(Root(), script_name=None)
-        # However, this does not apply to tree.mount
-        self.assertRaises(TypeError, cherrypy.tree.mount, a, None)
-
-
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_proxy.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-
-script_names = ["", "/path/to/myapp"]
-
-def setup_server():
-    
-    # Set up site
-    cherrypy.config.update({
-        'tools.proxy.on': True,
-        'tools.proxy.base': 'www.mydomain.test',
-        })
-    
-    # Set up application
-    
-    class Root:
-        
-        def __init__(self, sn):
-            # Calculate a URL outside of any requests.
-            self.thisnewpage = cherrypy.url("/this/new/page", script_name=sn)
-        
-        def pageurl(self):
-            return self.thisnewpage
-        pageurl.exposed = True
-        
-        def index(self):
-            raise cherrypy.HTTPRedirect('dummy')
-        index.exposed = True
-        
-        def remoteip(self):
-            return cherrypy.request.remote.ip
-        remoteip.exposed = True
-        
-        def xhost(self):
-            raise cherrypy.HTTPRedirect('blah')
-        xhost.exposed = True
-        xhost._cp_config = {'tools.proxy.local': 'X-Host',
-                            'tools.trailing_slash.extra': True,
-                            }
-        
-        def base(self):
-            return cherrypy.request.base
-        base.exposed = True
-        
-        def ssl(self):
-            return cherrypy.request.base
-        ssl.exposed = True
-        ssl._cp_config = {'tools.proxy.scheme': 'X-Forwarded-Ssl'}
-        
-        def newurl(self):
-            return ("Browse to <a href='%s'>this page</a>."
-                    % cherrypy.url("/this/new/page"))
-        newurl.exposed = True
-    
-    for sn in script_names:
-        cherrypy.tree.mount(Root(sn), sn)
-
-
-from cherrypy.test import helper
-
-class ProxyTest(helper.CPWebCase):
-    
-    def testProxy(self):
-        self.getPage("/")
-        self.assertHeader('Location',
-                          "%s://www.mydomain.test%s/dummy" %
-                          (self.scheme, self.prefix()))
-        
-        # Test X-Forwarded-Host (Apache 1.3.33+ and Apache 2)
-        self.getPage("/", headers=[('X-Forwarded-Host', 'http://www.example.test')])
-        self.assertHeader('Location', "http://www.example.test/dummy")
-        self.getPage("/", headers=[('X-Forwarded-Host', 'www.example.test')])
-        self.assertHeader('Location', "%s://www.example.test/dummy" % self.scheme)
-        # Test multiple X-Forwarded-Host headers
-        self.getPage("/", headers=[
-            ('X-Forwarded-Host', 'http://www.example.test, www.cherrypy.test'),
-            ])
-        self.assertHeader('Location', "http://www.example.test/dummy")
-        
-        # Test X-Forwarded-For (Apache2)
-        self.getPage("/remoteip",
-                     headers=[('X-Forwarded-For', '192.168.0.20')])
-        self.assertBody("192.168.0.20")
-        self.getPage("/remoteip",
-                     headers=[('X-Forwarded-For', '67.15.36.43, 192.168.0.20')])
-        self.assertBody("192.168.0.20")
-        
-        # Test X-Host (lighttpd; see https://trac.lighttpd.net/trac/ticket/418)
-        self.getPage("/xhost", headers=[('X-Host', 'www.example.test')])
-        self.assertHeader('Location', "%s://www.example.test/blah" % self.scheme)
-        
-        # Test X-Forwarded-Proto (lighttpd)
-        self.getPage("/base", headers=[('X-Forwarded-Proto', 'https')])
-        self.assertBody("https://www.mydomain.test")
-        
-        # Test X-Forwarded-Ssl (webfaction?)
-        self.getPage("/ssl", headers=[('X-Forwarded-Ssl', 'on')])
-        self.assertBody("https://www.mydomain.test")
-        
-        # Test cherrypy.url()
-        for sn in script_names:
-            # Test the value inside requests
-            self.getPage(sn + "/newurl")
-            self.assertBody("Browse to <a href='%s://www.mydomain.test" % self.scheme
-                            + sn + "/this/new/page'>this page</a>.")
-            self.getPage(sn + "/newurl", headers=[('X-Forwarded-Host',
-                                                   'http://www.example.test')])
-            self.assertBody("Browse to <a href='http://www.example.test"
-                            + sn + "/this/new/page'>this page</a>.")
-            
-            # Test the value outside requests
-            port = ""
-            if self.scheme == "http" and self.PORT != 80:
-                port = ":%s" % self.PORT
-            elif self.scheme == "https" and self.PORT != 443:
-                port = ":%s" % self.PORT
-            host = self.HOST
-            if host in ('0.0.0.0', '::'):
-                import socket
-                host = socket.gethostname()
-            expected = ("%s://%s%s%s/this/new/page"
-                        % (self.scheme, host, port, sn))
-            self.getPage(sn + "/pageurl")
-            self.assertBody(expected)
-        
-        # Test trailing slash (see http://www.cherrypy.org/ticket/562).
-        self.getPage("/xhost/", headers=[('X-Host', 'www.example.test')])
-        self.assertHeader('Location', "%s://www.example.test/xhost"
-                          % self.scheme)
-
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_refleaks.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-"""Tests for refleaks."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import gc
-from httplib import HTTPConnection, HTTPSConnection
-import threading
-
-import cherrypy
-from cherrypy import _cprequest
-
-
-data = object()
-
-def get_instances(cls):
-    return [x for x in gc.get_objects() if isinstance(x, cls)]
-
-def setup_server():
-    
-    class Root:
-        def index(self, *args, **kwargs):
-            cherrypy.request.thing = data
-            return "Hello world!"
-        index.exposed = True
-        
-        def gc_stats(self):
-            output = ["Statistics:"]
-            
-            # Uncollectable garbage
-            
-            # gc_collect isn't perfectly synchronous, because it may
-            # break reference cycles that then take time to fully
-            # finalize. Call it twice and hope for the best.
-            gc.collect()
-            unreachable = gc.collect()
-            if unreachable:
-                output.append("\n%s unreachable objects:" % unreachable)
-                trash = {}
-                for x in gc.garbage:
-                    trash[type(x)] = trash.get(type(x), 0) + 1
-                trash = [(v, k) for k, v in trash.iteritems()]
-                trash.sort()
-                for pair in trash:
-                    output.append("    " + repr(pair))
-            
-            # Request references
-            reqs = get_instances(_cprequest.Request)
-            lenreqs = len(reqs)
-            if lenreqs < 2:
-                output.append("\nMissing Request reference. Should be 1 in "
-                              "this request thread and 1 in the main thread.")
-            elif lenreqs > 2:
-                output.append("\nToo many Request references (%r)." % lenreqs)
-                for req in reqs:
-                    output.append("Referrers for %s:" % repr(req))
-                    for ref in gc.get_referrers(req):
-                        if ref is not reqs:
-                            output.append("    %s" % repr(ref))
-            
-            # Response references
-            resps = get_instances(_cprequest.Response)
-            lenresps = len(resps)
-            if lenresps < 2:
-                output.append("\nMissing Response reference. Should be 1 in "
-                              "this request thread and 1 in the main thread.")
-            elif lenresps > 2:
-                output.append("\nToo many Response references (%r)." % lenresps)
-                for resp in resps:
-                    output.append("Referrers for %s:" % repr(resp))
-                    for ref in gc.get_referrers(resp):
-                        if ref is not resps:
-                            output.append("    %s" % repr(ref))
-            
-            return "\n".join(output)
-        gc_stats.exposed = True
-    
-    cherrypy.tree.mount(Root())
-
-
-from cherrypy.test import helper
-
-
-class ReferenceTests(helper.CPWebCase):
-    
-    def test_threadlocal_garbage(self):
-        success = []
-        
-        def getpage():
-            host = '%s:%s' % (self.interface(), self.PORT)
-            if self.scheme == 'https':
-                c = HTTPSConnection(host)
-            else:
-                c = HTTPConnection(host)
-            try:
-                c.putrequest('GET', '/')
-                c.endheaders()
-                response = c.getresponse()
-                body = response.read()
-                self.assertEqual(response.status, 200)
-                self.assertEqual(body, "Hello world!")
-            finally:
-                c.close()
-            success.append(True)
-        
-        ITERATIONS = 25
-        ts = []
-        for _ in range(ITERATIONS):
-            t = threading.Thread(target=getpage)
-            ts.append(t)
-            t.start()
-        
-        for t in ts:
-            t.join()
-        
-        self.assertEqual(len(success), ITERATIONS)
-        
-        self.getPage("/gc_stats")
-        self.assertBody("Statistics:")
-
-
-if __name__ == '__main__':
-    helper.testmain({'server.socket_queue_size': 10})
--- a/bundled/cherrypy/cherrypy/test/test_request_obj.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,728 +0,0 @@
-"""Basic tests for the cherrypy.Request object."""
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os
-localDir = os.path.dirname(__file__)
-import sys
-import types
-from httplib import IncompleteRead
-
-import cherrypy
-from cherrypy import _cptools, tools
-from cherrypy.lib import static, httputil
-
-defined_http_methods = ("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE",
-                        "TRACE", "PROPFIND")
-
-
-def setup_server():
-    class Root:
-        
-        def index(self):
-            return "hello"
-        index.exposed = True
-        
-        def scheme(self):
-            return cherrypy.request.scheme
-        scheme.exposed = True
-    
-    root = Root()
-    
-    
-    class TestType(type):
-        """Metaclass which automatically exposes all functions in each subclass,
-        and adds an instance of the subclass as an attribute of root.
-        """
-        def __init__(cls, name, bases, dct):
-            type.__init__(cls, name, bases, dct)
-            for value in dct.itervalues():
-                if isinstance(value, types.FunctionType):
-                    value.exposed = True
-            setattr(root, name.lower(), cls())
-    class Test(object):
-        __metaclass__ = TestType
-    
-    
-    class Params(Test):
-        
-        def index(self, thing):
-            return repr(thing)
-        
-        def ismap(self, x, y):
-            return "Coordinates: %s, %s" % (x, y)
-        
-        def default(self, *args, **kwargs):
-            return "args: %s kwargs: %s" % (args, kwargs)
-        default._cp_config = {'request.query_string_encoding': 'latin1'}
-
-
-    class ParamErrorsCallable(object):
-        exposed = True
-        def __call__(self):
-            return "data"
-
-    class ParamErrors(Test):
-
-        def one_positional(self, param1):
-            return "data"
-        one_positional.exposed = True
-
-        def one_positional_args(self, param1, *args):
-            return "data"
-        one_positional_args.exposed = True
-
-        def one_positional_args_kwargs(self, param1, *args, **kwargs):
-            return "data"
-        one_positional_args_kwargs.exposed = True
-
-        def one_positional_kwargs(self, param1, **kwargs):
-            return "data"
-        one_positional_kwargs.exposed = True
-
-        def no_positional(self):
-            return "data"
-        no_positional.exposed = True
-
-        def no_positional_args(self, *args):
-            return "data"
-        no_positional_args.exposed = True
-
-        def no_positional_args_kwargs(self, *args, **kwargs):
-            return "data"
-        no_positional_args_kwargs.exposed = True
-
-        def no_positional_kwargs(self, **kwargs):
-            return "data"
-        no_positional_kwargs.exposed = True
-
-        callable_object = ParamErrorsCallable()
-
-        def raise_type_error(self, **kwargs):
-            raise TypeError("Client Error")
-        raise_type_error.exposed = True
-
-        def raise_type_error_with_default_param(self, x, y=None):
-            return '%d' % 'a' # throw an exception
-        raise_type_error_with_default_param.exposed = True
-
-    def callable_error_page(status, **kwargs):
-        return "Error %s - Well, I'm very sorry but you haven't paid!" % status
-    
-    
-    class Error(Test):
-        
-        _cp_config = {'tools.log_tracebacks.on': True,
-                      }
-        
-        def reason_phrase(self):
-            raise cherrypy.HTTPError("410 Gone fishin'")
-        
-        def custom(self, err='404'):
-            raise cherrypy.HTTPError(int(err), "No, <b>really</b>, not found!")
-        custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html"),
-                             'error_page.401': callable_error_page,
-                             }
-        
-        def custom_default(self):
-            return 1 + 'a' # raise an unexpected error
-        custom_default._cp_config = {'error_page.default': callable_error_page}
-        
-        def noexist(self):
-            raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
-        noexist._cp_config = {'error_page.404': "nonexistent.html"}
-        
-        def page_method(self):
-            raise ValueError()
-        
-        def page_yield(self):
-            yield "howdy"
-            raise ValueError()
-        
-        def page_streamed(self):
-            yield "word up"
-            raise ValueError()
-            yield "very oops"
-        page_streamed._cp_config = {"response.stream": True}
-        
-        def cause_err_in_finalize(self):
-            # Since status must start with an int, this should error.
-            cherrypy.response.status = "ZOO OK"
-        cause_err_in_finalize._cp_config = {'request.show_tracebacks': False}
-        
-        def rethrow(self):
-            """Test that an error raised here will be thrown out to the server."""
-            raise ValueError()
-        rethrow._cp_config = {'request.throw_errors': True}
-    
-    
-    class Expect(Test):
-        
-        def expectation_failed(self):
-            expect = cherrypy.request.headers.elements("Expect")
-            if expect and expect[0].value != '100-continue':
-                raise cherrypy.HTTPError(400)
-            raise cherrypy.HTTPError(417, 'Expectation Failed')
-
-    class Headers(Test):
-        
-        def default(self, headername):
-            """Spit back out the value for the requested header."""
-            return cherrypy.request.headers[headername]
-        
-        def doubledheaders(self):
-            # From http://www.cherrypy.org/ticket/165:
-            # "header field names should not be case sensitive sayes the rfc.
-            # if i set a headerfield in complete lowercase i end up with two
-            # header fields, one in lowercase, the other in mixed-case."
-            
-            # Set the most common headers
-            hMap = cherrypy.response.headers
-            hMap['content-type'] = "text/html"
-            hMap['content-length'] = 18
-            hMap['server'] = 'CherryPy headertest'
-            hMap['location'] = ('%s://%s:%s/headers/'
-                                % (cherrypy.request.local.ip,
-                                   cherrypy.request.local.port,
-                                   cherrypy.request.scheme))
-            
-            # Set a rare header for fun
-            hMap['Expires'] = 'Thu, 01 Dec 2194 16:00:00 GMT'
-            
-            return "double header test"
-        
-        def ifmatch(self):
-            val = cherrypy.request.headers['If-Match']
-            assert isinstance(val, unicode)
-            cherrypy.response.headers['ETag'] = val
-            return val
-    
-    
-    class HeaderElements(Test):
-        
-        def get_elements(self, headername):
-            e = cherrypy.request.headers.elements(headername)
-            return "\n".join([unicode(x) for x in e])
-    
-    
-    class Method(Test):
-        
-        def index(self):
-            m = cherrypy.request.method
-            if m in defined_http_methods or m == "CONNECT":
-                return m
-            
-            if m == "LINK":
-                raise cherrypy.HTTPError(405)
-            else:
-                raise cherrypy.HTTPError(501)
-        
-        def parameterized(self, data):
-            return data
-        
-        def request_body(self):
-            # This should be a file object (temp file),
-            # which CP will just pipe back out if we tell it to.
-            return cherrypy.request.body
-        
-        def reachable(self):
-            return "success"
-
-    class Divorce:
-        """HTTP Method handlers shouldn't collide with normal method names.
-        For example, a GET-handler shouldn't collide with a method named 'get'.
-        
-        If you build HTTP method dispatching into CherryPy, rewrite this class
-        to use your new dispatch mechanism and make sure that:
-            "GET /divorce HTTP/1.1" maps to divorce.index() and
-            "GET /divorce/get?ID=13 HTTP/1.1" maps to divorce.get()
-        """
-        
-        documents = {}
-        
-        def index(self):
-            yield "<h1>Choose your document</h1>\n"
-            yield "<ul>\n"
-            for id, contents in self.documents.items():
-                yield ("    <li><a href='/divorce/get?ID=%s'>%s</a>: %s</li>\n"
-                       % (id, id, contents))
-            yield "</ul>"
-        index.exposed = True
-        
-        def get(self, ID):
-            return ("Divorce document %s: %s" %
-                    (ID, self.documents.get(ID, "empty")))
-        get.exposed = True
-
-    root.divorce = Divorce()
-
-
-    class ThreadLocal(Test):
-        
-        def index(self):
-            existing = repr(getattr(cherrypy.request, "asdf", None))
-            cherrypy.request.asdf = "rassfrassin"
-            return existing
-    
-    appconf = {
-        '/method': {'request.methods_with_bodies': ("POST", "PUT", "PROPFIND")},
-        }
-    cherrypy.tree.mount(root, config=appconf)
-
-
-#                             Client-side code                             #
-
-from cherrypy.test import helper
-
-class RequestObjectTests(helper.CPWebCase):
-    
-    def test_scheme(self):
-        self.getPage("/scheme")
-        self.assertBody(self.scheme)
-    
-    def testParams(self):
-        self.getPage("/params/?thing=a")
-        self.assertBody("u'a'")
-        
-        self.getPage("/params/?thing=a&thing=b&thing=c")
-        self.assertBody("[u'a', u'b', u'c']")
-
-        # Test friendly error message when given params are not accepted.
-        cherrypy.config.update({"request.show_mismatched_params": True})
-        self.getPage("/params/?notathing=meeting")
-        self.assertInBody("Missing parameters: thing")
-        self.getPage("/params/?thing=meeting&notathing=meeting")
-        self.assertInBody("Unexpected query string parameters: notathing")
-        
-        # Test ability to turn off friendly error messages
-        cherrypy.config.update({"request.show_mismatched_params": False})
-        self.getPage("/params/?notathing=meeting")
-        self.assertInBody("Not Found")
-        self.getPage("/params/?thing=meeting&notathing=meeting")
-        self.assertInBody("Not Found")
-
-        # Test "% HEX HEX"-encoded URL, param keys, and values
-        self.getPage("/params/%d4%20%e3/cheese?Gruy%E8re=Bulgn%e9ville")
-        self.assertBody(r"args: ('\xd4 \xe3', 'cheese') "
-                        r"kwargs: {'Gruy\xe8re': u'Bulgn\xe9ville'}")
-        
-        # Make sure that encoded = and & get parsed correctly
-        self.getPage("/params/code?url=http%3A//cherrypy.org/index%3Fa%3D1%26b%3D2")
-        self.assertBody(r"args: ('code',) "
-                        r"kwargs: {'url': u'http://cherrypy.org/index?a=1&b=2'}")
-        
-        # Test coordinates sent by <img ismap>
-        self.getPage("/params/ismap?223,114")
-        self.assertBody("Coordinates: 223, 114")
-        
-        # Test "name[key]" dict-like params
-        self.getPage("/params/dictlike?a[1]=1&a[2]=2&b=foo&b[bar]=baz")
-        self.assertBody(
-            "args: ('dictlike',) "
-            "kwargs: {'a[1]': u'1', 'b[bar]': u'baz', 'b': u'foo', 'a[2]': u'2'}")
-
-    def testParamErrors(self):
-
-        # test that all of the handlers work when given 
-        # the correct parameters in order to ensure that the
-        # errors below aren't coming from some other source.
-        for uri in (
-                '/paramerrors/one_positional?param1=foo',
-                '/paramerrors/one_positional_args?param1=foo',
-                '/paramerrors/one_positional_args/foo',
-                '/paramerrors/one_positional_args/foo/bar/baz',
-                '/paramerrors/one_positional_args_kwargs?param1=foo&param2=bar',
-                '/paramerrors/one_positional_args_kwargs/foo?param2=bar&param3=baz',
-                '/paramerrors/one_positional_args_kwargs/foo/bar/baz?param2=bar&param3=baz',
-                '/paramerrors/one_positional_kwargs?param1=foo&param2=bar&param3=baz',
-                '/paramerrors/one_positional_kwargs/foo?param4=foo&param2=bar&param3=baz',
-                '/paramerrors/no_positional',
-                '/paramerrors/no_positional_args/foo',
-                '/paramerrors/no_positional_args/foo/bar/baz',
-                '/paramerrors/no_positional_args_kwargs?param1=foo&param2=bar',
-                '/paramerrors/no_positional_args_kwargs/foo?param2=bar',
-                '/paramerrors/no_positional_args_kwargs/foo/bar/baz?param2=bar&param3=baz',
-                '/paramerrors/no_positional_kwargs?param1=foo&param2=bar',
-                '/paramerrors/callable_object',
-            ):
-            self.getPage(uri)
-            self.assertStatus(200)
-
-        # query string parameters are part of the URI, so if they are wrong
-        # for a particular handler, the status MUST be a 404.
-        error_msgs = [
-                'Missing parameters',
-                'Nothing matches the given URI',
-                'Multiple values for parameters',
-                'Unexpected query string parameters',
-                'Unexpected body parameters',
-            ]
-        for uri, msg in (
-            ('/paramerrors/one_positional', error_msgs[0]),
-            ('/paramerrors/one_positional?foo=foo', error_msgs[0]),
-            ('/paramerrors/one_positional/foo/bar/baz', error_msgs[1]),
-            ('/paramerrors/one_positional/foo?param1=foo', error_msgs[2]),
-            ('/paramerrors/one_positional/foo?param1=foo&param2=foo', error_msgs[2]),
-            ('/paramerrors/one_positional_args/foo?param1=foo&param2=foo', error_msgs[2]),
-            ('/paramerrors/one_positional_args/foo/bar/baz?param2=foo', error_msgs[3]),
-            ('/paramerrors/one_positional_args_kwargs/foo/bar/baz?param1=bar&param3=baz', error_msgs[2]),
-            ('/paramerrors/one_positional_kwargs/foo?param1=foo&param2=bar&param3=baz', error_msgs[2]),
-            ('/paramerrors/no_positional/boo', error_msgs[1]),
-            ('/paramerrors/no_positional?param1=foo', error_msgs[3]),
-            ('/paramerrors/no_positional_args/boo?param1=foo', error_msgs[3]),
-            ('/paramerrors/no_positional_kwargs/boo?param1=foo', error_msgs[1]),
-            ('/paramerrors/callable_object?param1=foo', error_msgs[3]),
-            ('/paramerrors/callable_object/boo', error_msgs[1]),
-            ):
-            for show_mismatched_params in (True, False):
-                cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
-                self.getPage(uri)
-                self.assertStatus(404)
-                if show_mismatched_params:
-                    self.assertInBody(msg)
-                else:
-                    self.assertInBody("Not Found")
-
-        # if body parameters are wrong, a 400 must be returned.
-        for uri, body, msg in (
-                ('/paramerrors/one_positional/foo', 'param1=foo', error_msgs[2]),
-                ('/paramerrors/one_positional/foo', 'param1=foo&param2=foo', error_msgs[2]),
-                ('/paramerrors/one_positional_args/foo', 'param1=foo&param2=foo', error_msgs[2]),
-                ('/paramerrors/one_positional_args/foo/bar/baz', 'param2=foo', error_msgs[4]),
-                ('/paramerrors/one_positional_args_kwargs/foo/bar/baz', 'param1=bar&param3=baz', error_msgs[2]),
-                ('/paramerrors/one_positional_kwargs/foo', 'param1=foo&param2=bar&param3=baz', error_msgs[2]),
-                ('/paramerrors/no_positional', 'param1=foo', error_msgs[4]),
-                ('/paramerrors/no_positional_args/boo', 'param1=foo', error_msgs[4]),
-                ('/paramerrors/callable_object', 'param1=foo', error_msgs[4]),
-            ):
-            for show_mismatched_params in (True, False):
-                cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
-                self.getPage(uri, method='POST', body=body)
-                self.assertStatus(400)
-                if show_mismatched_params:
-                    self.assertInBody(msg)
-                else:
-                    self.assertInBody("Bad Request")
-
-
-        # even if body parameters are wrong, if we get the uri wrong, then 
-        # it's a 404
-        for uri, body, msg in (
-                ('/paramerrors/one_positional?param2=foo', 'param1=foo', error_msgs[3]),
-                ('/paramerrors/one_positional/foo/bar', 'param2=foo', error_msgs[1]),
-                ('/paramerrors/one_positional_args/foo/bar?param2=foo', 'param3=foo', error_msgs[3]),
-                ('/paramerrors/one_positional_kwargs/foo/bar', 'param2=bar&param3=baz', error_msgs[1]),
-                ('/paramerrors/no_positional?param1=foo', 'param2=foo', error_msgs[3]),
-                ('/paramerrors/no_positional_args/boo?param2=foo', 'param1=foo', error_msgs[3]),
-                ('/paramerrors/callable_object?param2=bar', 'param1=foo', error_msgs[3]),
-            ):
-            for show_mismatched_params in (True, False):
-                cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
-                self.getPage(uri, method='POST', body=body)
-                self.assertStatus(404)
-                if show_mismatched_params:
-                    self.assertInBody(msg)
-                else:
-                    self.assertInBody("Not Found")
-
-        # In the case that a handler raises a TypeError we should
-        # let that type error through.
-        for uri in (
-                '/paramerrors/raise_type_error',
-                '/paramerrors/raise_type_error_with_default_param?x=0',
-                '/paramerrors/raise_type_error_with_default_param?x=0&y=0',
-            ):
-            self.getPage(uri, method='GET')
-            self.assertStatus(500)
-            self.assertTrue('Client Error', self.body)
-
-    def testErrorHandling(self):
-        self.getPage("/error/missing")
-        self.assertStatus(404)
-        self.assertErrorPage(404, "The path '/error/missing' was not found.")
-        
-        ignore = helper.webtest.ignored_exceptions
-        ignore.append(ValueError)
-        try:
-            valerr = '\n    raise ValueError()\nValueError'
-            self.getPage("/error/page_method")
-            self.assertErrorPage(500, pattern=valerr)
-            
-            self.getPage("/error/page_yield")
-            self.assertErrorPage(500, pattern=valerr)
-            
-            if (cherrypy.server.protocol_version == "HTTP/1.0" or
-                getattr(cherrypy.server, "using_apache", False)):
-                self.getPage("/error/page_streamed")
-                # Because this error is raised after the response body has
-                # started, the status should not change to an error status.
-                self.assertStatus(200)
-                self.assertBody("word up")
-            else:
-                # Under HTTP/1.1, the chunked transfer-coding is used.
-                # The HTTP client will choke when the output is incomplete.
-                self.assertRaises((ValueError, IncompleteRead), self.getPage,
-                                  "/error/page_streamed")
-            
-            # No traceback should be present
-            self.getPage("/error/cause_err_in_finalize")
-            msg = "Illegal response status from server ('ZOO' is non-numeric)."
-            self.assertErrorPage(500, msg, None)
-        finally:
-            ignore.pop()
-        
-        # Test HTTPError with a reason-phrase in the status arg.
-        self.getPage('/error/reason_phrase')
-        self.assertStatus("410 Gone fishin'")
-        
-        # Test custom error page for a specific error.
-        self.getPage("/error/custom")
-        self.assertStatus(404)
-        self.assertBody("Hello, world\r\n" + (" " * 499))
-        
-        # Test custom error page for a specific error.
-        self.getPage("/error/custom?err=401")
-        self.assertStatus(401)
-        self.assertBody("Error 401 Unauthorized - Well, I'm very sorry but you haven't paid!")
-        
-        # Test default custom error page.
-        self.getPage("/error/custom_default")
-        self.assertStatus(500)
-        self.assertBody("Error 500 Internal Server Error - Well, I'm very sorry but you haven't paid!".ljust(513))
-        
-        # Test error in custom error page (ticket #305).
-        # Note that the message is escaped for HTML (ticket #310).
-        self.getPage("/error/noexist")
-        self.assertStatus(404)
-        msg = ("No, &lt;b&gt;really&lt;/b&gt;, not found!<br />"
-               "In addition, the custom error page failed:\n<br />"
-               "IOError: [Errno 2] No such file or directory: 'nonexistent.html'")
-        self.assertInBody(msg)
-        
-        if getattr(cherrypy.server, "using_apache", False):
-            pass
-        else:
-            # Test throw_errors (ticket #186).
-            self.getPage("/error/rethrow")
-            self.assertInBody("raise ValueError()")
-    
-    def testExpect(self):
-        e = ('Expect', '100-continue')
-        self.getPage("/headerelements/get_elements?headername=Expect", [e])
-        self.assertBody('100-continue')
-        
-        self.getPage("/expect/expectation_failed", [e])
-        self.assertStatus(417)
-    
-    def testHeaderElements(self):
-        # Accept-* header elements should be sorted, with most preferred first.
-        h = [('Accept', 'audio/*; q=0.2, audio/basic')]
-        self.getPage("/headerelements/get_elements?headername=Accept", h)
-        self.assertStatus(200)
-        self.assertBody("audio/basic\n"
-                        "audio/*;q=0.2")
-        
-        h = [('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')]
-        self.getPage("/headerelements/get_elements?headername=Accept", h)
-        self.assertStatus(200)
-        self.assertBody("text/x-c\n"
-                        "text/html\n"
-                        "text/x-dvi;q=0.8\n"
-                        "text/plain;q=0.5")
-        
-        # Test that more specific media ranges get priority.
-        h = [('Accept', 'text/*, text/html, text/html;level=1, */*')]
-        self.getPage("/headerelements/get_elements?headername=Accept", h)
-        self.assertStatus(200)
-        self.assertBody("text/html;level=1\n"
-                        "text/html\n"
-                        "text/*\n"
-                        "*/*")
-        
-        # Test Accept-Charset
-        h = [('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8')]
-        self.getPage("/headerelements/get_elements?headername=Accept-Charset", h)
-        self.assertStatus("200 OK")
-        self.assertBody("iso-8859-5\n"
-                        "unicode-1-1;q=0.8")
-        
-        # Test Accept-Encoding
-        h = [('Accept-Encoding', 'gzip;q=1.0, identity; q=0.5, *;q=0')]
-        self.getPage("/headerelements/get_elements?headername=Accept-Encoding", h)
-        self.assertStatus("200 OK")
-        self.assertBody("gzip;q=1.0\n"
-                        "identity;q=0.5\n"
-                        "*;q=0")
-        
-        # Test Accept-Language
-        h = [('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7')]
-        self.getPage("/headerelements/get_elements?headername=Accept-Language", h)
-        self.assertStatus("200 OK")
-        self.assertBody("da\n"
-                        "en-gb;q=0.8\n"
-                        "en;q=0.7")
-        
-        # Test malformed header parsing. See http://www.cherrypy.org/ticket/763.
-        self.getPage("/headerelements/get_elements?headername=Content-Type",
-                     # Note the illegal trailing ";"
-                     headers=[('Content-Type', 'text/html; charset=utf-8;')])
-        self.assertStatus(200)
-        self.assertBody("text/html;charset=utf-8")
-    
-    def test_repeated_headers(self):
-        # Test that two request headers are collapsed into one.
-        # See http://www.cherrypy.org/ticket/542.
-        self.getPage("/headers/Accept-Charset",
-                     headers=[("Accept-Charset", "iso-8859-5"),
-                              ("Accept-Charset", "unicode-1-1;q=0.8")])
-        self.assertBody("iso-8859-5, unicode-1-1;q=0.8")
-        
-        # Tests that each header only appears once, regardless of case.
-        self.getPage("/headers/doubledheaders")
-        self.assertBody("double header test")
-        hnames = [name.title() for name, val in self.headers]
-        for key in ['Content-Length', 'Content-Type', 'Date',
-                    'Expires', 'Location', 'Server']:
-            self.assertEqual(hnames.count(key), 1, self.headers)
-    
-    def test_encoded_headers(self):
-        # First, make sure the innards work like expected.
-        self.assertEqual(httputil.decode_TEXT(u"=?utf-8?q?f=C3=BCr?="), u"f\xfcr")
-        
-        if cherrypy.server.protocol_version == "HTTP/1.1":
-            # Test RFC-2047-encoded request and response header values
-            u = u'\u212bngstr\xf6m'
-            c = u"=E2=84=ABngstr=C3=B6m"
-            self.getPage("/headers/ifmatch", [('If-Match', u'=?utf-8?q?%s?=' % c)])
-            # The body should be utf-8 encoded.
-            self.assertBody("\xe2\x84\xabngstr\xc3\xb6m")
-            # But the Etag header should be RFC-2047 encoded (binary)
-            self.assertHeader("ETag", u'=?utf-8?b?4oSrbmdzdHLDtm0=?=')
-            
-            # Test a *LONG* RFC-2047-encoded request and response header value
-            self.getPage("/headers/ifmatch",
-                         [('If-Match', u'=?utf-8?q?%s?=' % (c * 10))])
-            self.assertBody("\xe2\x84\xabngstr\xc3\xb6m" * 10)
-            # Note: this is different output for Python3, but it decodes fine.
-            etag = self.assertHeader("ETag",
-                '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
-                '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
-                '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
-                '4oSrbmdzdHLDtm0=?=')
-            self.assertEqual(httputil.decode_TEXT(etag), u * 10)
-    
-    def test_header_presence(self):
-        # If we don't pass a Content-Type header, it should not be present
-        # in cherrypy.request.headers
-        self.getPage("/headers/Content-Type",
-                     headers=[])
-        self.assertStatus(500)
-        
-        # If Content-Type is present in the request, it should be present in
-        # cherrypy.request.headers
-        self.getPage("/headers/Content-Type",
-                     headers=[("Content-type", "application/json")])
-        self.assertBody("application/json")
-    
-    def test_basic_HTTPMethods(self):
-        helper.webtest.methods_with_bodies = ("POST", "PUT", "PROPFIND")
-        
-        # Test that all defined HTTP methods work.
-        for m in defined_http_methods:
-            self.getPage("/method/", method=m)
-            
-            # HEAD requests should not return any body.
-            if m == "HEAD":
-                self.assertBody("")
-            elif m == "TRACE":
-                # Some HTTP servers (like modpy) have their own TRACE support
-                self.assertEqual(self.body[:5], "TRACE")
-            else:
-                self.assertBody(m)
-        
-        # Request a PUT method with a form-urlencoded body
-        self.getPage("/method/parameterized", method="PUT",
-                       body="data=on+top+of+other+things")
-        self.assertBody("on top of other things")
-        
-        # Request a PUT method with a file body
-        b = "one thing on top of another"
-        h = [("Content-Type", "text/plain"),
-             ("Content-Length", str(len(b)))]
-        self.getPage("/method/request_body", headers=h, method="PUT", body=b)
-        self.assertStatus(200)
-        self.assertBody(b)
-        
-        # Request a PUT method with a file body but no Content-Type.
-        # See http://www.cherrypy.org/ticket/790.
-        b = "one thing on top of another"
-        self.persistent = True
-        try:
-            conn = self.HTTP_CONN
-            conn.putrequest("PUT", "/method/request_body", skip_host=True)
-            conn.putheader("Host", self.HOST)
-            conn.putheader('Content-Length', str(len(b)))
-            conn.endheaders()
-            conn.send(b)
-            response = conn.response_class(conn.sock, method="PUT")
-            response.begin()
-            self.assertEqual(response.status, 200)
-            self.body = response.read()
-            self.assertBody(b)
-        finally:
-            self.persistent = False
-        
-        # Request a PUT method with no body whatsoever (not an empty one).
-        # See http://www.cherrypy.org/ticket/650.
-        # Provide a C-T or webtest will provide one (and a C-L) for us.
-        h = [("Content-Type", "text/plain")]
-        self.getPage("/method/reachable", headers=h, method="PUT")
-        self.assertStatus(411)
-        
-        # Request a custom method with a request body
-        b = ('<?xml version="1.0" encoding="utf-8" ?>\n\n'
-             '<propfind xmlns="DAV:"><prop><getlastmodified/>'
-             '</prop></propfind>')
-        h = [('Content-Type', 'text/xml'),
-             ('Content-Length', str(len(b)))]
-        self.getPage("/method/request_body", headers=h, method="PROPFIND", body=b)
-        self.assertStatus(200)
-        self.assertBody(b)
-        
-        # Request a disallowed method
-        self.getPage("/method/", method="LINK")
-        self.assertStatus(405)
-        
-        # Request an unknown method
-        self.getPage("/method/", method="SEARCH")
-        self.assertStatus(501)
-        
-        # For method dispatchers: make sure that an HTTP method doesn't
-        # collide with a virtual path atom. If you build HTTP-method
-        # dispatching into the core, rewrite these handlers to use
-        # your dispatch idioms.
-        self.getPage("/divorce/get?ID=13")
-        self.assertBody('Divorce document 13: empty')
-        self.assertStatus(200)
-        self.getPage("/divorce/", method="GET")
-        self.assertBody('<h1>Choose your document</h1>\n<ul>\n</ul>')
-        self.assertStatus(200)
-    
-    def test_CONNECT_method(self):
-        if getattr(cherrypy.server, "using_apache", False):
-            return self.skip("skipped due to known Apache differences... ")
-        
-        self.getPage("/method/", method="CONNECT")
-        self.assertBody("CONNECT")
-    
-    def testEmptyThreadlocals(self):
-        results = []
-        for x in xrange(20):
-            self.getPage("/threadlocal/")
-            results.append(self.body)
-        self.assertEqual(results, ["None"] * 20)
-
-
-if __name__ == '__main__':
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_routes.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-import cherrypy
-
-
-def setup_server():
-
-    class Dummy:
-        def index(self):
-            return "I said good day!"
-    
-    class City:
-        
-        def __init__(self, name):
-            self.name = name
-            self.population = 10000
-        
-        def index(self, **kwargs):
-            return "Welcome to %s, pop. %s" % (self.name, self.population)
-        index._cp_config = {'tools.response_headers.on': True,
-                            'tools.response_headers.headers': [('Content-Language', 'en-GB')]}
-        
-        def update(self, **kwargs):
-            self.population = kwargs['pop']
-            return "OK"
-        
-    d = cherrypy.dispatch.RoutesDispatcher()
-    d.connect(name='hounslow', route='hounslow', controller=City('Hounslow'))
-    d.connect(name='surbiton', route='surbiton', controller=City('Surbiton'),
-              action='index', conditions=dict(method=['GET']))
-    d.mapper.connect('surbiton', controller='surbiton',
-                     action='update', conditions=dict(method=['POST']))
-    d.connect('main', ':action', controller=Dummy())
-    
-    conf = {'/': {'request.dispatch': d}}
-    cherrypy.tree.mount(root=None, config=conf)
-
-
-from cherrypy.test import helper
-
-class RoutesDispatchTest(helper.CPWebCase):
-
-    def test_Routes_Dispatch(self):
-        self.getPage("/hounslow")
-        self.assertStatus("200 OK")
-        self.assertBody("Welcome to Hounslow, pop. 10000")
-        
-        self.getPage("/foo")
-        self.assertStatus("404 Not Found")
-        
-        self.getPage("/surbiton")
-        self.assertStatus("200 OK")
-        self.assertBody("Welcome to Surbiton, pop. 10000")
-        
-        self.getPage("/surbiton", method="POST", body="pop=1327")
-        self.assertStatus("200 OK")
-        self.assertBody("OK")
-        self.getPage("/surbiton")
-        self.assertStatus("200 OK")
-        self.assertHeader("Content-Language", "en-GB")
-        self.assertBody("Welcome to Surbiton, pop. 1327")
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_session.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,467 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-from httplib import HTTPConnection, HTTPSConnection
-import os
-localDir = os.path.dirname(__file__)
-import sys
-import threading
-import time
-
-import cherrypy
-from cherrypy.lib import sessions
-
-def http_methods_allowed(methods=['GET', 'HEAD']):
-    method = cherrypy.request.method.upper()
-    if method not in methods:
-        cherrypy.response.headers['Allow'] = ", ".join(methods)
-        raise cherrypy.HTTPError(405)
-
-cherrypy.tools.allow = cherrypy.Tool('on_start_resource', http_methods_allowed)
-
-
-def setup_server():
-    
-    class Root:
-        
-        _cp_config = {'tools.sessions.on': True,
-                      'tools.sessions.storage_type' : 'ram',
-                      'tools.sessions.storage_path' : localDir,
-                      'tools.sessions.timeout': (1.0 / 60),
-                      'tools.sessions.clean_freq': (1.0 / 60),
-                      }
-        
-        def clear(self):
-            cherrypy.session.cache.clear()
-        clear.exposed = True
-        
-        def data(self):
-            cherrypy.session['aha'] = 'foo'
-            return repr(cherrypy.session._data)
-        data.exposed = True
-        
-        def testGen(self):
-            counter = cherrypy.session.get('counter', 0) + 1
-            cherrypy.session['counter'] = counter
-            yield str(counter)
-        testGen.exposed = True
-        
-        def testStr(self):
-            counter = cherrypy.session.get('counter', 0) + 1
-            cherrypy.session['counter'] = counter
-            return str(counter)
-        testStr.exposed = True
-        
-        def setsessiontype(self, newtype):
-            self.__class__._cp_config.update({'tools.sessions.storage_type': newtype})
-            if hasattr(cherrypy, "session"):
-                del cherrypy.session
-            cls = getattr(sessions, newtype.title() + 'Session')
-            if cls.clean_thread:
-                cls.clean_thread.stop()
-                cls.clean_thread.unsubscribe()
-                del cls.clean_thread
-        setsessiontype.exposed = True
-        setsessiontype._cp_config = {'tools.sessions.on': False}
-        
-        def index(self):
-            sess = cherrypy.session
-            c = sess.get('counter', 0) + 1
-            time.sleep(0.01)
-            sess['counter'] = c
-            return str(c)
-        index.exposed = True
-        
-        def keyin(self, key):
-            return str(key in cherrypy.session)
-        keyin.exposed = True
-        
-        def delete(self):
-            cherrypy.session.delete()
-            sessions.expire()
-            return "done"
-        delete.exposed = True
-        
-        def delkey(self, key):
-            del cherrypy.session[key]
-            return "OK"
-        delkey.exposed = True
-        
-        def blah(self):
-            return self._cp_config['tools.sessions.storage_type']
-        blah.exposed = True
-        
-        def iredir(self):
-            raise cherrypy.InternalRedirect('/blah')
-        iredir.exposed = True
-        
-        def restricted(self):
-            return cherrypy.request.method
-        restricted.exposed = True
-        restricted._cp_config = {'tools.allow.on': True,
-                                 'tools.allow.methods': ['GET']}
-        
-        def regen(self):
-            cherrypy.tools.sessions.regenerate()
-            return "logged in"
-        regen.exposed = True
-        
-        def length(self):
-            return str(len(cherrypy.session))
-        length.exposed = True
-        
-        def session_cookie(self):
-            # Must load() to start the clean thread.
-            cherrypy.session.load()
-            return cherrypy.session.id
-        session_cookie.exposed = True
-        session_cookie._cp_config = {
-            'tools.sessions.path': '/session_cookie',
-            'tools.sessions.name': 'temp',
-            'tools.sessions.persistent': False}
-    
-    cherrypy.tree.mount(Root())
-
-
-from cherrypy.test import helper
-
-class SessionTest(helper.CPWebCase):
-    
-    def tearDown(self):
-        # Clean up sessions.
-        for fname in os.listdir(localDir):
-            if fname.startswith(sessions.FileSession.SESSION_PREFIX):
-                os.unlink(os.path.join(localDir, fname))
-    
-    def test_0_Session(self):
-        self.getPage('/setsessiontype/ram')
-        self.getPage('/clear')
-        
-        # Test that a normal request gets the same id in the cookies.
-        # Note: this wouldn't work if /data didn't load the session.
-        self.getPage('/data')
-        self.assertBody("{'aha': 'foo'}")
-        c = self.cookies[0]
-        self.getPage('/data', self.cookies)
-        self.assertEqual(self.cookies[0], c)
-        
-        self.getPage('/testStr')
-        self.assertBody('1')
-        cookie_parts = dict([p.strip().split('=')
-                             for p in self.cookies[0][1].split(";")])
-        # Assert there is an 'expires' param
-        self.assertEqual(set(cookie_parts.keys()),
-                         set(['session_id', 'expires', 'Path']))
-        self.getPage('/testGen', self.cookies)
-        self.assertBody('2')
-        self.getPage('/testStr', self.cookies)
-        self.assertBody('3')
-        self.getPage('/data', self.cookies)
-        self.assertBody("{'aha': 'foo', 'counter': 3}")
-        self.getPage('/length', self.cookies)
-        self.assertBody('2')
-        self.getPage('/delkey?key=counter', self.cookies)
-        self.assertStatus(200)
-        
-        self.getPage('/setsessiontype/file')
-        self.getPage('/testStr')
-        self.assertBody('1')
-        self.getPage('/testGen', self.cookies)
-        self.assertBody('2')
-        self.getPage('/testStr', self.cookies)
-        self.assertBody('3')
-        self.getPage('/delkey?key=counter', self.cookies)
-        self.assertStatus(200)
-        
-        # Wait for the session.timeout (1 second)
-        time.sleep(2)
-        self.getPage('/')
-        self.assertBody('1')
-        self.getPage('/length', self.cookies)
-        self.assertBody('1')
-        
-        # Test session __contains__
-        self.getPage('/keyin?key=counter', self.cookies)
-        self.assertBody("True")
-        cookieset1 = self.cookies
-        
-        # Make a new session and test __len__ again
-        self.getPage('/')
-        self.getPage('/length', self.cookies)
-        self.assertBody('2')
-        
-        # Test session delete
-        self.getPage('/delete', self.cookies)
-        self.assertBody("done")
-        self.getPage('/delete', cookieset1)
-        self.assertBody("done")
-        f = lambda: [x for x in os.listdir(localDir) if x.startswith('session-')]
-        self.assertEqual(f(), [])
-        
-        # Wait for the cleanup thread to delete remaining session files
-        self.getPage('/')
-        f = lambda: [x for x in os.listdir(localDir) if x.startswith('session-')]
-        self.assertNotEqual(f(), [])
-        time.sleep(2)
-        self.assertEqual(f(), [])
-    
-    def test_1_Ram_Concurrency(self):
-        self.getPage('/setsessiontype/ram')
-        self._test_Concurrency()
-    
-    def test_2_File_Concurrency(self):
-        self.getPage('/setsessiontype/file')
-        self._test_Concurrency()
-    
-    def _test_Concurrency(self):
-        client_thread_count = 5
-        request_count = 30
-        
-        # Get initial cookie
-        self.getPage("/")
-        self.assertBody("1")
-        cookies = self.cookies
-        
-        data_dict = {}
-        errors = []
-        
-        def request(index):
-            if self.scheme == 'https':
-                c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
-            else:
-                c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
-            for i in range(request_count):
-                c.putrequest('GET', '/')
-                for k, v in cookies:
-                    c.putheader(k, v)
-                c.endheaders()
-                response = c.getresponse()
-                body = response.read()
-                if response.status != 200 or not body.isdigit():
-                    errors.append((response.status, body))
-                else:
-                    data_dict[index] = max(data_dict[index], int(body))
-                # Uncomment the following line to prove threads overlap.
-##                print index,
-        
-        # Start <request_count> requests from each of
-        # <client_thread_count> concurrent clients
-        ts = []
-        for c in range(client_thread_count):
-            data_dict[c] = 0
-            t = threading.Thread(target=request, args=(c,))
-            ts.append(t)
-            t.start()
-        
-        for t in ts:
-            t.join()
-        
-        hitcount = max(data_dict.values())
-        expected = 1 + (client_thread_count * request_count)
-        
-        for e in errors:
-            print(e)
-        self.assertEqual(hitcount, expected)
-    
-    def test_3_Redirect(self):
-        # Start a new session
-        self.getPage('/testStr')
-        self.getPage('/iredir', self.cookies)
-        self.assertBody("file")
-    
-    def test_4_File_deletion(self):
-        # Start a new session
-        self.getPage('/testStr')
-        # Delete the session file manually and retry.
-        id = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
-        path = os.path.join(localDir, "session-" + id)
-        os.unlink(path)
-        self.getPage('/testStr', self.cookies)
-    
-    def test_5_Error_paths(self):
-        self.getPage('/unknown/page')
-        self.assertErrorPage(404, "The path '/unknown/page' was not found.")
-        
-        # Note: this path is *not* the same as above. The above
-        # takes a normal route through the session code; this one
-        # skips the session code's before_handler and only calls
-        # before_finalize (save) and on_end (close). So the session
-        # code has to survive calling save/close without init.
-        self.getPage('/restricted', self.cookies, method='POST')
-        self.assertErrorPage(405, "Specified method is invalid for this server.")
-    
-    def test_6_regenerate(self):
-        self.getPage('/testStr')
-        # grab the cookie ID
-        id1 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
-        self.getPage('/regen')
-        self.assertBody('logged in')
-        id2 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
-        self.assertNotEqual(id1, id2)
-        
-        self.getPage('/testStr')
-        # grab the cookie ID
-        id1 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
-        self.getPage('/testStr',
-                     headers=[('Cookie',
-                               'session_id=maliciousid; '
-                               'expires=Sat, 27 Oct 2017 04:18:28 GMT; Path=/;')])
-        id2 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
-        self.assertNotEqual(id1, id2)
-        self.assertNotEqual(id2, 'maliciousid')
-    
-    def test_7_session_cookies(self):
-        self.getPage('/setsessiontype/ram')
-        self.getPage('/clear')
-        self.getPage('/session_cookie')
-        # grab the cookie ID
-        cookie_parts = dict([p.strip().split('=') for p in self.cookies[0][1].split(";")])
-        # Assert there is no 'expires' param
-        self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
-        id1 = cookie_parts['temp']
-        self.assertEqual(sessions.RamSession.cache.keys(), [id1])
-        
-        # Send another request in the same "browser session".
-        self.getPage('/session_cookie', self.cookies)
-        cookie_parts = dict([p.strip().split('=') for p in self.cookies[0][1].split(";")])
-        # Assert there is no 'expires' param
-        self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
-        self.assertBody(id1)
-        self.assertEqual(sessions.RamSession.cache.keys(), [id1])
-        
-        # Simulate a browser close by just not sending the cookies
-        self.getPage('/session_cookie')
-        # grab the cookie ID
-        cookie_parts = dict([p.strip().split('=') for p in self.cookies[0][1].split(";")])
-        # Assert there is no 'expires' param
-        self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
-        # Assert a new id has been generated...
-        id2 = cookie_parts['temp']
-        self.assertNotEqual(id1, id2)
-        self.assertEqual(set(sessions.RamSession.cache.keys()), set([id1, id2]))
-        
-        # Wait for the session.timeout on both sessions
-        time.sleep(2.5)
-        cache = sessions.RamSession.cache.keys()
-        if cache:
-            if cache == [id2]:
-                self.fail("The second session did not time out.")
-            else:
-                self.fail("Unknown session id in cache: %r", cache)
-
-
-import socket
-try:
-    import memcache
-    
-    host, port = '127.0.0.1', 11211
-    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                  socket.SOCK_STREAM):
-        af, socktype, proto, canonname, sa = res
-        s = None
-        try:
-            s = socket.socket(af, socktype, proto)
-            # See http://groups.google.com/group/cherrypy-users/
-            #        browse_frm/thread/bbfe5eb39c904fe0
-            s.settimeout(1.0)
-            s.connect((host, port))
-            s.close()
-        except socket.error:
-            if s:
-                s.close()
-            raise
-        break
-except (ImportError, socket.error):
-    class MemcachedSessionTest(helper.CPWebCase):
-        
-        def test(self):
-            return self.skip("memcached not reachable ")
-else:
-    class MemcachedSessionTest(helper.CPWebCase):
-        
-        def test_0_Session(self):
-            self.getPage('/setsessiontype/memcached')
-            
-            self.getPage('/testStr')
-            self.assertBody('1')
-            self.getPage('/testGen', self.cookies)
-            self.assertBody('2')
-            self.getPage('/testStr', self.cookies)
-            self.assertBody('3')
-            self.getPage('/length', self.cookies)
-            self.assertErrorPage(500)
-            self.assertInBody("NotImplementedError")
-            self.getPage('/delkey?key=counter', self.cookies)
-            self.assertStatus(200)
-            
-            # Wait for the session.timeout (1 second)
-            time.sleep(1.25)
-            self.getPage('/')
-            self.assertBody('1')
-            
-            # Test session __contains__
-            self.getPage('/keyin?key=counter', self.cookies)
-            self.assertBody("True")
-            
-            # Test session delete
-            self.getPage('/delete', self.cookies)
-            self.assertBody("done")
-        
-        def test_1_Concurrency(self):
-            client_thread_count = 5
-            request_count = 30
-            
-            # Get initial cookie
-            self.getPage("/")
-            self.assertBody("1")
-            cookies = self.cookies
-            
-            data_dict = {}
-            
-            def request(index):
-                for i in range(request_count):
-                    self.getPage("/", cookies)
-                    # Uncomment the following line to prove threads overlap.
-##                    print index,
-                if not self.body.isdigit():
-                    self.fail(self.body)
-                data_dict[index] = v = int(self.body)
-            
-            # Start <request_count> concurrent requests from
-            # each of <client_thread_count> clients
-            ts = []
-            for c in range(client_thread_count):
-                data_dict[c] = 0
-                t = threading.Thread(target=request, args=(c,))
-                ts.append(t)
-                t.start()
-            
-            for t in ts:
-                t.join()
-            
-            hitcount = max(data_dict.values())
-            expected = 1 + (client_thread_count * request_count)
-            self.assertEqual(hitcount, expected)
-        
-        def test_3_Redirect(self):
-            # Start a new session
-            self.getPage('/testStr')
-            self.getPage('/iredir', self.cookies)
-            self.assertBody("memcached")
-        
-        def test_5_Error_paths(self):
-            self.getPage('/unknown/page')
-            self.assertErrorPage(404, "The path '/unknown/page' was not found.")
-            
-            # Note: this path is *not* the same as above. The above
-            # takes a normal route through the session code; this one
-            # skips the session code's before_handler and only calls
-            # before_finalize (save) and on_end (close). So the session
-            # code has to survive calling save/close without init.
-            self.getPage('/restricted', self.cookies, method='POST')
-            self.assertErrorPage(405, "Specified method is invalid for this server.")
-
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_sessionauthenticate.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-
-def setup_server():
-    
-    def check(username, password):
-        # Dummy check_username_and_password function
-        if username != 'test' or password != 'password':
-            return u'Wrong login/password'
-    
-    def augment_params():
-        # A simple tool to add some things to request.params
-        # This is to check to make sure that session_auth can handle request
-        # params (ticket #780)
-        cherrypy.request.params["test"] = "test"
-
-    cherrypy.tools.augment_params = cherrypy.Tool('before_handler',
-             augment_params, None, priority=30)
-
-    class Test:
-        
-        _cp_config = {'tools.sessions.on': True,
-                      'tools.session_auth.on': True,
-                      'tools.session_auth.check_username_and_password': check,
-                      'tools.augment_params.on': True,
-                      }
-        
-        def index(self, **kwargs):
-            return "Hi %s, you are logged in" % cherrypy.request.login
-        index.exposed = True
-    
-    cherrypy.tree.mount(Test())
-
-
-from cherrypy.test import helper
-
-
-class SessionAuthenticateTest(helper.CPWebCase):
-    
-    def testSessionAuthenticate(self):
-        # request a page and check for login form
-        self.getPage('/')
-        self.assertInBody('<form method="post" action="do_login">')
-        
-        # setup credentials
-        login_body = 'username=test&password=password&from_page=/'
-        
-        # attempt a login
-        self.getPage('/do_login', method='POST', body=login_body)
-        self.assertStatus((302, 303))
-        
-        # get the page now that we are logged in
-        self.getPage('/', self.cookies)
-        self.assertBody('Hi test, you are logged in')
-        
-        # do a logout
-        self.getPage('/do_logout', self.cookies, method='POST')
-        self.assertStatus((302, 303))
-        
-        # verify we are logged out
-        self.getPage('/', self.cookies)
-        self.assertInBody('<form method="post" action="do_login">')
-
-
-if __name__ == "__main__":
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_states.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,445 +0,0 @@
-from httplib import BadStatusLine
-
-import os
-import sys
-import threading
-import time
-
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-engine = cherrypy.engine
-thisdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-class Dependency:
-    
-    def __init__(self, bus):
-        self.bus = bus
-        self.running = False
-        self.startcount = 0
-        self.gracecount = 0
-        self.threads = {}
-    
-    def subscribe(self):
-        self.bus.subscribe('start', self.start)
-        self.bus.subscribe('stop', self.stop)
-        self.bus.subscribe('graceful', self.graceful)
-        self.bus.subscribe('start_thread', self.startthread)
-        self.bus.subscribe('stop_thread', self.stopthread)
-    
-    def start(self):
-        self.running = True
-        self.startcount += 1
-    
-    def stop(self):
-        self.running = False
-    
-    def graceful(self):
-        self.gracecount += 1
-    
-    def startthread(self, thread_id):
-        self.threads[thread_id] = None
-    
-    def stopthread(self, thread_id):
-        del self.threads[thread_id]
-
-db_connection = Dependency(engine)
-
-def setup_server():
-    class Root:
-        def index(self):
-            return "Hello World"
-        index.exposed = True
-        
-        def ctrlc(self):
-            raise KeyboardInterrupt()
-        ctrlc.exposed = True
-        
-        def graceful(self):
-            engine.graceful()
-            return "app was (gracefully) restarted succesfully"
-        graceful.exposed = True
-        
-        def block_explicit(self):
-            while True:
-                if cherrypy.response.timed_out:
-                    cherrypy.response.timed_out = False
-                    return "broken!"
-                time.sleep(0.01)
-        block_explicit.exposed = True
-        
-        def block_implicit(self):
-            time.sleep(0.5)
-            return "response.timeout = %s" % cherrypy.response.timeout
-        block_implicit.exposed = True
-
-    cherrypy.tree.mount(Root())
-    cherrypy.config.update({
-        'environment': 'test_suite',
-        'engine.deadlock_poll_freq': 0.1,
-        })
-
-    db_connection.subscribe()
-
-
-
-# ------------ Enough helpers. Time for real live test cases. ------------ #
-
-
-from cherrypy.test import helper
-
-class ServerStateTests(helper.CPWebCase):
-    
-    def setUp(self):
-        cherrypy.server.socket_timeout = 0.1
-    
-    def test_0_NormalStateFlow(self):
-        engine.stop()
-        # Our db_connection should not be running
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(db_connection.startcount, 1)
-        self.assertEqual(len(db_connection.threads), 0)
-        
-        # Test server start
-        engine.start()
-        self.assertEqual(engine.state, engine.states.STARTED)
-        
-        host = cherrypy.server.socket_host
-        port = cherrypy.server.socket_port
-        self.assertRaises(IOError, cherrypy._cpserver.check_port, host, port)
-        
-        # The db_connection should be running now
-        self.assertEqual(db_connection.running, True)
-        self.assertEqual(db_connection.startcount, 2)
-        self.assertEqual(len(db_connection.threads), 0)
-        
-        self.getPage("/")
-        self.assertBody("Hello World")
-        self.assertEqual(len(db_connection.threads), 1)
-        
-        # Test engine stop. This will also stop the HTTP server.
-        engine.stop()
-        self.assertEqual(engine.state, engine.states.STOPPED)
-        
-        # Verify that our custom stop function was called
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(len(db_connection.threads), 0)
-        
-        # Block the main thread now and verify that exit() works.
-        def exittest():
-            self.getPage("/")
-            self.assertBody("Hello World")
-            engine.exit()
-        cherrypy.server.start()
-        engine.start_with_callback(exittest)
-        engine.block()
-        self.assertEqual(engine.state, engine.states.EXITING)
-    
-    def test_1_Restart(self):
-        cherrypy.server.start()
-        engine.start()
-        
-        # The db_connection should be running now
-        self.assertEqual(db_connection.running, True)
-        grace = db_connection.gracecount
-        
-        self.getPage("/")
-        self.assertBody("Hello World")
-        self.assertEqual(len(db_connection.threads), 1)
-        
-        # Test server restart from this thread
-        engine.graceful()
-        self.assertEqual(engine.state, engine.states.STARTED)
-        self.getPage("/")
-        self.assertBody("Hello World")
-        self.assertEqual(db_connection.running, True)
-        self.assertEqual(db_connection.gracecount, grace + 1)
-        self.assertEqual(len(db_connection.threads), 1)
-        
-        # Test server restart from inside a page handler
-        self.getPage("/graceful")
-        self.assertEqual(engine.state, engine.states.STARTED)
-        self.assertBody("app was (gracefully) restarted succesfully")
-        self.assertEqual(db_connection.running, True)
-        self.assertEqual(db_connection.gracecount, grace + 2)
-        # Since we are requesting synchronously, is only one thread used?
-        # Note that the "/graceful" request has been flushed.
-        self.assertEqual(len(db_connection.threads), 0)
-        
-        engine.stop()
-        self.assertEqual(engine.state, engine.states.STOPPED)
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(len(db_connection.threads), 0)
-    
-    def test_2_KeyboardInterrupt(self):
-        # Raise a keyboard interrupt in the HTTP server's main thread.
-        # We must start the server in this, the main thread
-        engine.start()
-        cherrypy.server.start()
-        
-        self.persistent = True
-        try:
-            # Make the first request and assert there's no "Connection: close".
-            self.getPage("/")
-            self.assertStatus('200 OK')
-            self.assertBody("Hello World")
-            self.assertNoHeader("Connection")
-            
-            cherrypy.server.httpserver.interrupt = KeyboardInterrupt
-            engine.block()
-            
-            self.assertEqual(db_connection.running, False)
-            self.assertEqual(len(db_connection.threads), 0)
-            self.assertEqual(engine.state, engine.states.EXITING)
-        finally:
-            self.persistent = False
-        
-        # Raise a keyboard interrupt in a page handler; on multithreaded
-        # servers, this should occur in one of the worker threads.
-        # This should raise a BadStatusLine error, since the worker
-        # thread will just die without writing a response.
-        engine.start()
-        cherrypy.server.start()
-        
-        try:
-            self.getPage("/ctrlc")
-        except BadStatusLine:
-            pass
-        else:
-            print(self.body)
-            self.fail("AssertionError: BadStatusLine not raised")
-        
-        engine.block()
-        self.assertEqual(db_connection.running, False)
-        self.assertEqual(len(db_connection.threads), 0)
-    
-    def test_3_Deadlocks(self):
-        cherrypy.config.update({'response.timeout': 0.2})
-        
-        engine.start()
-        cherrypy.server.start()
-        try:
-            self.assertNotEqual(engine.timeout_monitor.thread, None)
-            
-            # Request a "normal" page.
-            self.assertEqual(engine.timeout_monitor.servings, [])
-            self.getPage("/")
-            self.assertBody("Hello World")
-            # request.close is called async.
-            while engine.timeout_monitor.servings:
-                print ".",
-                time.sleep(0.01)
-            
-            # Request a page that explicitly checks itself for deadlock.
-            # The deadlock_timeout should be 2 secs.
-            self.getPage("/block_explicit")
-            self.assertBody("broken!")
-            
-            # Request a page that implicitly breaks deadlock.
-            # If we deadlock, we want to touch as little code as possible,
-            # so we won't even call handle_error, just bail ASAP.
-            self.getPage("/block_implicit")
-            self.assertStatus(500)
-            self.assertInBody("raise cherrypy.TimeoutError()")
-        finally:
-            engine.exit()
-    
-    def test_4_Autoreload(self):
-        # Start the demo script in a new process
-        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
-        p.write_conf(
-                extra='test_case_name: "test_4_Autoreload"')
-        p.start(imports='cherrypy.test.test_states_demo')
-        try:
-            self.getPage("/start")
-            start = float(self.body)
-            
-            # Give the autoreloader time to cache the file time.
-            time.sleep(2)
-            
-            # Touch the file
-            os.utime(os.path.join(thisdir, "test_states_demo.py"), None)
-            
-            # Give the autoreloader time to re-exec the process
-            time.sleep(2)
-            host = cherrypy.server.socket_host
-            port = cherrypy.server.socket_port
-            cherrypy._cpserver.wait_for_occupied_port(host, port)
-            
-            self.getPage("/start")
-            self.assert_(float(self.body) > start)
-        finally:
-            # Shut down the spawned process
-            self.getPage("/exit")
-        p.join()
-    
-    def test_5_Start_Error(self):
-        # If a process errors during start, it should stop the engine
-        # and exit with a non-zero exit code.
-        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
-                             wait=True)
-        p.write_conf(
-                extra="""starterror: True
-test_case_name: "test_5_Start_Error"
-"""
-        )
-        p.start(imports='cherrypy.test.test_states_demo')
-        if p.exit_code == 0:
-            self.fail("Process failed to return nonzero exit code.")
-
-
-class PluginTests(helper.CPWebCase):
-    
-    def test_daemonize(self):
-        if os.name not in ['posix']: 
-            return self.skip("skipped (not on posix) ")
-        self.HOST = '127.0.0.1'
-        self.PORT = 8081
-        # Spawn the process and wait, when this returns, the original process
-        # is finished.  If it daemonized properly, we should still be able
-        # to access pages.
-        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
-                             wait=True, daemonize=True,
-                             socket_host='127.0.0.1',
-                             socket_port=8081)
-        p.write_conf(
-             extra='test_case_name: "test_daemonize"')
-        p.start(imports='cherrypy.test.test_states_demo')
-        try:
-            # Just get the pid of the daemonization process.
-            self.getPage("/pid")
-            self.assertStatus(200)
-            page_pid = int(self.body)
-            self.assertEqual(page_pid, p.get_pid())
-        finally:
-            # Shut down the spawned process
-            self.getPage("/exit")
-        p.join()
-        
-        # Wait until here to test the exit code because we want to ensure
-        # that we wait for the daemon to finish running before we fail.
-        if p.exit_code != 0:
-            self.fail("Daemonized parent process failed to exit cleanly.")
-
-
-class SignalHandlingTests(helper.CPWebCase):
-    
-    def test_SIGHUP_tty(self):
-        # When not daemonized, SIGHUP should shut down the server.
-        try:
-            from signal import SIGHUP
-        except ImportError:
-            return self.skip("skipped (no SIGHUP) ")
-        
-        # Spawn the process.
-        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
-        p.write_conf(
-                extra='test_case_name: "test_SIGHUP_tty"')
-        p.start(imports='cherrypy.test.test_states_demo')
-        # Send a SIGHUP
-        os.kill(p.get_pid(), SIGHUP)
-        # This might hang if things aren't working right, but meh.
-        p.join()
-    
-    def test_SIGHUP_daemonized(self):
-        # When daemonized, SIGHUP should restart the server.
-        try:
-            from signal import SIGHUP
-        except ImportError:
-            return self.skip("skipped (no SIGHUP) ")
-        
-        if os.name not in ['posix']: 
-            return self.skip("skipped (not on posix) ")
-        
-        # Spawn the process and wait, when this returns, the original process
-        # is finished.  If it daemonized properly, we should still be able
-        # to access pages.
-        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
-                             wait=True, daemonize=True)
-        p.write_conf(
-             extra='test_case_name: "test_SIGHUP_daemonized"')
-        p.start(imports='cherrypy.test.test_states_demo')
-        
-        pid = p.get_pid()
-        try:
-            # Send a SIGHUP
-            os.kill(pid, SIGHUP)
-            # Give the server some time to restart
-            time.sleep(2)
-            self.getPage("/pid")
-            self.assertStatus(200)
-            new_pid = int(self.body)
-            self.assertNotEqual(new_pid, pid)
-        finally:
-            # Shut down the spawned process
-            self.getPage("/exit")
-        p.join()
-    
-    def test_SIGTERM(self):
-        # SIGTERM should shut down the server whether daemonized or not.
-        try:
-            from signal import SIGTERM
-        except ImportError:
-            return self.skip("skipped (no SIGTERM) ")
-        
-        try:
-            from os import kill
-        except ImportError:
-            return self.skip("skipped (no os.kill) ")
-        
-        # Spawn a normal, undaemonized process.
-        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
-        p.write_conf(
-                extra='test_case_name: "test_SIGTERM"')
-        p.start(imports='cherrypy.test.test_states_demo')
-        # Send a SIGTERM
-        os.kill(p.get_pid(), SIGTERM)
-        # This might hang if things aren't working right, but meh.
-        p.join()
-        
-        if os.name in ['posix']: 
-            # Spawn a daemonized process and test again.
-            p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
-                                 wait=True, daemonize=True)
-            p.write_conf(
-                 extra='test_case_name: "test_SIGTERM_2"')
-            p.start(imports='cherrypy.test.test_states_demo')
-            # Send a SIGTERM
-            os.kill(p.get_pid(), SIGTERM)
-            # This might hang if things aren't working right, but meh.
-            p.join()
-    
-    def test_signal_handler_unsubscribe(self):
-        try:
-            from signal import SIGTERM
-        except ImportError:
-            return self.skip("skipped (no SIGTERM) ")
-        
-        try:
-            from os import kill
-        except ImportError:
-            return self.skip("skipped (no os.kill) ")
-        
-        # Spawn a normal, undaemonized process.
-        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
-        p.write_conf(
-            extra="""unsubsig: True
-test_case_name: "test_signal_handler_unsubscribe"
-""")
-        p.start(imports='cherrypy.test.test_states_demo')
-        # Send a SIGTERM
-        os.kill(p.get_pid(), SIGTERM)
-        # This might hang if things aren't working right, but meh.
-        p.join()
-        
-        # Assert the old handler ran.
-        target_line = open(p.error_log, 'rb').readlines()[-10]
-        if not "I am an old SIGTERM handler." in target_line:
-            self.fail("Old SIGTERM handler did not run.\n%r" % target_line)
-
-
-if __name__ == "__main__":
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_states_demo.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-import os
-import sys
-import time
-starttime = time.time()
-
-import cherrypy
-
-
-class Root:
-    
-    def index(self):
-        return "Hello World"
-    index.exposed = True
-    
-    def mtimes(self):
-        return repr(cherrypy.engine.publish("Autoreloader", "mtimes"))
-    mtimes.exposed = True
-    
-    def pid(self):
-        return str(os.getpid())
-    pid.exposed = True
-    
-    def start(self):
-        return repr(starttime)
-    start.exposed = True
-    
-    def exit(self):
-        # This handler might be called before the engine is STARTED if an
-        # HTTP worker thread handles it before the HTTP server returns
-        # control to engine.start. We avoid that race condition here
-        # by waiting for the Bus to be STARTED.
-        cherrypy.engine.wait(state=cherrypy.engine.states.STARTED)
-        cherrypy.engine.exit()
-    exit.exposed = True
-    
-
-def unsub_sig():
-    cherrypy.log("unsubsig: %s" % cherrypy.config.get('unsubsig', False))
-    if cherrypy.config.get('unsubsig', False):
-        cherrypy.log("Unsubscribing the default cherrypy signal handler")
-        cherrypy.engine.signal_handler.unsubscribe()
-    try:
-        from signal import signal, SIGTERM
-    except ImportError:
-        pass
-    else:
-        def old_term_handler(signum=None, frame=None):
-            cherrypy.log("I am an old SIGTERM handler.")
-            sys.exit(0)
-        cherrypy.log("Subscribing the new one.")
-        signal(SIGTERM, old_term_handler)
-cherrypy.engine.subscribe('start', unsub_sig, priority=100)
-
-
-def starterror():
-    if cherrypy.config.get('starterror', False):
-        zerodiv = 1 / 0
-cherrypy.engine.subscribe('start', starterror, priority=6)
-
-def log_test_case_name():
-    if cherrypy.config.get('test_case_name', False):
-        cherrypy.log("STARTED FROM: %s" % cherrypy.config.get('test_case_name'))
-cherrypy.engine.subscribe('start', log_test_case_name, priority=6)
-
-
-cherrypy.tree.mount(Root(), '/', {'/': {}})
--- a/bundled/cherrypy/cherrypy/test/test_static.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-from httplib import HTTPConnection, HTTPSConnection
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-has_space_filepath = os.path.join(curdir, 'static', 'has space.html')
-bigfile_filepath = os.path.join(curdir, "static", "bigfile.log")
-BIGFILE_SIZE = 1024 * 1024
-import threading
-
-import cherrypy
-from cherrypy.lib import static
-
-def setup_server():
-    if not os.path.exists(has_space_filepath):
-        open(has_space_filepath, 'wb').write('Hello, world\r\n')
-    if not os.path.exists(bigfile_filepath):
-        open(bigfile_filepath, 'wb').write("x" * BIGFILE_SIZE)
-    
-    class Root:
-        
-        def bigfile(self):
-            from cherrypy.lib import static
-            self.f = static.serve_file(bigfile_filepath)
-            return self.f
-        bigfile.exposed = True
-        bigfile._cp_config = {'response.stream': True}
-        
-        def tell(self):
-            if self.f.input.closed:
-                return ''
-            return repr(self.f.input.tell()).rstrip('L')
-        tell.exposed = True
-        
-        def fileobj(self):
-            f = open(os.path.join(curdir, 'style.css'), 'rb')
-            return static.serve_fileobj(f, content_type='text/css')
-        fileobj.exposed = True
-        
-        def stringio(self):
-            f = StringIO.StringIO('Fee\nfie\nfo\nfum')
-            return static.serve_fileobj(f, content_type='text/plain')
-        stringio.exposed = True
-    
-    class Static:
-        
-        def index(self):
-            return 'You want the Baron? You can have the Baron!'
-        index.exposed = True
-        
-        def dynamic(self):
-            return "This is a DYNAMIC page"
-        dynamic.exposed = True
-    
-    
-    root = Root()
-    root.static = Static()
-    
-    rootconf = {
-        '/static': {
-            'tools.staticdir.on': True,
-            'tools.staticdir.dir': 'static',
-            'tools.staticdir.root': curdir,
-        },
-        '/style.css': {
-            'tools.staticfile.on': True,
-            'tools.staticfile.filename': os.path.join(curdir, 'style.css'),
-        },
-        '/docroot': {
-            'tools.staticdir.on': True,
-            'tools.staticdir.root': curdir,
-            'tools.staticdir.dir': 'static',
-            'tools.staticdir.index': 'index.html',
-        },
-        '/error': {
-            'tools.staticdir.on': True,
-            'request.show_tracebacks': True,
-        },
-        }
-    rootApp = cherrypy.Application(root)
-    rootApp.merge(rootconf)
-    
-    test_app_conf = {
-        '/test': {
-            'tools.staticdir.index': 'index.html',
-            'tools.staticdir.on': True,
-            'tools.staticdir.root': curdir,
-            'tools.staticdir.dir': 'static',
-            },
-        }
-    testApp = cherrypy.Application(Static())
-    testApp.merge(test_app_conf)
-    
-    vhost = cherrypy._cpwsgi.VirtualHost(rootApp, {'virt.net': testApp})
-    cherrypy.tree.graft(vhost)
-
-
-def teardown_server():
-    for f in (has_space_filepath, bigfile_filepath):
-        if os.path.exists(f):
-            try:
-                os.unlink(f)
-            except:
-                pass
-
-
-
-from cherrypy.test import helper
-
-class StaticTest(helper.CPWebCase):
-    
-    def testStatic(self):
-        self.getPage("/static/index.html")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-        
-        # Using a staticdir.root value in a subdir...
-        self.getPage("/docroot/index.html")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-        
-        # Check a filename with spaces in it
-        self.getPage("/static/has%20space.html")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-        
-        self.getPage("/style.css")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/css')
-        # Note: The body should be exactly 'Dummy stylesheet\n', but
-        #   unfortunately some tools such as WinZip sometimes turn \n
-        #   into \r\n on Windows when extracting the CherryPy tarball so
-        #   we just check the content
-        self.assertMatchesBody('^Dummy stylesheet')
-    
-    def test_fallthrough(self):
-        # Test that NotFound will then try dynamic handlers (see [878]).
-        self.getPage("/static/dynamic")
-        self.assertBody("This is a DYNAMIC page")
-        
-        # Check a directory via fall-through to dynamic handler.
-        self.getPage("/static/")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
-        self.assertBody('You want the Baron? You can have the Baron!')
-    
-    def test_index(self):
-        # Check a directory via "staticdir.index".
-        self.getPage("/docroot/")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/html')
-        self.assertBody('Hello, world\r\n')
-        # The same page should be returned even if redirected.
-        self.getPage("/docroot")
-        self.assertStatus(301)
-        self.assertHeader('Location', '%s/docroot/' % self.base())
-        self.assertMatchesBody("This resource .* <a href='%s/docroot/'>"
-                               "%s/docroot/</a>." % (self.base(), self.base()))
-    
-    def test_config_errors(self):
-        # Check that we get an error if no .file or .dir
-        self.getPage("/error/thing.html")
-        self.assertErrorPage(500)
-        self.assertInBody("TypeError: staticdir() takes at least 2 "
-                          "arguments (0 given)")
-    
-    def test_security(self):
-        # Test up-level security
-        self.getPage("/static/../../test/style.css")
-        self.assertStatus((400, 403))
-    
-    def test_modif(self):
-        # Test modified-since on a reasonably-large file
-        self.getPage("/static/dirback.jpg")
-        self.assertStatus("200 OK")
-        lastmod = ""
-        for k, v in self.headers:
-            if k == 'Last-Modified':
-                lastmod = v
-        ims = ("If-Modified-Since", lastmod)
-        self.getPage("/static/dirback.jpg", headers=[ims])
-        self.assertStatus(304)
-        self.assertNoHeader("Content-Type")
-        self.assertNoHeader("Content-Length")
-        self.assertNoHeader("Content-Disposition")
-        self.assertBody("")
-    
-    def test_755_vhost(self):
-        self.getPage("/test/", [('Host', 'virt.net')])
-        self.assertStatus(200)
-        self.getPage("/test", [('Host', 'virt.net')])
-        self.assertStatus(301)
-        self.assertHeader('Location', self.scheme + '://virt.net/test/')
-    
-    def test_serve_fileobj(self):
-        self.getPage("/fileobj")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/css;charset=utf-8')
-        self.assertMatchesBody('^Dummy stylesheet')
-    
-    def test_serve_stringio(self):
-        self.getPage("/stringio")
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-        self.assertHeader('Content-Length', 14)
-        self.assertMatchesBody('Fee\nfie\nfo\nfum')
-    
-    def test_file_stream(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        # Make an initial request
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest("GET", "/bigfile", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 200)
-        
-        body = ''
-        remaining = BIGFILE_SIZE
-        while remaining > 0:
-            data = response.fp.read(65536)
-            if not data:
-                break
-            body += data
-            remaining -= len(data)
-            
-            if self.scheme == "https":
-                newconn = HTTPSConnection
-            else:
-                newconn = HTTPConnection
-            s, h, b = helper.webtest.openURL(
-                "/tell", headers=[], host=self.HOST, port=self.PORT,
-                http_conn=newconn)
-            if not b:
-                # The file was closed on the server.
-                tell_position = BIGFILE_SIZE
-            else:
-                tell_position = int(b)
-            
-            expected = len(body)
-            if tell_position >= BIGFILE_SIZE:
-                # We can't exactly control how much content the server asks for.
-                # Fudge it by only checking the first half of the reads.
-                if expected < (BIGFILE_SIZE / 2):
-                    self.fail(
-                        "The file should have advanced to position %r, but has "
-                        "already advanced to the end of the file. It may not be "
-                        "streamed as intended, or at the wrong chunk size (64k)" %
-                        expected)
-            elif tell_position < expected:
-                self.fail(
-                    "The file should have advanced to position %r, but has "
-                    "only advanced to position %r. It may not be streamed "
-                    "as intended, or at the wrong chunk size (65536)" %
-                    (expected, tell_position))
-        
-        if body != "x" * BIGFILE_SIZE:
-            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
-                      (BIGFILE_SIZE, body[:50], len(body)))
-        conn.close()
-    
-    def test_file_stream_deadlock(self):
-        if cherrypy.server.protocol_version != "HTTP/1.1":
-            return self.skip()
-        
-        self.PROTOCOL = "HTTP/1.1"
-        
-        # Make an initial request but abort early.
-        self.persistent = True
-        conn = self.HTTP_CONN
-        conn.putrequest("GET", "/bigfile", skip_host=True)
-        conn.putheader("Host", self.HOST)
-        conn.endheaders()
-        response = conn.response_class(conn.sock, method="GET")
-        response.begin()
-        self.assertEqual(response.status, 200)
-        body = response.fp.read(65536)
-        if body != "x" * 65536:
-            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
-                      (65536, body[:50], len(body)))
-        response.close()
-        conn.close()
-        
-        # Make a second request, which should fetch the whole file.
-        self.persistent = False
-        self.getPage("/bigfile")
-        if self.body != "x" * BIGFILE_SIZE:
-            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
-                      (BIGFILE_SIZE, self.body[:50], len(body)))
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_tools.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,405 +0,0 @@
-"""Test the various means of instantiating and invoking tools."""
-
-import gzip
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-import sys
-from httplib import IncompleteRead
-import time
-timeout = 0.2
-
-import types
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-from cherrypy import tools
-
-
-europoundUnicode = u'\x80\xa3'
-
-def setup_server():
-    
-    # Put check_access in a custom toolbox with its own namespace
-    myauthtools = cherrypy._cptools.Toolbox("myauth")
-    
-    def check_access(default=False):
-        if not getattr(cherrypy.request, "userid", default):
-            raise cherrypy.HTTPError(401)
-    myauthtools.check_access = cherrypy.Tool('before_request_body', check_access)
-    
-    def numerify():
-        def number_it(body):
-            for chunk in body:
-                for k, v in cherrypy.request.numerify_map:
-                    chunk = chunk.replace(k, v)
-                yield chunk
-        cherrypy.response.body = number_it(cherrypy.response.body)
-    
-    class NumTool(cherrypy.Tool):
-        def _setup(self):
-            def makemap():
-                m = self._merged_args().get("map", {})
-                cherrypy.request.numerify_map = m.items()
-            cherrypy.request.hooks.attach('on_start_resource', makemap)
-            
-            def critical():
-                cherrypy.request.error_response = cherrypy.HTTPError(502).set_response
-            critical.failsafe = True
-            
-            cherrypy.request.hooks.attach('on_start_resource', critical)
-            cherrypy.request.hooks.attach(self._point, self.callable)
-    
-    tools.numerify = NumTool('before_finalize', numerify)
-    
-    # It's not mandatory to inherit from cherrypy.Tool.
-    class NadsatTool:
-        
-        def __init__(self):
-            self.ended = {}
-            self._name = "nadsat"
-        
-        def nadsat(self):
-            def nadsat_it_up(body):
-                for chunk in body:
-                    chunk = chunk.replace("good", "horrorshow")
-                    chunk = chunk.replace("piece", "lomtick")
-                    yield chunk
-            cherrypy.response.body = nadsat_it_up(cherrypy.response.body)
-        nadsat.priority = 0
-        
-        def cleanup(self):
-            # This runs after the request has been completely written out.
-            cherrypy.response.body = "razdrez"
-            id = cherrypy.request.params.get("id")
-            if id:
-                self.ended[id] = True
-        cleanup.failsafe = True
-        
-        def _setup(self):
-            cherrypy.request.hooks.attach('before_finalize', self.nadsat)
-            cherrypy.request.hooks.attach('on_end_request', self.cleanup)
-    tools.nadsat = NadsatTool()
-    
-    def pipe_body():
-        cherrypy.request.process_request_body = False
-        clen = int(cherrypy.request.headers['Content-Length'])
-        cherrypy.request.body = cherrypy.request.rfile.read(clen)
-    
-    # Assert that we can use a callable object instead of a function.
-    class Rotator(object):
-        def __call__(self, scale):
-            r = cherrypy.response
-            r.collapse_body()
-            r.body = [chr((ord(x) + scale) % 256) for x in r.body[0]]
-    cherrypy.tools.rotator = cherrypy.Tool('before_finalize', Rotator())
-    
-    def stream_handler(next_handler, *args, **kwargs):
-        cherrypy.response.output = o = StringIO()
-        try:
-            response = next_handler(*args, **kwargs)
-            # Ignore the response and return our accumulated output instead.
-            return o.getvalue()
-        finally:
-            o.close()
-    cherrypy.tools.streamer = cherrypy._cptools.HandlerWrapperTool(stream_handler)
-    
-    class Root:
-        def index(self):
-            return "Howdy earth!"
-        index.exposed = True
-        
-        def tarfile(self):
-            cherrypy.response.output.write('I am ')
-            cherrypy.response.output.write('a tarfile')
-        tarfile.exposed = True
-        tarfile._cp_config = {'tools.streamer.on': True}
-        
-        def euro(self):
-            hooks = list(cherrypy.request.hooks['before_finalize'])
-            hooks.sort()
-            cbnames = [x.callback.__name__ for x in hooks]
-            assert cbnames == ['gzip'], cbnames
-            priorities = [x.priority for x in hooks]
-            assert priorities == [80], priorities
-            yield u"Hello,"
-            yield u"world"
-            yield europoundUnicode
-        euro.exposed = True
-        
-        # Bare hooks
-        def pipe(self):
-            return cherrypy.request.body
-        pipe.exposed = True
-        pipe._cp_config = {'hooks.before_request_body': pipe_body}
-        
-        # Multiple decorators; include kwargs just for fun.
-        # Note that rotator must run before gzip.
-        def decorated_euro(self, *vpath):
-            yield u"Hello,"
-            yield u"world"
-            yield europoundUnicode
-        decorated_euro.exposed = True
-        decorated_euro = tools.gzip(compress_level=6)(decorated_euro)
-        decorated_euro = tools.rotator(scale=3)(decorated_euro)
-    
-    root = Root()
-    
-    
-    class TestType(type):
-        """Metaclass which automatically exposes all functions in each subclass,
-        and adds an instance of the subclass as an attribute of root.
-        """
-        def __init__(cls, name, bases, dct):
-            type.__init__(cls, name, bases, dct)
-            for value in dct.itervalues():
-                if isinstance(value, types.FunctionType):
-                    value.exposed = True
-            setattr(root, name.lower(), cls())
-    class Test(object):
-        __metaclass__ = TestType
-    
-    
-    # METHOD ONE:
-    # Declare Tools in _cp_config
-    class Demo(Test):
-        
-        _cp_config = {"tools.nadsat.on": True}
-        
-        def index(self, id=None):
-            return "A good piece of cherry pie"
-        
-        def ended(self, id):
-            return repr(tools.nadsat.ended[id])
-        
-        def err(self, id=None):
-            raise ValueError()
-        
-        def errinstream(self, id=None):
-            yield "nonconfidential"
-            raise ValueError()
-            yield "confidential"
-        
-        # METHOD TWO: decorator using Tool()
-        # We support Python 2.3, but the @-deco syntax would look like this:
-        # @tools.check_access()
-        def restricted(self):
-            return "Welcome!"
-        restricted = myauthtools.check_access()(restricted)
-        userid = restricted
-        
-        def err_in_onstart(self):
-            return "success!"
-        
-        def stream(self, id=None):
-            for x in xrange(100000000):
-                yield str(x)
-        stream._cp_config = {'response.stream': True}
-    
-    
-    conf = {
-        # METHOD THREE:
-        # Declare Tools in detached config
-        '/demo': {
-            'tools.numerify.on': True,
-            'tools.numerify.map': {"pie": "3.14159"},
-        },
-        '/demo/restricted': {
-            'request.show_tracebacks': False,
-        },
-        '/demo/userid': {
-            'request.show_tracebacks': False,
-            'myauth.check_access.default': True,
-        },
-        '/demo/errinstream': {
-            'response.stream': True,
-        },
-        '/demo/err_in_onstart': {
-            # Because this isn't a dict, on_start_resource will error.
-            'tools.numerify.map': "pie->3.14159"
-        },
-        # Combined tools
-        '/euro': {
-            'tools.gzip.on': True,
-            'tools.encode.on': True,
-        },
-        # Priority specified in config
-        '/decorated_euro/subpath': {
-            'tools.gzip.priority': 10,
-        },
-        # Handler wrappers
-        '/tarfile': {'tools.streamer.on': True}
-    }
-    app = cherrypy.tree.mount(root, config=conf)
-    app.request_class.namespaces['myauth'] = myauthtools
-    
-    if sys.version_info >= (2, 5):
-        from cherrypy.test import py25
-        root.tooldecs = py25.ToolExamples()
-
-
-#                             Client-side code                             #
-
-from cherrypy.test import helper
-
-
-class ToolTests(helper.CPWebCase):
-    
-    def testHookErrors(self):
-        self.getPage("/demo/?id=1")
-        # If body is "razdrez", then on_end_request is being called too early.
-        self.assertBody("A horrorshow lomtick of cherry 3.14159")
-        # If this fails, then on_end_request isn't being called at all.
-        time.sleep(0.1)
-        self.getPage("/demo/ended/1")
-        self.assertBody("True")
-        
-        valerr = '\n    raise ValueError()\nValueError'
-        self.getPage("/demo/err?id=3")
-        # If body is "razdrez", then on_end_request is being called too early.
-        self.assertErrorPage(502, pattern=valerr)
-        # If this fails, then on_end_request isn't being called at all.
-        time.sleep(0.1)
-        self.getPage("/demo/ended/3")
-        self.assertBody("True")
-        
-        # If body is "razdrez", then on_end_request is being called too early.
-        if (cherrypy.server.protocol_version == "HTTP/1.0" or
-            getattr(cherrypy.server, "using_apache", False)):
-            self.getPage("/demo/errinstream?id=5")
-            # Because this error is raised after the response body has
-            # started, the status should not change to an error status.
-            self.assertStatus("200 OK")
-            self.assertBody("nonconfidential")
-        else:
-            # Because this error is raised after the response body has
-            # started, and because it's chunked output, an error is raised by
-            # the HTTP client when it encounters incomplete output.
-            self.assertRaises((ValueError, IncompleteRead), self.getPage,
-                              "/demo/errinstream?id=5")
-        # If this fails, then on_end_request isn't being called at all.
-        time.sleep(0.1)
-        self.getPage("/demo/ended/5")
-        self.assertBody("True")
-        
-        # Test the "__call__" technique (compile-time decorator).
-        self.getPage("/demo/restricted")
-        self.assertErrorPage(401)
-        
-        # Test compile-time decorator with kwargs from config.
-        self.getPage("/demo/userid")
-        self.assertBody("Welcome!")
-    
-    def testEndRequestOnDrop(self):
-        old_timeout = None
-        try:
-            httpserver = cherrypy.server.httpserver
-            old_timeout = httpserver.timeout
-        except (AttributeError, IndexError):
-            return self.skip()
-        
-        try:
-            httpserver.timeout = timeout
-            
-            # Test that on_end_request is called even if the client drops.
-            self.persistent = True
-            try:
-                conn = self.HTTP_CONN
-                conn.putrequest("GET", "/demo/stream?id=9", skip_host=True)
-                conn.putheader("Host", self.HOST)
-                conn.endheaders()
-                # Skip the rest of the request and close the conn. This will
-                # cause the server's active socket to error, which *should*
-                # result in the request being aborted, and request.close being
-                # called all the way up the stack (including WSGI middleware),
-                # eventually calling our on_end_request hook.
-            finally:
-                self.persistent = False
-            time.sleep(timeout * 2)
-            # Test that the on_end_request hook was called.
-            self.getPage("/demo/ended/9")
-            self.assertBody("True")
-        finally:
-            if old_timeout is not None:
-                httpserver.timeout = old_timeout
-    
-    def testGuaranteedHooks(self):
-        # The 'critical' on_start_resource hook is 'failsafe' (guaranteed
-        # to run even if there are failures in other on_start methods).
-        # This is NOT true of the other hooks.
-        # Here, we have set up a failure in NumerifyTool.numerify_map,
-        # but our 'critical' hook should run and set the error to 502.
-        self.getPage("/demo/err_in_onstart")
-        self.assertErrorPage(502)
-        self.assertInBody("AttributeError: 'str' object has no attribute 'items'")
-    
-    def testCombinedTools(self):
-        expectedResult = (u"Hello,world" + europoundUnicode).encode('utf-8')
-        zbuf = StringIO()
-        zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=9)
-        zfile.write(expectedResult)
-        zfile.close()
-        
-        self.getPage("/euro", headers=[("Accept-Encoding", "gzip"),
-                                        ("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7")])
-        self.assertInBody(zbuf.getvalue()[:3])
-        
-        zbuf = StringIO()
-        zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=6)
-        zfile.write(expectedResult)
-        zfile.close()
-        
-        self.getPage("/decorated_euro", headers=[("Accept-Encoding", "gzip")])
-        self.assertInBody(zbuf.getvalue()[:3])
-        
-        # This returns a different value because gzip's priority was
-        # lowered in conf, allowing the rotator to run after gzip.
-        # Of course, we don't want breakage in production apps,
-        # but it proves the priority was changed.
-        self.getPage("/decorated_euro/subpath",
-                     headers=[("Accept-Encoding", "gzip")])
-        self.assertInBody(''.join([chr((ord(x) + 3) % 256) for x in zbuf.getvalue()]))
-    
-    def testBareHooks(self):
-        content = "bit of a pain in me gulliver"
-        self.getPage("/pipe",
-                     headers=[("Content-Length", len(content)),
-                              ("Content-Type", "text/plain")],
-                     method="POST", body=content)
-        self.assertBody(content)
-    
-    def testHandlerWrapperTool(self):
-        self.getPage("/tarfile")
-        self.assertBody("I am a tarfile")
-    
-    def testToolWithConfig(self):
-        if not sys.version_info >= (2, 5):
-            return self.skip("skipped (Python 2.5+ only)")
-        
-        self.getPage('/tooldecs/blah')
-        self.assertHeader('Content-Type', 'application/data')
-    
-    def testWarnToolOn(self):
-        # get
-        try:
-            numon = cherrypy.tools.numerify.on
-        except AttributeError:
-            pass
-        else:
-            raise AssertionError("Tool.on did not error as it should have.")
-        
-        # set
-        try:
-            cherrypy.tools.numerify.on = True
-        except AttributeError:
-            pass
-        else:
-            raise AssertionError("Tool.on did not error as it should have.")
-
-
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_tutorials.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import sys
-
-import cherrypy
-from cherrypy.test import helper
-
-
-def setup_server():
-    
-    conf = cherrypy.config.copy()
-    
-    def load_tut_module(name):
-        """Import or reload tutorial module as needed."""
-        cherrypy.config.reset()
-        cherrypy.config.update(conf)
-        
-        target = "cherrypy.tutorial." + name
-        if target in sys.modules:
-            module = reload(sys.modules[target])
-        else:
-            module = __import__(target, globals(), locals(), [''])
-        # The above import will probably mount a new app at "".
-        app = cherrypy.tree.apps[""]
-        
-        app.root.load_tut_module = load_tut_module
-        app.root.sessions = sessions
-        app.root.traceback_setting = traceback_setting
-        
-        test.sync_apps()
-    load_tut_module.exposed = True
-    
-    def sessions():
-        cherrypy.config.update({"tools.sessions.on": True})
-    sessions.exposed = True
-    
-    def traceback_setting():
-        return repr(cherrypy.request.show_tracebacks)
-    traceback_setting.exposed = True
-    
-    class Dummy:
-        pass
-    root = Dummy()
-    root.load_tut_module = load_tut_module
-    cherrypy.tree.mount(root)
-
-
-class TutorialTest(helper.CPWebCase):
-    
-    def test01HelloWorld(self):
-        self.getPage("/load_tut_module/tut01_helloworld")
-        self.getPage("/")
-        self.assertBody('Hello world!')
-    
-    def test02ExposeMethods(self):
-        self.getPage("/load_tut_module/tut02_expose_methods")
-        self.getPage("/showMessage")
-        self.assertBody('Hello world!')
-    
-    def test03GetAndPost(self):
-        self.getPage("/load_tut_module/tut03_get_and_post")
-        
-        # Try different GET queries
-        self.getPage("/greetUser?name=Bob")
-        self.assertBody("Hey Bob, what's up?")
-        
-        self.getPage("/greetUser")
-        self.assertBody('Please enter your name <a href="./">here</a>.')
-        
-        self.getPage("/greetUser?name=")
-        self.assertBody('No, really, enter your name <a href="./">here</a>.')
-        
-        # Try the same with POST
-        self.getPage("/greetUser", method="POST", body="name=Bob")
-        self.assertBody("Hey Bob, what's up?")
-        
-        self.getPage("/greetUser", method="POST", body="name=")
-        self.assertBody('No, really, enter your name <a href="./">here</a>.')
-    
-    def test04ComplexSite(self):
-        self.getPage("/load_tut_module/tut04_complex_site")
-        msg = '''
-            <p>Here are some extra useful links:</p>
-            
-            <ul>
-                <li><a href="http://del.icio.us">del.icio.us</a></li>
-                <li><a href="http://www.mornography.de">Hendrik's weblog</a></li>
-            </ul>
-            
-            <p>[<a href="../">Return to links page</a>]</p>'''
-        self.getPage("/links/extra/")
-        self.assertBody(msg)
-    
-    def test05DerivedObjects(self):
-        self.getPage("/load_tut_module/tut05_derived_objects")
-        msg = '''
-            <html>
-            <head>
-                <title>Another Page</title>
-            <head>
-            <body>
-            <h2>Another Page</h2>
-        
-            <p>
-            And this is the amazing second page!
-            </p>
-        
-            </body>
-            </html>
-        '''
-        self.getPage("/another/")
-        self.assertBody(msg)
-    
-    def test06DefaultMethod(self):
-        self.getPage("/load_tut_module/tut06_default_method")
-        self.getPage('/hendrik')
-        self.assertBody('Hendrik Mans, CherryPy co-developer & crazy German '
-                        '(<a href="./">back</a>)')
-    
-    def test07Sessions(self):
-        self.getPage("/load_tut_module/tut07_sessions")
-        self.getPage("/sessions")
-        
-        self.getPage('/')
-        self.assertBody("\n            During your current session, you've viewed this"
-                         "\n            page 1 times! Your life is a patio of fun!"
-                         "\n        ")
-        
-        self.getPage('/', self.cookies)
-        self.assertBody("\n            During your current session, you've viewed this"
-                         "\n            page 2 times! Your life is a patio of fun!"
-                         "\n        ")
-    
-    def test08GeneratorsAndYield(self):
-        self.getPage("/load_tut_module/tut08_generators_and_yield")
-        self.getPage('/')
-        self.assertBody('<html><body><h2>Generators rule!</h2>'
-                         '<h3>List of users:</h3>'
-                         'Remi<br/>Carlos<br/>Hendrik<br/>Lorenzo Lamas<br/>'
-                         '</body></html>')
-    
-    def test09Files(self):
-        self.getPage("/load_tut_module/tut09_files")
-        
-        # Test upload
-        filesize = 5
-        h = [("Content-type", "multipart/form-data; boundary=x"),
-             ("Content-Length", str(105 + filesize))]
-        b = '--x\n' + \
-            'Content-Disposition: form-data; name="myFile"; filename="hello.txt"\r\n' + \
-            'Content-Type: text/plain\r\n' + \
-            '\r\n' + \
-            'a' * filesize + '\n' + \
-            '--x--\n'
-        self.getPage('/upload', h, "POST", b)
-        self.assertBody('''<html>
-        <body>
-            myFile length: %d<br />
-            myFile filename: hello.txt<br />
-            myFile mime-type: text/plain
-        </body>
-        </html>''' % filesize)
-    
-        # Test download
-        self.getPage('/download')
-        self.assertStatus("200 OK")
-        self.assertHeader("Content-Type", "application/x-download")
-        self.assertHeader("Content-Disposition",
-                          # Make sure the filename is quoted.
-                          'attachment; filename="pdf_file.pdf"')
-        self.assertEqual(len(self.body), 85698)
-    
-    def test10HTTPErrors(self):
-        self.getPage("/load_tut_module/tut10_http_errors")
-        
-        self.getPage("/")
-        self.assertInBody("""<a href="toggleTracebacks">""")
-        self.assertInBody("""<a href="/doesNotExist">""")
-        self.assertInBody("""<a href="/error?code=403">""")
-        self.assertInBody("""<a href="/error?code=500">""")
-        self.assertInBody("""<a href="/messageArg">""")
-        
-        self.getPage("/traceback_setting")
-        setting = self.body
-        self.getPage("/toggleTracebacks")
-        self.assertStatus((302, 303))
-        self.getPage("/traceback_setting")
-        self.assertBody(str(not eval(setting)))
-        
-        self.getPage("/error?code=500")
-        self.assertStatus(500)
-        self.assertInBody("The server encountered an unexpected condition "
-                          "which prevented it from fulfilling the request.")
-        
-        self.getPage("/error?code=403")
-        self.assertStatus(403)
-        self.assertInBody("<h2>You can't do that!</h2>")
-        
-        self.getPage("/messageArg")
-        self.assertStatus(500)
-        self.assertInBody("If you construct an HTTPError with a 'message'")
-
-
-if __name__ == "__main__":
-    helper.testmain({
-        'server.socket_host': '127.0.0.1',
-        'server.socket_port': 8080,
-        'server.thread_pool': 10,
-        })
--- a/bundled/cherrypy/cherrypy/test/test_virtualhost.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-import cherrypy
-
-def setup_server():
-    class Root:
-        def index(self):
-            return "Hello, world"
-        index.exposed = True
-        
-        def dom4(self):
-            return "Under construction"
-        dom4.exposed = True
-        
-        def method(self, value):
-            return "You sent %s" % repr(value)
-        method.exposed = True
-    
-    class VHost:
-        def __init__(self, sitename):
-            self.sitename = sitename
-        
-        def index(self):
-            return "Welcome to %s" % self.sitename
-        index.exposed = True
-        
-        def vmethod(self, value):
-            return "You sent %s" % repr(value)
-        vmethod.exposed = True
-        
-        def url(self):
-            return cherrypy.url("nextpage")
-        url.exposed = True
-        
-        # Test static as a handler (section must NOT include vhost prefix)
-        static = cherrypy.tools.staticdir.handler(section='/static', dir=curdir)
-    
-    root = Root()
-    root.mydom2 = VHost("Domain 2")
-    root.mydom3 = VHost("Domain 3")
-    hostmap = {'www.mydom2.com': '/mydom2',
-               'www.mydom3.com': '/mydom3',
-               'www.mydom4.com': '/dom4',
-               }
-    cherrypy.tree.mount(root, config={
-        '/': {'request.dispatch': cherrypy.dispatch.VirtualHost(**hostmap)},
-        # Test static in config (section must include vhost prefix)
-        '/mydom2/static2': {'tools.staticdir.on': True,
-                            'tools.staticdir.root': curdir,
-                            'tools.staticdir.dir': 'static',
-                            'tools.staticdir.index': 'index.html',
-                            },
-        })
-
-
-from cherrypy.test import helper
-
-class VirtualHostTest(helper.CPWebCase):
-    
-    def testVirtualHost(self):
-        self.getPage("/", [('Host', 'www.mydom1.com')])
-        self.assertBody('Hello, world')
-        self.getPage("/mydom2/", [('Host', 'www.mydom1.com')])
-        self.assertBody('Welcome to Domain 2')
-        
-        self.getPage("/", [('Host', 'www.mydom2.com')])
-        self.assertBody('Welcome to Domain 2')
-        self.getPage("/", [('Host', 'www.mydom3.com')])
-        self.assertBody('Welcome to Domain 3')
-        self.getPage("/", [('Host', 'www.mydom4.com')])
-        self.assertBody('Under construction')
-        
-        # Test GET, POST, and positional params
-        self.getPage("/method?value=root")
-        self.assertBody("You sent u'root'")
-        self.getPage("/vmethod?value=dom2+GET", [('Host', 'www.mydom2.com')])
-        self.assertBody("You sent u'dom2 GET'")
-        self.getPage("/vmethod", [('Host', 'www.mydom3.com')], method="POST",
-                     body="value=dom3+POST")
-        self.assertBody("You sent u'dom3 POST'")
-        self.getPage("/vmethod/pos", [('Host', 'www.mydom3.com')])
-        self.assertBody("You sent 'pos'")
-        
-        # Test that cherrypy.url uses the browser url, not the virtual url
-        self.getPage("/url", [('Host', 'www.mydom2.com')])
-        self.assertBody("%s://www.mydom2.com/nextpage" % self.scheme)
-    
-    def test_VHost_plus_Static(self):
-        # Test static as a handler
-        self.getPage("/static/style.css", [('Host', 'www.mydom2.com')])
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'text/css;charset=utf-8')
-        
-        # Test static in config
-        self.getPage("/static2/dirback.jpg", [('Host', 'www.mydom2.com')])
-        self.assertStatus('200 OK')
-        self.assertHeader('Content-Type', 'image/jpeg')
-        
-        # Test static config with "index" arg
-        self.getPage("/static2/", [('Host', 'www.mydom2.com')])
-        self.assertStatus('200 OK')
-        self.assertBody('Hello, world\r\n')
-        # Since tools.trailing_slash is on by default, this should redirect
-        self.getPage("/static2", [('Host', 'www.mydom2.com')])
-        self.assertStatus(301)
-
-
-if __name__ == "__main__":
-    helper.testmain()
--- a/bundled/cherrypy/cherrypy/test/test_wsgi_ns.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-
-
-def setup_server():
-    
-    class WSGIResponse(object):
-        
-        def __init__(self, appresults):
-            self.appresults = appresults
-            self.iter = iter(appresults)
-        
-        def __iter__(self):
-            return self
-        
-        def next(self):
-            return self.iter.next()
-        
-        def close(self):
-            if hasattr(self.appresults, "close"):
-                self.appresults.close()
-    
-    
-    class ChangeCase(object):
-        
-        def __init__(self, app, to=None):
-            self.app = app
-            self.to = to
-        
-        def __call__(self, environ, start_response):
-            res = self.app(environ, start_response)
-            class CaseResults(WSGIResponse):
-                def next(this):
-                    return getattr(this.iter.next(), self.to)()
-            return CaseResults(res)
-    
-    class Replacer(object):
-        
-        def __init__(self, app, map={}):
-            self.app = app
-            self.map = map
-        
-        def __call__(self, environ, start_response):
-            res = self.app(environ, start_response)
-            class ReplaceResults(WSGIResponse):
-                def next(this):
-                    line = this.iter.next()
-                    for k, v in self.map.iteritems():
-                        line = line.replace(k, v)
-                    return line
-            return ReplaceResults(res)
-    
-    class Root(object):
-        
-        def index(self):
-            return "HellO WoRlD!"
-        index.exposed = True
-    
-    
-    root_conf = {'wsgi.pipeline': [('replace', Replacer)],
-                 'wsgi.replace.map': {'L': 'X', 'l': 'r'},
-                 }
-    
-    app = cherrypy.Application(Root())
-    app.wsgiapp.pipeline.append(('changecase', ChangeCase))
-    app.wsgiapp.config['changecase'] = {'to': 'upper'}
-    cherrypy.tree.mount(app, config={'/': root_conf})
-
-
-from cherrypy.test import helper
-
-
-class WSGI_Namespace_Test(helper.CPWebCase):
-    
-    def test_pipeline(self):
-        if not cherrypy.server.httpserver:
-            return self.skip()
-        
-        self.getPage("/")
-        # If body is "HEXXO WORXD!", the middleware was applied out of order.
-        self.assertBody("HERRO WORRD!")
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_wsgi_vhost.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-import cherrypy
-
-
-def setup_server():
-    
-    class ClassOfRoot(object):
-        
-        def __init__(self, name):
-            self.name = name
-        
-        def index(self):
-            return "Welcome to the %s website!" % self.name
-        index.exposed = True
-    
-    
-    default = cherrypy.Application(None)
-    
-    domains = {}
-    for year in range(1997, 2008):
-        app = cherrypy.Application(ClassOfRoot('Class of %s' % year))
-        domains['www.classof%s.example' % year] = app
-    
-    cherrypy.tree.graft(cherrypy._cpwsgi.VirtualHost(default, domains))
-
-
-from cherrypy.test import helper
-
-
-class WSGI_VirtualHost_Test(helper.CPWebCase):
-    
-    def test_welcome(self):
-        if not cherrypy.server.using_wsgi:
-            return self.skip("skipped (not using WSGI)... ")
-        
-        for year in range(1997, 2008):
-            self.getPage("/", headers=[('Host', 'www.classof%s.example' % year)])
-            self.assertBody("Welcome to the Class of %s website!" % year)
-
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_wsgiapps.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-
-
-def setup_server():
-    import os
-    curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-    
-    import cherrypy
-    
-    def test_app(environ, start_response):
-        status = '200 OK'
-        response_headers = [('Content-type', 'text/plain')]
-        start_response(status, response_headers)
-        output = ['Hello, world!\n',
-                  'This is a wsgi app running within CherryPy!\n\n']
-        keys = list(environ.keys())
-        keys.sort()
-        for k in keys:
-            output.append('%s: %s\n' % (k,environ[k]))
-        return output
-    
-    def test_empty_string_app(environ, start_response):
-        status = '200 OK'
-        response_headers = [('Content-type', 'text/plain')]
-        start_response(status, response_headers)
-        return ['Hello', '', ' ', '', 'world']
-    
-    
-    class WSGIResponse(object):
-        
-        def __init__(self, appresults):
-            self.appresults = appresults
-            self.iter = iter(appresults)
-        
-        def __iter__(self):
-            return self
-        
-        def next(self):
-            return self.iter.next()
-        
-        def close(self):
-            if hasattr(self.appresults, "close"):
-                self.appresults.close()
-    
-    
-    class ReversingMiddleware(object):
-        
-        def __init__(self, app):
-            self.app = app
-        
-        def __call__(self, environ, start_response):
-            results = app(environ, start_response)
-            class Reverser(WSGIResponse):
-                def next(this):
-                    line = list(this.iter.next())
-                    line.reverse()
-                    return "".join(line)
-            return Reverser(results)
-    
-    class Root:
-        def index(self):
-            return "I'm a regular CherryPy page handler!"
-        index.exposed = True
-    
-    
-    cherrypy.tree.mount(Root())
-    
-    cherrypy.tree.graft(test_app, '/hosted/app1')
-    cherrypy.tree.graft(test_empty_string_app, '/hosted/app3')
-    
-    # Set script_name explicitly to None to signal CP that it should
-    # be pulled from the WSGI environ each time.
-    app = cherrypy.Application(Root(), script_name=None)
-    cherrypy.tree.graft(ReversingMiddleware(app), '/hosted/app2')
-
-from cherrypy.test import helper
-
-
-class WSGIGraftTests(helper.CPWebCase):
-    
-    wsgi_output = '''Hello, world!
-This is a wsgi app running within CherryPy!'''
-
-    def test_01_standard_app(self):
-        self.getPage("/")
-        self.assertBody("I'm a regular CherryPy page handler!")
-    
-    def test_04_pure_wsgi(self):
-        import cherrypy
-        if not cherrypy.server.using_wsgi:
-            return self.skip("skipped (not using WSGI)... ")
-        self.getPage("/hosted/app1")
-        self.assertHeader("Content-Type", "text/plain")
-        self.assertInBody(self.wsgi_output)
-
-    def test_05_wrapped_cp_app(self):
-        import cherrypy
-        if not cherrypy.server.using_wsgi:
-            return self.skip("skipped (not using WSGI)... ")
-        self.getPage("/hosted/app2/")
-        body = list("I'm a regular CherryPy page handler!")
-        body.reverse()
-        body = "".join(body)
-        self.assertInBody(body)
-
-    def test_06_empty_string_app(self):
-        import cherrypy
-        if not cherrypy.server.using_wsgi:
-            return self.skip("skipped (not using WSGI)... ")
-        self.getPage("/hosted/app3")
-        self.assertHeader("Content-Type", "text/plain")
-        self.assertInBody('Hello world')
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/test_xmlrpc.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-from cherrypy.test import test
-test.prefer_parent_path()
-import xmlrpclib
-
-import cherrypy
-
-
-def setup_server():
-    from cherrypy import _cptools
-    
-    class Root:
-        def index(self):
-            return "I'm a standard index!"
-        index.exposed = True
-
-
-    class XmlRpc(_cptools.XMLRPCController):
-        
-        def foo(self):
-            return "Hello world!"
-        foo.exposed = True
-        
-        def return_single_item_list(self):
-            return [42]
-        return_single_item_list.exposed = True
-        
-        def return_string(self):
-            return "here is a string"
-        return_string.exposed = True
-        
-        def return_tuple(self):
-            return ('here', 'is', 1, 'tuple')
-        return_tuple.exposed = True
-        
-        def return_dict(self):
-            return dict(a=1, b=2, c=3)
-        return_dict.exposed = True
-        
-        def return_composite(self):
-            return dict(a=1,z=26), 'hi', ['welcome', 'friend']
-        return_composite.exposed = True
-
-        def return_int(self):
-            return 42
-        return_int.exposed = True
-
-        def return_float(self):
-            return 3.14
-        return_float.exposed = True
-
-        def return_datetime(self):
-            return xmlrpclib.DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1))
-        return_datetime.exposed = True
-
-        def return_boolean(self):
-            return True
-        return_boolean.exposed = True
-
-        def test_argument_passing(self, num):
-            return num * 2
-        test_argument_passing.exposed = True
-
-        def test_returning_Fault(self):
-            return xmlrpclib.Fault(1, "custom Fault response")
-        test_returning_Fault.exposed = True
-
-    root = Root()
-    root.xmlrpc = XmlRpc()
-    cherrypy.tree.mount(root, config={'/': {
-        'request.dispatch': cherrypy.dispatch.XMLRPCDispatcher(),
-        'tools.xmlrpc.allow_none': 0,
-        }})
-
-
-class HTTPSTransport(xmlrpclib.SafeTransport):
-    """Subclass of SafeTransport to fix sock.recv errors (by using file)."""
-    
-    def request(self, host, handler, request_body, verbose=0):
-        # issue XML-RPC request
-        h = self.make_connection(host)
-        if verbose:
-            h.set_debuglevel(1)
-        
-        self.send_request(h, handler, request_body)
-        self.send_host(h, host)
-        self.send_user_agent(h)
-        self.send_content(h, request_body)
-        
-        errcode, errmsg, headers = h.getreply()
-        if errcode != 200:
-            raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg,
-                                          headers)
-        
-        self.verbose = verbose
-        
-        # Here's where we differ from the superclass. It says:
-        # try:
-        #     sock = h._conn.sock
-        # except AttributeError:
-        #     sock = None
-        # return self._parse_response(h.getfile(), sock)
-        
-        return self.parse_response(h.getfile())
-
-
-from cherrypy.test import helper
-
-class XmlRpcTest(helper.CPWebCase):
-    def testXmlRpc(self):
-        
-        # load the appropriate xmlrpc proxy
-        scheme = "http"
-        try:
-            scheme = self.harness.scheme
-        except AttributeError:
-            pass
-        
-        if scheme == "https":
-            url = 'https://%s:%s/xmlrpc/' % (self.interface(), self.PORT)
-            proxy = xmlrpclib.ServerProxy(url, transport=HTTPSTransport())
-        else:
-            url = 'http://%s:%s/xmlrpc/' % (self.interface(), self.PORT)
-            proxy = xmlrpclib.ServerProxy(url)
-        
-        # begin the tests ...
-        self.getPage("/xmlrpc/foo")
-        self.assertBody("Hello world!")
-        
-        self.assertEqual(proxy.return_single_item_list(), [42])
-        self.assertNotEqual(proxy.return_single_item_list(), 'one bazillion')
-        self.assertEqual(proxy.return_string(), "here is a string")
-        self.assertEqual(proxy.return_tuple(), list(('here', 'is', 1, 'tuple')))
-        self.assertEqual(proxy.return_dict(), {'a': 1, 'c': 3, 'b': 2})
-        self.assertEqual(proxy.return_composite(),
-                         [{'a': 1, 'z': 26}, 'hi', ['welcome', 'friend']])
-        self.assertEqual(proxy.return_int(), 42)
-        self.assertEqual(proxy.return_float(), 3.14)
-        self.assertEqual(proxy.return_datetime(),
-                         xmlrpclib.DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1)))
-        self.assertEqual(proxy.return_boolean(), True)
-        self.assertEqual(proxy.test_argument_passing(22), 22 * 2)
-        
-        # Test an error in the page handler (should raise an xmlrpclib.Fault)
-        try:
-            proxy.test_argument_passing({})
-        except Exception, x:
-            self.assertEqual(x.__class__, xmlrpclib.Fault)
-            self.assertEqual(x.faultString, ("unsupported operand type(s) "
-                                             "for *: 'dict' and 'int'"))
-        else:
-            self.fail("Expected xmlrpclib.Fault")
-        
-        # http://www.cherrypy.org/ticket/533
-        # if a method is not found, an xmlrpclib.Fault should be raised
-        try:
-            proxy.non_method()
-        except Exception, x:
-            self.assertEqual(x.__class__, xmlrpclib.Fault)
-            self.assertEqual(x.faultString, 'method "non_method" is not supported')
-        else:
-            self.fail("Expected xmlrpclib.Fault")
-        
-        # Test returning a Fault from the page handler.
-        try:
-            proxy.test_returning_Fault()
-        except Exception, x:
-            self.assertEqual(x.__class__, xmlrpclib.Fault)
-            self.assertEqual(x.faultString, ("custom Fault response"))
-        else:
-            self.fail("Expected xmlrpclib.Fault")
-
-
-if __name__ == '__main__':
-    helper.testmain()
-
--- a/bundled/cherrypy/cherrypy/test/webtest.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,597 +0,0 @@
-"""Extensions to unittest for web frameworks.
-
-Use the WebCase.getPage method to request a page from your HTTP server.
-
-Framework Integration
-=====================
-
-If you have control over your server process, you can handle errors
-in the server-side of the HTTP conversation a bit better. You must run
-both the client (your WebCase tests) and the server in the same process
-(but in separate threads, obviously).
-
-When an error occurs in the framework, call server_error. It will print
-the traceback to stdout, and keep any assertions you have from running
-(the assumption is that, if the server errors, the page output will not
-be of further significance to your tests).
-"""
-
-from httplib import HTTPConnection, HTTPSConnection
-import os
-import pprint
-import re
-import socket
-import sys
-import time
-import traceback
-import types
-
-from unittest import *
-from unittest import _TextTestResult
-
-
-
-def interface(host):
-    """Return an IP address for a client connection given the server host.
-    
-    If the server is listening on '0.0.0.0' (INADDR_ANY)
-    or '::' (IN6ADDR_ANY), this will return the proper localhost."""
-    if host == '0.0.0.0':
-        # INADDR_ANY, which should respond on localhost.
-        return "127.0.0.1"
-    if host == '::':
-        # IN6ADDR_ANY, which should respond on localhost.
-        return "::1"
-    return host
-
-
-class TerseTestResult(_TextTestResult):
-    
-    def printErrors(self):
-        # Overridden to avoid unnecessary empty line
-        if self.errors or self.failures:
-            if self.dots or self.showAll:
-                self.stream.writeln()
-            self.printErrorList('ERROR', self.errors)
-            self.printErrorList('FAIL', self.failures)
-
-
-class TerseTestRunner(TextTestRunner):
-    """A test runner class that displays results in textual form."""
-    
-    def _makeResult(self):
-        return TerseTestResult(self.stream, self.descriptions, self.verbosity)
-    
-    def run(self, test):
-        "Run the given test case or test suite."
-        # Overridden to remove unnecessary empty lines and separators
-        result = self._makeResult()
-        test(result)
-        result.printErrors()
-        if not result.wasSuccessful():
-            self.stream.write("FAILED (")
-            failed, errored = map(len, (result.failures, result.errors))
-            if failed:
-                self.stream.write("failures=%d" % failed)
-            if errored:
-                if failed: self.stream.write(", ")
-                self.stream.write("errors=%d" % errored)
-            self.stream.writeln(")")
-        return result
-
-
-class ReloadingTestLoader(TestLoader):
-    
-    def loadTestsFromName(self, name, module=None):
-        """Return a suite of all tests cases given a string specifier.
-
-        The name may resolve either to a module, a test case class, a
-        test method within a test case class, or a callable object which
-        returns a TestCase or TestSuite instance.
-
-        The method optionally resolves the names relative to a given module.
-        """
-        parts = name.split('.')
-        unused_parts = []
-        if module is None:
-            if not parts:
-                raise ValueError("incomplete test name: %s" % name)
-            else:
-                parts_copy = parts[:]
-                while parts_copy:
-                    target = ".".join(parts_copy)
-                    if target in sys.modules:
-                        module = reload(sys.modules[target])
-                        parts = unused_parts
-                        break
-                    else:
-                        try:
-                            module = __import__(target)
-                            parts = unused_parts
-                            break
-                        except ImportError:
-                            unused_parts.insert(0,parts_copy[-1])
-                            del parts_copy[-1]
-                            if not parts_copy:
-                                raise
-                parts = parts[1:]
-        obj = module
-        for part in parts:
-            obj = getattr(obj, part)
-        
-        if type(obj) == types.ModuleType:
-            return self.loadTestsFromModule(obj)
-        elif (isinstance(obj, (type, types.ClassType)) and
-              issubclass(obj, TestCase)):
-            return self.loadTestsFromTestCase(obj)
-        elif type(obj) == types.UnboundMethodType:
-            return obj.im_class(obj.__name__)
-        elif callable(obj):
-            test = obj()
-            if not isinstance(test, TestCase) and \
-               not isinstance(test, TestSuite):
-                raise ValueError("calling %s returned %s, "
-                                 "not a test" % (obj,test))
-            return test
-        else:
-            raise ValueError("do not know how to make test from: %s" % obj)
-
-
-try:
-    # On Windows, msvcrt.getch reads a single char without output.
-    import msvcrt
-    def getchar():
-        return msvcrt.getch()
-except ImportError:
-    # Unix getchr
-    import tty, termios
-    def getchar():
-        fd = sys.stdin.fileno()
-        old_settings = termios.tcgetattr(fd)
-        try:
-            tty.setraw(sys.stdin.fileno())
-            ch = sys.stdin.read(1)
-        finally:
-            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
-        return ch
-
-
-class WebCase(TestCase):
-    HOST = "127.0.0.1"
-    PORT = 8000
-    HTTP_CONN = HTTPConnection
-    PROTOCOL = "HTTP/1.1"
-    
-    scheme = "http"
-    url = None
-    
-    status = None
-    headers = None
-    body = None
-    time = None
-    
-    def get_conn(self, auto_open=False):
-        """Return a connection to our HTTP server."""
-        if self.scheme == "https":
-            cls = HTTPSConnection
-        else:
-            cls = HTTPConnection
-        conn = cls(self.interface(), self.PORT)
-        # Automatically re-connect?
-        conn.auto_open = auto_open
-        conn.connect()
-        return conn
-    
-    def set_persistent(self, on=True, auto_open=False):
-        """Make our HTTP_CONN persistent (or not).
-        
-        If the 'on' argument is True (the default), then self.HTTP_CONN
-        will be set to an instance of HTTPConnection (or HTTPS
-        if self.scheme is "https"). This will then persist across requests.
-        
-        We only allow for a single open connection, so if you call this
-        and we currently have an open connection, it will be closed.
-        """
-        try:
-            self.HTTP_CONN.close()
-        except (TypeError, AttributeError):
-            pass
-        
-        if on:
-            self.HTTP_CONN = self.get_conn(auto_open=auto_open)
-        else:
-            if self.scheme == "https":
-                self.HTTP_CONN = HTTPSConnection
-            else:
-                self.HTTP_CONN = HTTPConnection
-    
-    def _get_persistent(self):
-        return hasattr(self.HTTP_CONN, "__class__")
-    def _set_persistent(self, on):
-        self.set_persistent(on)
-    persistent = property(_get_persistent, _set_persistent)
-    
-    def interface(self):
-        """Return an IP address for a client connection.
-        
-        If the server is listening on '0.0.0.0' (INADDR_ANY)
-        or '::' (IN6ADDR_ANY), this will return the proper localhost."""
-        return interface(self.HOST)
-    
-    def getPage(self, url, headers=None, method="GET", body=None, protocol=None):
-        """Open the url with debugging support. Return status, headers, body."""
-        ServerError.on = False
-        
-        self.url = url
-        self.time = None
-        start = time.time()
-        result = openURL(url, headers, method, body, self.HOST, self.PORT,
-                         self.HTTP_CONN, protocol or self.PROTOCOL)
-        self.time = time.time() - start
-        self.status, self.headers, self.body = result
-        
-        # Build a list of request cookies from the previous response cookies.
-        self.cookies = [('Cookie', v) for k, v in self.headers
-                        if k.lower() == 'set-cookie']
-        
-        if ServerError.on:
-            raise ServerError()
-        return result
-    
-    interactive = True
-    console_height = 30
-    
-    def _handlewebError(self, msg):
-        import cherrypy
-        print("")
-        print("    ERROR: %s" % msg)
-        
-        if not self.interactive:
-            raise self.failureException(msg)
-        
-        p = "    Show: [B]ody [H]eaders [S]tatus [U]RL; [I]gnore, [R]aise, or sys.e[X]it >> "
-        print p,
-        # ARGH!
-        sys.stdout.flush()
-        while True:
-            i = getchar().upper()
-            if i not in "BHSUIRX":
-                continue
-            print(i.upper())  # Also prints new line
-            if i == "B":
-                for x, line in enumerate(self.body.splitlines()):
-                    if (x + 1) % self.console_height == 0:
-                        # The \r and comma should make the next line overwrite
-                        print "<-- More -->\r",
-                        m = getchar().lower()
-                        # Erase our "More" prompt
-                        print "            \r",
-                        if m == "q":
-                            break
-                    print(line)
-            elif i == "H":
-                pprint.pprint(self.headers)
-            elif i == "S":
-                print(self.status)
-            elif i == "U":
-                print(self.url)
-            elif i == "I":
-                # return without raising the normal exception
-                return
-            elif i == "R":
-                raise self.failureException(msg)
-            elif i == "X":
-                self.exit()
-            print p,
-            # ARGH
-            sys.stdout.flush()    
-    def exit(self):
-        sys.exit()
-    
-    if sys.version_info >= (2, 5):
-        def __call__(self, result=None):
-            if result is None:
-                result = self.defaultTestResult()
-            result.startTest(self)
-            testMethod = getattr(self, self._testMethodName)
-            try:
-                try:
-                    self.setUp()
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except:
-                    result.addError(self, self._exc_info())
-                    return
-                
-                ok = 0
-                try:
-                    testMethod()
-                    ok = 1
-                except self.failureException:
-                    result.addFailure(self, self._exc_info())
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except:
-                    result.addError(self, self._exc_info())
-                
-                try:
-                    self.tearDown()
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except:
-                    result.addError(self, self._exc_info())
-                    ok = 0
-                if ok:
-                    result.addSuccess(self)
-            finally:
-                result.stopTest(self)
-    else:
-        def __call__(self, result=None):
-            if result is None:
-                result = self.defaultTestResult()
-            result.startTest(self)
-            testMethod = getattr(self, self._TestCase__testMethodName)
-            try:
-                try:
-                    self.setUp()
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except:
-                    result.addError(self, self._TestCase__exc_info())
-                    return
-                
-                ok = 0
-                try:
-                    testMethod()
-                    ok = 1
-                except self.failureException:
-                    result.addFailure(self, self._TestCase__exc_info())
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except:
-                    result.addError(self, self._TestCase__exc_info())
-                
-                try:
-                    self.tearDown()
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except:
-                    result.addError(self, self._TestCase__exc_info())
-                    ok = 0
-                if ok:
-                    result.addSuccess(self)
-            finally:
-                result.stopTest(self)
-    
-    def assertStatus(self, status, msg=None):
-        """Fail if self.status != status."""
-        if isinstance(status, basestring):
-            if not self.status == status:
-                if msg is None:
-                    msg = 'Status (%r) != %r' % (self.status, status)
-                self._handlewebError(msg)
-        elif isinstance(status, int):
-            code = int(self.status[:3])
-            if code != status:
-                if msg is None:
-                    msg = 'Status (%r) != %r' % (self.status, status)
-                self._handlewebError(msg)
-        else:
-            # status is a tuple or list.
-            match = False
-            for s in status:
-                if isinstance(s, basestring):
-                    if self.status == s:
-                        match = True
-                        break
-                elif int(self.status[:3]) == s:
-                    match = True
-                    break
-            if not match:
-                if msg is None:
-                    msg = 'Status (%r) not in %r' % (self.status, status)
-                self._handlewebError(msg)
-    
-    def assertHeader(self, key, value=None, msg=None):
-        """Fail if (key, [value]) not in self.headers."""
-        lowkey = key.lower()
-        for k, v in self.headers:
-            if k.lower() == lowkey:
-                if value is None or str(value) == v:
-                    return v
-        
-        if msg is None:
-            if value is None:
-                msg = '%r not in headers' % key
-            else:
-                msg = '%r:%r not in headers' % (key, value)
-        self._handlewebError(msg)
-    
-    def assertHeaderItemValue(self, key, value, msg=None):
-        """Fail if the header does not contain the specified value"""
-        actual_value = self.assertHeader(key, msg=msg)
-        header_values = map(str.strip, actual_value.split(','))
-        if value in header_values:
-            return value
-        
-        if msg is None:
-            msg = "%r not in %r" % (value, header_values)
-        self._handlewebError(msg)
-
-    def assertNoHeader(self, key, msg=None):
-        """Fail if key in self.headers."""
-        lowkey = key.lower()
-        matches = [k for k, v in self.headers if k.lower() == lowkey]
-        if matches:
-            if msg is None:
-                msg = '%r in headers' % key
-            self._handlewebError(msg)
-    
-    def assertBody(self, value, msg=None):
-        """Fail if value != self.body."""
-        if value != self.body:
-            if msg is None:
-                msg = 'expected body:\n%r\n\nactual body:\n%r' % (value, self.body)
-            self._handlewebError(msg)
-    
-    def assertInBody(self, value, msg=None):
-        """Fail if value not in self.body."""
-        if value not in self.body:
-            if msg is None:
-                msg = '%r not in body: %s' % (value, self.body)
-            self._handlewebError(msg)
-    
-    def assertNotInBody(self, value, msg=None):
-        """Fail if value in self.body."""
-        if value in self.body:
-            if msg is None:
-                msg = '%r found in body' % value
-            self._handlewebError(msg)
-    
-    def assertMatchesBody(self, pattern, msg=None, flags=0):
-        """Fail if value (a regex pattern) is not in self.body."""
-        if re.search(pattern, self.body, flags) is None:
-            if msg is None:
-                msg = 'No match for %r in body' % pattern
-            self._handlewebError(msg)
-
-
-methods_with_bodies = ("POST", "PUT")
-
-def cleanHeaders(headers, method, body, host, port):
-    """Return request headers, with required headers added (if missing)."""
-    if headers is None:
-        headers = []
-    
-    # Add the required Host request header if not present.
-    # [This specifies the host:port of the server, not the client.]
-    found = False
-    for k, v in headers:
-        if k.lower() == 'host':
-            found = True
-            break
-    if not found:
-        if port == 80:
-            headers.append(("Host", host))
-        else:
-            headers.append(("Host", "%s:%s" % (host, port)))
-    
-    if method in methods_with_bodies:
-        # Stick in default type and length headers if not present
-        found = False
-        for k, v in headers:
-            if k.lower() == 'content-type':
-                found = True
-                break
-        if not found:
-            headers.append(("Content-Type", "application/x-www-form-urlencoded"))
-            headers.append(("Content-Length", str(len(body or ""))))
-    
-    return headers
-
-
-def shb(response):
-    """Return status, headers, body the way we like from a response."""
-    h = []
-    key, value = None, None
-    for line in response.msg.headers:
-        if line:
-            if line[0] in " \t":
-                value += line.strip()
-            else:
-                if key and value:
-                    h.append((key, value))
-                key, value = line.split(":", 1)
-                key = key.strip()
-                value = value.strip()
-    if key and value:
-        h.append((key, value))
-    
-    return "%s %s" % (response.status, response.reason), h, response.read()
-
-
-def openURL(url, headers=None, method="GET", body=None,
-            host="127.0.0.1", port=8000, http_conn=HTTPConnection,
-            protocol="HTTP/1.1"):
-    """Open the given HTTP resource and return status, headers, and body."""
-    
-    headers = cleanHeaders(headers, method, body, host, port)
-    
-    # Trying 10 times is simply in case of socket errors.
-    # Normal case--it should run once.
-    for trial in range(10):
-        try:
-            # Allow http_conn to be a class or an instance
-            if hasattr(http_conn, "host"):
-                conn = http_conn
-            else:
-                conn = http_conn(interface(host), port)
-
-            conn._http_vsn_str = protocol
-            conn._http_vsn = int("".join([x for x in protocol if x.isdigit()]))
-            
-            # skip_accept_encoding argument added in python version 2.4
-            if sys.version_info < (2, 4):
-                def putheader(self, header, value):
-                    if header == 'Accept-Encoding' and value == 'identity':
-                        return
-                    self.__class__.putheader(self, header, value)
-                import new
-                conn.putheader = new.instancemethod(putheader, conn, conn.__class__)
-                conn.putrequest(method.upper(), url, skip_host=True)
-            else:
-                conn.putrequest(method.upper(), url, skip_host=True,
-                                skip_accept_encoding=True)
-            
-            for key, value in headers:
-                conn.putheader(key, value)
-            conn.endheaders()
-            
-            if body is not None:
-                conn.send(body)
-            
-            # Handle response
-            response = conn.getresponse()
-            
-            s, h, b = shb(response)
-            
-            if not hasattr(http_conn, "host"):
-                # We made our own conn instance. Close it.
-                conn.close()
-            
-            return s, h, b
-        except socket.error:
-            time.sleep(0.5)
-    raise
-
-
-# Add any exceptions which your web framework handles
-# normally (that you don't want server_error to trap).
-ignored_exceptions = []
-
-# You'll want set this to True when you can't guarantee
-# that each response will immediately follow each request;
-# for example, when handling requests via multiple threads.
-ignore_all = False
-
-class ServerError(Exception):
-    on = False
-
-
-def server_error(exc=None):
-    """Server debug hook. Return True if exception handled, False if ignored.
-    
-    You probably want to wrap this, so you can still handle an error using
-    your framework when it's ignored.
-    """
-    if exc is None: 
-        exc = sys.exc_info()
-    
-    if ignore_all or exc[0] in ignored_exceptions:
-        return False
-    else:
-        ServerError.on = True
-        print("")
-        print("".join(traceback.format_exception(*exc)))
-        return True
-
--- a/bundled/cherrypy/cherrypy/tutorial/README.txt	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-CherryPy Tutorials
-------------------------------------------------------------------------
-
-This is a series of tutorials explaining how to develop dynamic web
-applications using CherryPy. A couple of notes:
-
-  - Each of these tutorials builds on the ones before it. If you're
-    new to CherryPy, we recommend you start with 01_helloworld.py and
-    work your way upwards. :)
-
-  - In most of these tutorials, you will notice that all output is done
-    by returning normal Python strings, often using simple Python
-    variable substitution. In most real-world applications, you will
-    probably want to use a separate template package (like Cheetah,
-    CherryTemplate or XML/XSL).
-
--- a/bundled/cherrypy/cherrypy/tutorial/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-
-# This is used in test_config to test unrepr of "from A import B"
-thing2 = object()
\ No newline at end of file
--- a/bundled/cherrypy/cherrypy/tutorial/bonus-sqlobject.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-'''
-Bonus Tutorial: Using SQLObject
-
-This is a silly little contacts manager application intended to
-demonstrate how to use SQLObject from within a CherryPy2 project. It
-also shows how to use inline Cheetah templates.
-
-SQLObject is an Object/Relational Mapper that allows you to access
-data stored in an RDBMS in a pythonic fashion. You create data objects
-as Python classes and let SQLObject take care of all the nasty details.
-
-This code depends on the latest development version (0.6+) of SQLObject.
-You can get it from the SQLObject Subversion server. You can find all
-necessary information at <http://www.sqlobject.org>. This code will NOT
-work with the 0.5.x version advertised on their website!
-
-This code also depends on a recent version of Cheetah. You can find
-Cheetah at <http://www.cheetahtemplate.org>.
-
-After starting this application for the first time, you will need to
-access the /reset URI in order to create the database table and some
-sample data. Accessing /reset again will drop and re-create the table,
-so you may want to be careful. :-)
-
-This application isn't supposed to be fool-proof, it's not even supposed
-to be very GOOD. Play around with it some, browse the source code, smile.
-
-:)
-
--- Hendrik Mans <hendrik@mans.de>
-'''
-
-import cherrypy
-from Cheetah.Template import Template
-from sqlobject import *
-
-# configure your database connection here
-__connection__ = 'mysql://root:@localhost/test'
-
-# this is our (only) data class.
-class Contact(SQLObject):
-    lastName = StringCol(length = 50, notNone = True)
-    firstName = StringCol(length = 50, notNone = True)
-    phone = StringCol(length = 30, notNone = True, default = '')
-    email = StringCol(length = 30, notNone = True, default = '')
-    url = StringCol(length = 100, notNone = True, default = '')
-
-
-class ContactManager:
-    def index(self):
-        # Let's display a list of all stored contacts.
-        contacts = Contact.select()
-
-        template = Template('''
-            <h2>All Contacts</h2>
-
-            #for $contact in $contacts
-                <a href="mailto:$contact.email">$contact.lastName, $contact.firstName</a>
-                [<a href="./edit?id=$contact.id">Edit</a>]
-                [<a href="./delete?id=$contact.id">Delete</a>]
-                <br/>
-            #end for
-
-            <p>[<a href="./edit">Add new contact</a>]</p>
-        ''', [locals(), globals()])
-
-        return template.respond()
-
-    index.exposed = True
-
-
-    def edit(self, id = 0):
-        # we really want id as an integer. Since GET/POST parameters
-        # are always passed as strings, let's convert it.
-        id = int(id)
-
-        if id > 0:
-            # if an id is specified, we're editing an existing contact.
-            contact = Contact.get(id)
-            title = "Edit Contact"
-        else:
-            # if no id is specified, we're entering a new contact.
-            contact = None
-            title = "New Contact"
-
-
-        # In the following template code, please note that we use
-        # Cheetah's $getVar() construct for the form values. We have
-        # to do this because contact may be set to None (see above).
-        template = Template('''
-            <h2>$title</h2>
-
-            <form action="./store" method="POST">
-                <input type="hidden" name="id" value="$id" />
-                Last Name: <input name="lastName" value="$getVar('contact.lastName', '')" /><br/>
-                First Name: <input name="firstName" value="$getVar('contact.firstName', '')" /><br/>
-                Phone: <input name="phone" value="$getVar('contact.phone', '')" /><br/>
-                Email: <input name="email" value="$getVar('contact.email', '')" /><br/>
-                URL: <input name="url" value="$getVar('contact.url', '')" /><br/>
-                <input type="submit" value="Store" />
-            </form>
-        ''', [locals(), globals()])
-
-        return template.respond()
-
-    edit.exposed = True
-
-
-    def delete(self, id):
-        # Delete the specified contact
-        contact = Contact.get(int(id))
-        contact.destroySelf()
-        return 'Deleted. <a href="./">Return to Index</a>'
-
-    delete.exposed = True
-
-
-    def store(self, lastName, firstName, phone, email, url, id = None):
-        if id and int(id) > 0:
-            # If an id was specified, update an existing contact.
-            contact = Contact.get(int(id))
-
-            # We could set one field after another, but that would
-            # cause multiple UPDATE clauses. So we'll just do it all
-            # in a single pass through the set() method.
-            contact.set(
-                lastName = lastName,
-                firstName = firstName,
-                phone = phone,
-                email = email,
-                url = url)
-        else:
-            # Otherwise, add a new contact.
-            contact = Contact(
-                lastName = lastName,
-                firstName = firstName,
-                phone = phone,
-                email = email,
-                url = url)
-
-        return 'Stored. <a href="./">Return to Index</a>'
-
-    store.exposed = True
-
-
-    def reset(self):
-        # Drop existing table
-        Contact.dropTable(True)
-
-        # Create new table
-        Contact.createTable()
-
-        # Create some sample data
-        Contact(
-            firstName = 'Hendrik',
-            lastName = 'Mans',
-            email = 'hendrik@mans.de',
-            phone = '++49 89 12345678',
-            url = 'http://www.mornography.de')
-
-        return "reset completed!"
-
-    reset.exposed = True
-
-
-print("If you're running this application for the first time, please go to http://localhost:8080/reset once in order to create the database!")
-
-cherrypy.quickstart(ContactManager())
--- a/bundled/cherrypy/cherrypy/tutorial/custom_error.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-<head>
-    <title>403 Unauthorized</title>
-</head>
-    <body>
-        <h2>You can't do that!</h2>
-        <p>%(message)s</p>
-        <p>This is a custom error page that is read from a file.<p>
-        <p>%(traceback)s</p>
-    </body>
-</html>
\ No newline at end of file
Binary file bundled/cherrypy/cherrypy/tutorial/pdf_file.pdf has changed
--- a/bundled/cherrypy/cherrypy/tutorial/tut01_helloworld.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-"""
-Tutorial - Hello World
-
-The most basic (working) CherryPy application possible.
-"""
-
-# Import CherryPy global namespace
-import cherrypy
-
-class HelloWorld:
-    """ Sample request handler class. """
-
-    def index(self):
-        # CherryPy will call this method for the root URI ("/") and send
-        # its return value to the client. Because this is tutorial
-        # lesson number 01, we'll just send something really simple.
-        # How about...
-        return "Hello world!"
-
-    # Expose the index method through the web. CherryPy will never
-    # publish methods that don't have the exposed attribute set to True.
-    index.exposed = True
-
-
-import os.path
-tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
-
-if __name__ == '__main__':
-    # CherryPy always starts with app.root when trying to map request URIs
-    # to objects, so we need to mount a request handler root. A request
-    # to '/' will be mapped to HelloWorld().index().
-    cherrypy.quickstart(HelloWorld(), config=tutconf)
-else:
-    # This branch is for the test suite; you can ignore it.
-    cherrypy.tree.mount(HelloWorld(), config=tutconf)
--- a/bundled/cherrypy/cherrypy/tutorial/tut02_expose_methods.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-"""
-Tutorial - Multiple methods
-
-This tutorial shows you how to link to other methods of your request
-handler.
-"""
-
-import cherrypy
-
-class HelloWorld:
-    
-    def index(self):
-        # Let's link to another method here.
-        return 'We have an <a href="showMessage">important message</a> for you!'
-    index.exposed = True
-    
-    def showMessage(self):
-        # Here's the important message!
-        return "Hello world!"
-    showMessage.exposed = True
-
-cherrypy.tree.mount(HelloWorld())
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
-
--- a/bundled/cherrypy/cherrypy/tutorial/tut03_get_and_post.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-"""
-Tutorial - Passing variables
-
-This tutorial shows you how to pass GET/POST variables to methods.
-"""
-
-import cherrypy
-
-
-class WelcomePage:
-
-    def index(self):
-        # Ask for the user's name.
-        return '''
-            <form action="greetUser" method="GET">
-            What is your name?
-            <input type="text" name="name" />
-            <input type="submit" />
-            </form>'''
-    index.exposed = True
-    
-    def greetUser(self, name = None):
-        # CherryPy passes all GET and POST variables as method parameters.
-        # It doesn't make a difference where the variables come from, how
-        # large their contents are, and so on.
-        #
-        # You can define default parameter values as usual. In this
-        # example, the "name" parameter defaults to None so we can check
-        # if a name was actually specified.
-        
-        if name:
-            # Greet the user!
-            return "Hey %s, what's up?" % name
-        else:
-            if name is None:
-                # No name was specified
-                return 'Please enter your name <a href="./">here</a>.'
-            else:
-                return 'No, really, enter your name <a href="./">here</a>.'
-    greetUser.exposed = True
-
-
-cherrypy.tree.mount(WelcomePage())
-
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
--- a/bundled/cherrypy/cherrypy/tutorial/tut04_complex_site.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-"""
-Tutorial - Multiple objects
-
-This tutorial shows you how to create a site structure through multiple
-possibly nested request handler objects.
-"""
-
-import cherrypy
-
-
-class HomePage:
-    def index(self):
-        return '''
-            <p>Hi, this is the home page! Check out the other
-            fun stuff on this site:</p>
-            
-            <ul>
-                <li><a href="/joke/">A silly joke</a></li>
-                <li><a href="/links/">Useful links</a></li>
-            </ul>'''
-    index.exposed = True
-
-
-class JokePage:
-    def index(self):
-        return '''
-            <p>"In Python, how do you create a string of random
-            characters?" -- "Read a Perl file!"</p>
-            <p>[<a href="../">Return</a>]</p>'''
-    index.exposed = True
-
-
-class LinksPage:
-    def __init__(self):
-        # Request handler objects can create their own nested request
-        # handler objects. Simply create them inside their __init__
-        # methods!
-        self.extra = ExtraLinksPage()
-    
-    def index(self):
-        # Note the way we link to the extra links page (and back).
-        # As you can see, this object doesn't really care about its
-        # absolute position in the site tree, since we use relative
-        # links exclusively.
-        return '''
-            <p>Here are some useful links:</p>
-            
-            <ul>
-                <li><a href="http://www.cherrypy.org">The CherryPy Homepage</a></li>
-                <li><a href="http://www.python.org">The Python Homepage</a></li>
-            </ul>
-            
-            <p>You can check out some extra useful
-            links <a href="./extra/">here</a>.</p>
-            
-            <p>[<a href="../">Return</a>]</p>
-        '''
-    index.exposed = True
-
-
-class ExtraLinksPage:
-    def index(self):
-        # Note the relative link back to the Links page!
-        return '''
-            <p>Here are some extra useful links:</p>
-            
-            <ul>
-                <li><a href="http://del.icio.us">del.icio.us</a></li>
-                <li><a href="http://www.mornography.de">Hendrik's weblog</a></li>
-            </ul>
-            
-            <p>[<a href="../">Return to links page</a>]</p>'''
-    index.exposed = True
-
-
-# Of course we can also mount request handler objects right here!
-root = HomePage()
-root.joke = JokePage()
-root.links = LinksPage()
-cherrypy.tree.mount(root)
-
-# Remember, we don't need to mount ExtraLinksPage here, because
-# LinksPage does that itself on initialization. In fact, there is
-# no reason why you shouldn't let your root object take care of
-# creating all contained request handler objects.
-
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
-
--- a/bundled/cherrypy/cherrypy/tutorial/tut05_derived_objects.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-"""
-Tutorial - Object inheritance
-
-You are free to derive your request handler classes from any base
-class you wish. In most real-world applications, you will probably
-want to create a central base class used for all your pages, which takes
-care of things like printing a common page header and footer.
-"""
-
-import cherrypy
-
-
-class Page:
-    # Store the page title in a class attribute
-    title = 'Untitled Page'
-    
-    def header(self):
-        return '''
-            <html>
-            <head>
-                <title>%s</title>
-            <head>
-            <body>
-            <h2>%s</h2>
-        ''' % (self.title, self.title)
-    
-    def footer(self):
-        return '''
-            </body>
-            </html>
-        '''
-    
-    # Note that header and footer don't get their exposed attributes
-    # set to True. This isn't necessary since the user isn't supposed
-    # to call header or footer directly; instead, we'll call them from
-    # within the actually exposed handler methods defined in this
-    # class' subclasses.
-
-
-class HomePage(Page):
-    # Different title for this page
-    title = 'Tutorial 5'
-    
-    def __init__(self):
-        # create a subpage
-        self.another = AnotherPage()
-    
-    def index(self):
-        # Note that we call the header and footer methods inherited
-        # from the Page class!
-        return self.header() + '''
-            <p>
-            Isn't this exciting? There's
-            <a href="./another/">another page</a>, too!
-            </p>
-        ''' + self.footer()
-    index.exposed = True
-
-
-class AnotherPage(Page):
-    title = 'Another Page'
-    
-    def index(self):
-        return self.header() + '''
-            <p>
-            And this is the amazing second page!
-            </p>
-        ''' + self.footer()
-    index.exposed = True
-
-
-cherrypy.tree.mount(HomePage())
-
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
--- a/bundled/cherrypy/cherrypy/tutorial/tut06_default_method.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-"""
-Tutorial - The default method
-
-Request handler objects can implement a method called "default" that
-is called when no other suitable method/object could be found.
-Essentially, if CherryPy2 can't find a matching request handler object
-for the given request URI, it will use the default method of the object
-located deepest on the URI path.
-
-Using this mechanism you can easily simulate virtual URI structures
-by parsing the extra URI string, which you can access through
-cherrypy.request.virtualPath.
-
-The application in this tutorial simulates an URI structure looking
-like /users/<username>. Since the <username> bit will not be found (as
-there are no matching methods), it is handled by the default method.
-"""
-
-import cherrypy
-
-
-class UsersPage:
-    
-    def index(self):
-        # Since this is just a stupid little example, we'll simply
-        # display a list of links to random, made-up users. In a real
-        # application, this could be generated from a database result set.
-        return '''
-            <a href="./remi">Remi Delon</a><br/>
-            <a href="./hendrik">Hendrik Mans</a><br/>
-            <a href="./lorenzo">Lorenzo Lamas</a><br/>
-        '''
-    index.exposed = True
-    
-    def default(self, user):
-        # Here we react depending on the virtualPath -- the part of the
-        # path that could not be mapped to an object method. In a real
-        # application, we would probably do some database lookups here
-        # instead of the silly if/elif/else construct.
-        if user == 'remi':
-            out = "Remi Delon, CherryPy lead developer"
-        elif user == 'hendrik':
-            out = "Hendrik Mans, CherryPy co-developer & crazy German"
-        elif user == 'lorenzo':
-            out = "Lorenzo Lamas, famous actor and singer!"
-        else:
-            out = "Unknown user. :-("
-        
-        return '%s (<a href="./">back</a>)' % out
-    default.exposed = True
-
-
-cherrypy.tree.mount(UsersPage())
-
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
--- a/bundled/cherrypy/cherrypy/tutorial/tut07_sessions.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-"""
-Tutorial - Sessions
-
-Storing session data in CherryPy applications is very easy: cherrypy
-provides a dictionary called "session" that represents the session
-data for the current user. If you use RAM based sessions, you can store
-any kind of object into that dictionary; otherwise, you are limited to
-objects that can be pickled.
-"""
-
-import cherrypy
-
-
-class HitCounter:
-    
-    _cp_config = {'tools.sessions.on': True}
-    
-    def index(self):
-        # Increase the silly hit counter
-        count = cherrypy.session.get('count', 0) + 1
-        
-        # Store the new value in the session dictionary
-        cherrypy.session['count'] = count
-        
-        # And display a silly hit count message!
-        return '''
-            During your current session, you've viewed this
-            page %s times! Your life is a patio of fun!
-        ''' % count
-    index.exposed = True
-
-
-cherrypy.tree.mount(HitCounter())
-
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
--- a/bundled/cherrypy/cherrypy/tutorial/tut08_generators_and_yield.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-"""
-Bonus Tutorial: Using generators to return result bodies
-
-Instead of returning a complete result string, you can use the yield
-statement to return one result part after another. This may be convenient
-in situations where using a template package like CherryPy or Cheetah
-would be overkill, and messy string concatenation too uncool. ;-)
-"""
-
-import cherrypy
-
-
-class GeneratorDemo:
-    
-    def header(self):
-        return "<html><body><h2>Generators rule!</h2>"
-    
-    def footer(self):
-        return "</body></html>"
-    
-    def index(self):
-        # Let's make up a list of users for presentation purposes
-        users = ['Remi', 'Carlos', 'Hendrik', 'Lorenzo Lamas']
-        
-        # Every yield line adds one part to the total result body.
-        yield self.header()
-        yield "<h3>List of users:</h3>"
-        
-        for user in users:
-            yield "%s<br/>" % user
-            
-        yield self.footer()
-    index.exposed = True
-
-cherrypy.tree.mount(GeneratorDemo())
-
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
--- a/bundled/cherrypy/cherrypy/tutorial/tut09_files.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-"""
-
-Tutorial: File upload and download
-
-Uploads
--------
-
-When a client uploads a file to a CherryPy application, it's placed
-on disk immediately. CherryPy will pass it to your exposed method
-as an argument (see "myFile" below); that arg will have a "file"
-attribute, which is a handle to the temporary uploaded file.
-If you wish to permanently save the file, you need to read()
-from myFile.file and write() somewhere else.
-
-Note the use of 'enctype="multipart/form-data"' and 'input type="file"'
-in the HTML which the client uses to upload the file.
-
-
-Downloads
----------
-
-If you wish to send a file to the client, you have two options:
-First, you can simply return a file-like object from your page handler.
-CherryPy will read the file and serve it as the content (HTTP body)
-of the response. However, that doesn't tell the client that
-the response is a file to be saved, rather than displayed.
-Use cherrypy.lib.static.serve_file for that; it takes four
-arguments:
-
-serve_file(path, content_type=None, disposition=None, name=None)
-
-Set "name" to the filename that you expect clients to use when they save
-your file. Note that the "name" argument is ignored if you don't also
-provide a "disposition" (usually "attachement"). You can manually set
-"content_type", but be aware that if you also use the encoding tool, it
-may choke if the file extension is not recognized as belonging to a known
-Content-Type. Setting the content_type to "application/x-download" works
-in most cases, and should prompt the user with an Open/Save dialog in
-popular browsers.
-
-"""
-
-import os
-localDir = os.path.dirname(__file__)
-absDir = os.path.join(os.getcwd(), localDir)
-
-import cherrypy
-from cherrypy.lib import static
-
-
-class FileDemo(object):
-    
-    def index(self):
-        return """
-        <html><body>
-            <form action="upload" method="post" enctype="multipart/form-data">
-            filename: <input type="file" name="myFile" /><br />
-            <input type="submit" />
-            </form>
-        </body></html>
-        """
-    index.exposed = True
-    
-    def upload(self, myFile):
-        out = """<html>
-        <body>
-            myFile length: %s<br />
-            myFile filename: %s<br />
-            myFile mime-type: %s
-        </body>
-        </html>"""
-        
-        # Although this just counts the file length, it demonstrates
-        # how to read large files in chunks instead of all at once.
-        # CherryPy reads the uploaded file into a temporary file;
-        # myFile.file.read reads from that.
-        size = 0
-        while True:
-            data = myFile.file.read(8192)
-            if not data:
-                break
-            size += len(data)
-        
-        return out % (size, myFile.filename, myFile.content_type)
-    upload.exposed = True
-    
-    def download(self):
-        path = os.path.join(absDir, "pdf_file.pdf")
-        return static.serve_file(path, "application/x-download",
-                                 "attachment", os.path.basename(path))
-    download.exposed = True
-
-
-cherrypy.tree.mount(FileDemo())
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
--- a/bundled/cherrypy/cherrypy/tutorial/tut10_http_errors.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-"""
-
-Tutorial: HTTP errors
-
-HTTPError is used to return an error response to the client.
-CherryPy has lots of options regarding how such errors are
-logged, displayed, and formatted.
-
-"""
-
-import os
-localDir = os.path.dirname(__file__)
-curpath = os.path.normpath(os.path.join(os.getcwd(), localDir))
-
-import cherrypy
-
-
-class HTTPErrorDemo(object):
-    
-    # Set a custom response for 403 errors.
-    _cp_config = {'error_page.403' : os.path.join(curpath, "custom_error.html")}
-    
-    def index(self):
-        # display some links that will result in errors
-        tracebacks = cherrypy.request.show_tracebacks
-        if tracebacks:
-            trace = 'off'
-        else:
-            trace = 'on'
-            
-        return """
-        <html><body>
-            <h2><a href="toggleTracebacks">Toggle tracebacks %s</a></h2>
-            <p><a href="/doesNotExist">Click me; I'm a broken link!</a></p>
-            <p><a href="/error?code=403">Use a custom an error page from a file.</a></p>
-            <p>These errors are explicitly raised by the application:</p>
-            <ul>
-                <li><a href="/error?code=400">400</a></li>
-                <li><a href="/error?code=401">401</a></li>
-                <li><a href="/error?code=402">402</a></li>
-                <li><a href="/error?code=500">500</a></li>
-            </ul>
-            <p><a href="/messageArg">You can also set the response body
-            when you raise an error.</a></p>
-        </body></html>
-        """ % trace
-    index.exposed = True
-    
-    def toggleTracebacks(self):
-        # simple function to toggle tracebacks on and off 
-        tracebacks = cherrypy.request.show_tracebacks
-        cherrypy.config.update({'request.show_tracebacks': not tracebacks})
-        
-        # redirect back to the index
-        raise cherrypy.HTTPRedirect('/')
-    toggleTracebacks.exposed = True
-    
-    def error(self, code):
-        # raise an error based on the get query
-        raise cherrypy.HTTPError(status = code)
-    error.exposed = True
-    
-    def messageArg(self):
-        message = ("If you construct an HTTPError with a 'message' "
-                   "argument, it wil be placed on the error page "
-                   "(underneath the status line by default).")
-        raise cherrypy.HTTPError(500, message=message)
-    messageArg.exposed = True
-
-
-cherrypy.tree.mount(HTTPErrorDemo())
-
-
-if __name__ == '__main__':
-    import os.path
-    thisdir = os.path.dirname(__file__)
-    cherrypy.quickstart(config=os.path.join(thisdir, 'tutorial.conf'))
--- a/bundled/cherrypy/cherrypy/tutorial/tutorial.conf	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-[global]
-server.socket_host = "127.0.0.1"
-server.socket_port = 8080
-server.thread_pool = 10
--- a/bundled/cherrypy/cherrypy/wsgiserver/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2074 +0,0 @@
-"""A high-speed, production ready, thread pooled, generic HTTP server.
-
-Simplest example on how to use this module directly
-(without using CherryPy's application machinery):
-
-    from cherrypy import wsgiserver
-    
-    def my_crazy_app(environ, start_response):
-        status = '200 OK'
-        response_headers = [('Content-type','text/plain')]
-        start_response(status, response_headers)
-        return ['Hello world!\n']
-    
-    server = wsgiserver.CherryPyWSGIServer(
-                ('0.0.0.0', 8070), my_crazy_app,
-                server_name='www.cherrypy.example')
-    
-The CherryPy WSGI server can serve as many WSGI applications 
-as you want in one instance by using a WSGIPathInfoDispatcher:
-    
-    d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
-    server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
-    
-Want SSL support? Just set server.ssl_adapter to an SSLAdapter instance.
-
-This won't call the CherryPy engine (application side) at all, only the
-HTTP server, which is independent from the rest of CherryPy. Don't
-let the name "CherryPyWSGIServer" throw you; the name merely reflects
-its origin, not its coupling.
-
-For those of you wanting to understand internals of this module, here's the
-basic call flow. The server's listening thread runs a very tight loop,
-sticking incoming connections onto a Queue:
-
-    server = CherryPyWSGIServer(...)
-    server.start()
-    while True:
-        tick()
-        # This blocks until a request comes in:
-        child = socket.accept()
-        conn = HTTPConnection(child, ...)
-        server.requests.put(conn)
-
-Worker threads are kept in a pool and poll the Queue, popping off and then
-handling each connection in turn. Each connection can consist of an arbitrary
-number of requests and their responses, so we run a nested loop:
-
-    while True:
-        conn = server.requests.get()
-        conn.communicate()
-        ->  while True:
-                req = HTTPRequest(...)
-                req.parse_request()
-                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
-                    req.rfile.readline()
-                    read_headers(req.rfile, req.inheaders)
-                req.respond()
-                ->  response = app(...)
-                    try:
-                        for chunk in response:
-                            if chunk:
-                                req.write(chunk)
-                    finally:
-                        if hasattr(response, "close"):
-                            response.close()
-                if req.close_connection:
-                    return
-"""
-
-CRLF = '\r\n'
-import os
-import Queue
-import re
-quoted_slash = re.compile("(?i)%2F")
-import rfc822
-import socket
-import sys
-if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'):
-    socket.IPPROTO_IPV6 = 41
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
-
-_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
-
-import threading
-import time
-import traceback
-from urllib import unquote
-from urlparse import urlparse
-import warnings
-
-import errno
-
-def plat_specific_errors(*errnames):
-    """Return error numbers for all errors in errnames on this platform.
-    
-    The 'errno' module contains different global constants depending on
-    the specific platform (OS). This function will return the list of
-    numeric values for a given list of potential names.
-    """
-    errno_names = dir(errno)
-    nums = [getattr(errno, k) for k in errnames if k in errno_names]
-    # de-dupe the list
-    return dict.fromkeys(nums).keys()
-
-socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
-
-socket_errors_to_ignore = plat_specific_errors(
-    "EPIPE",
-    "EBADF", "WSAEBADF",
-    "ENOTSOCK", "WSAENOTSOCK",
-    "ETIMEDOUT", "WSAETIMEDOUT",
-    "ECONNREFUSED", "WSAECONNREFUSED",
-    "ECONNRESET", "WSAECONNRESET",
-    "ECONNABORTED", "WSAECONNABORTED",
-    "ENETRESET", "WSAENETRESET",
-    "EHOSTDOWN", "EHOSTUNREACH",
-    )
-socket_errors_to_ignore.append("timed out")
-socket_errors_to_ignore.append("The read operation timed out")
-
-socket_errors_nonblocking = plat_specific_errors(
-    'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
-
-comma_separated_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding',
-    'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
-    'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
-    'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
-    'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
-    'WWW-Authenticate']
-
-
-def read_headers(rfile, hdict=None):
-    """Read headers from the given stream into the given header dict.
-    
-    If hdict is None, a new header dict is created. Returns the populated
-    header dict.
-    
-    Headers which are repeated are folded together using a comma if their
-    specification so dictates.
-    
-    This function raises ValueError when the read bytes violate the HTTP spec.
-    You should probably return "400 Bad Request" if this happens.
-    """
-    if hdict is None:
-        hdict = {}
-    
-    while True:
-        line = rfile.readline()
-        if not line:
-            # No more data--illegal end of headers
-            raise ValueError("Illegal end of headers.")
-        
-        if line == CRLF:
-            # Normal end of headers
-            break
-        if not line.endswith(CRLF):
-            raise ValueError("HTTP requires CRLF terminators")
-        
-        if line[0] in ' \t':
-            # It's a continuation line.
-            v = line.strip()
-        else:
-            try:
-                k, v = line.split(":", 1)
-            except ValueError:
-                raise ValueError("Illegal header line.")
-            # TODO: what about TE and WWW-Authenticate?
-            k = k.strip().title()
-            v = v.strip()
-            hname = k
-        
-        if k in comma_separated_headers:
-            existing = hdict.get(hname)
-            if existing:
-                v = ", ".join((existing, v))
-        hdict[hname] = v
-    
-    return hdict
-
-
-class MaxSizeExceeded(Exception):
-    pass
-
-class SizeCheckWrapper(object):
-    """Wraps a file-like object, raising MaxSizeExceeded if too large."""
-    
-    def __init__(self, rfile, maxlen):
-        self.rfile = rfile
-        self.maxlen = maxlen
-        self.bytes_read = 0
-    
-    def _check_length(self):
-        if self.maxlen and self.bytes_read > self.maxlen:
-            raise MaxSizeExceeded()
-    
-    def read(self, size=None):
-        data = self.rfile.read(size)
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-    
-    def readline(self, size=None):
-        if size is not None:
-            data = self.rfile.readline(size)
-            self.bytes_read += len(data)
-            self._check_length()
-            return data
-        
-        # User didn't specify a size ...
-        # We read the line in chunks to make sure it's not a 100MB line !
-        res = []
-        while True:
-            data = self.rfile.readline(256)
-            self.bytes_read += len(data)
-            self._check_length()
-            res.append(data)
-            # See http://www.cherrypy.org/ticket/421
-            if len(data) < 256 or data[-1:] == "\n":
-                return ''.join(res)
-    
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline()
-        return lines
-    
-    def close(self):
-        self.rfile.close()
-    
-    def __iter__(self):
-        return self
-    
-    def next(self):
-        data = self.rfile.next()
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-
-
-class KnownLengthRFile(object):
-    """Wraps a file-like object, returning an empty string when exhausted."""
-    
-    def __init__(self, rfile, content_length):
-        self.rfile = rfile
-        self.remaining = content_length
-    
-    def read(self, size=None):
-        if self.remaining == 0:
-            return ''
-        if size is None:
-            size = self.remaining
-        else:
-            size = min(size, self.remaining)
-        
-        data = self.rfile.read(size)
-        self.remaining -= len(data)
-        return data
-    
-    def readline(self, size=None):
-        if self.remaining == 0:
-            return ''
-        if size is None:
-            size = self.remaining
-        else:
-            size = min(size, self.remaining)
-        
-        data = self.rfile.readline(size)
-        self.remaining -= len(data)
-        return data
-    
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline(sizehint)
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-        return lines
-    
-    def close(self):
-        self.rfile.close()
-    
-    def __iter__(self):
-        return self
-    
-    def __next__(self):
-        data = next(self.rfile)
-        self.remaining -= len(data)
-        return data
-
-
-class MaxSizeExceeded(Exception):
-    pass
-
-
-class ChunkedRFile(object):
-    """Wraps a file-like object, returning an empty string when exhausted.
-    
-    This class is intended to provide a conforming wsgi.input value for
-    request entities that have been encoded with the 'chunked' transfer
-    encoding.
-    """
-    
-    def __init__(self, rfile, maxlen, bufsize=8192):
-        self.rfile = rfile
-        self.maxlen = maxlen
-        self.bytes_read = 0
-        self.buffer = ''
-        self.bufsize = bufsize
-        self.closed = False
-    
-    def _fetch(self):
-        if self.closed:
-            return
-        
-        line = self.rfile.readline()
-        self.bytes_read += len(line)
-        
-        if self.maxlen and self.bytes_read > self.maxlen:
-            raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
-        
-        line = line.strip().split(";", 1)
-        
-        try:
-            chunk_size = line.pop(0)
-            chunk_size = int(chunk_size, 16)
-        except ValueError:
-            raise ValueError("Bad chunked transfer size: " + repr(chunk_size))
-        
-        if chunk_size <= 0:
-            self.closed = True
-            return
-        
-##            if line: chunk_extension = line[0]
-        
-        if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
-            raise IOError("Request Entity Too Large")
-        
-        chunk = self.rfile.read(chunk_size)
-        self.bytes_read += len(chunk)
-        self.buffer += chunk
-        
-        crlf = self.rfile.read(2)
-        if crlf != CRLF:
-            raise ValueError(
-                 "Bad chunked transfer coding (expected '\\r\\n', "
-                 "got " + repr(crlf) + ")")
-    
-    def read(self, size=None):
-        data = ''
-        while True:
-            if size and len(data) >= size:
-                return data
-            
-            if not self.buffer:
-                self._fetch()
-                if not self.buffer:
-                    # EOF
-                    return data
-            
-            if size:
-                remaining = size - len(data)
-                data += self.buffer[:remaining]
-                self.buffer = self.buffer[remaining:]
-            else:
-                data += self.buffer
-    
-    def readline(self, size=None):
-        data = ''
-        while True:
-            if size and len(data) >= size:
-                return data
-            
-            if not self.buffer:
-                self._fetch()
-                if not self.buffer:
-                    # EOF
-                    return data
-            
-            newline_pos = self.buffer.find('\n')
-            if size:
-                if newline_pos == -1:
-                    remaining = size - len(data)
-                    data += self.buffer[:remaining]
-                    self.buffer = self.buffer[remaining:]
-                else:
-                    remaining = min(size - len(data), newline_pos)
-                    data += self.buffer[:remaining]
-                    self.buffer = self.buffer[remaining:]
-            else:
-                if newline_pos == -1:
-                    data += self.buffer
-                else:
-                    data += self.buffer[:newline_pos]
-                    self.buffer = self.buffer[newline_pos:]
-    
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline(sizehint)
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-        return lines
-    
-    def read_trailer_lines(self):
-        if not self.closed:
-            raise ValueError(
-                "Cannot read trailers until the request body has been read.")
-        
-        while True:
-            line = self.rfile.readline()
-            if not line:
-                # No more data--illegal end of headers
-                raise ValueError("Illegal end of headers.")
-            
-            self.bytes_read += len(line)
-            if self.maxlen and self.bytes_read > self.maxlen:
-                raise IOError("Request Entity Too Large")
-            
-            if line == CRLF:
-                # Normal end of headers
-                break
-            if not line.endswith(CRLF):
-                raise ValueError("HTTP requires CRLF terminators")
-            
-            yield line
-    
-    def close(self):
-        self.rfile.close()
-    
-    def __iter__(self):
-        # Shamelessly stolen from StringIO
-        total = 0
-        line = self.readline(sizehint)
-        while line:
-            yield line
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline(sizehint)
-
-
-class HTTPRequest(object):
-    """An HTTP Request (and response).
-    
-    A single HTTP connection may consist of multiple request/response pairs.
-    
-    server: the Server object which is receiving this request.
-    conn: the HTTPConnection object on which this request connected.
-    
-    inheaders: a dict of request headers.
-    outheaders: a list of header tuples to write in the response.
-    ready: when True, the request has been parsed and is ready to begin
-        generating the response. When False, signals the calling Connection
-        that the response should not be generated and the connection should
-        close.
-    close_connection: signals the calling Connection that the request
-        should close. This does not imply an error! The client and/or
-        server may each request that the connection be closed.
-    chunked_write: if True, output will be encoded with the "chunked"
-        transfer-coding. This value is set automatically inside
-        send_headers.
-    """
-    
-    def __init__(self, server, conn):
-        self.server= server
-        self.conn = conn
-        
-        self.ready = False
-        self.started_request = False
-        self.scheme = "http"
-        if self.server.ssl_adapter is not None:
-            self.scheme = "https"
-        self.inheaders = {}
-        
-        self.status = ""
-        self.outheaders = []
-        self.sent_headers = False
-        self.close_connection = False
-        self.chunked_write = False
-    
-    def parse_request(self):
-        """Parse the next HTTP request start-line and message-headers."""
-        self.rfile = SizeCheckWrapper(self.conn.rfile,
-                                      self.server.max_request_header_size)
-        try:
-            self._parse_request()
-        except MaxSizeExceeded:
-            self.simple_response("413 Request Entity Too Large")
-            return
-    
-    def _parse_request(self):
-        # HTTP/1.1 connections are persistent by default. If a client
-        # requests a page, then idles (leaves the connection open),
-        # then rfile.readline() will raise socket.error("timed out").
-        # Note that it does this based on the value given to settimeout(),
-        # and doesn't need the client to request or acknowledge the close
-        # (although your TCP stack might suffer for it: cf Apache's history
-        # with FIN_WAIT_2).
-        request_line = self.rfile.readline()
-        
-        # Set started_request to True so communicate() knows to send 408
-        # from here on out.
-        self.started_request = True
-        if not request_line:
-            # Force self.ready = False so the connection will close.
-            self.ready = False
-            return
-        
-        if request_line == CRLF:
-            # RFC 2616 sec 4.1: "...if the server is reading the protocol
-            # stream at the beginning of a message and receives a CRLF
-            # first, it should ignore the CRLF."
-            # But only ignore one leading line! else we enable a DoS.
-            request_line = self.rfile.readline()
-            if not request_line:
-                self.ready = False
-                return
-        
-        if not request_line.endswith(CRLF):
-            self.simple_response(400, "HTTP requires CRLF terminators")
-            return
-        
-        try:
-            method, uri, req_protocol = request_line.strip().split(" ", 2)
-        except ValueError:
-            self.simple_response(400, "Malformed Request-Line")
-            return
-        
-        self.uri = uri
-        self.method = method
-        
-        # uri may be an abs_path (including "http://host.domain.tld");
-        scheme, authority, path = self.parse_request_uri(uri)
-        if '#' in path:
-            self.simple_response("400 Bad Request",
-                                 "Illegal #fragment in Request-URI.")
-            return
-        
-        if scheme:
-            self.scheme = scheme
-        
-        qs = ''
-        if '?' in path:
-            path, qs = path.split('?', 1)
-        
-        # Unquote the path+params (e.g. "/this%20path" -> "/this path").
-        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
-        #
-        # But note that "...a URI must be separated into its components
-        # before the escaped characters within those components can be
-        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
-        # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
-        try:
-            atoms = [unquote(x) for x in quoted_slash.split(path)]
-        except ValueError, ex:
-            self.simple_response("400 Bad Request", ex.args[0])
-            return
-        path = "%2F".join(atoms)
-        self.path = path
-        
-        # Note that, like wsgiref and most other HTTP servers,
-        # we "% HEX HEX"-unquote the path but not the query string.
-        self.qs = qs
-        
-        # Compare request and server HTTP protocol versions, in case our
-        # server does not support the requested protocol. Limit our output
-        # to min(req, server). We want the following output:
-        #     request    server     actual written   supported response
-        #     protocol   protocol  response protocol    feature set
-        # a     1.0        1.0           1.0                1.0
-        # b     1.0        1.1           1.1                1.0
-        # c     1.1        1.0           1.0                1.0
-        # d     1.1        1.1           1.1                1.1
-        # Notice that, in (b), the response will be "HTTP/1.1" even though
-        # the client only understands 1.0. RFC 2616 10.5.6 says we should
-        # only return 505 if the _major_ version is different.
-        rp = int(req_protocol[5]), int(req_protocol[7])
-        sp = int(self.server.protocol[5]), int(self.server.protocol[7])
-        
-        if sp[0] != rp[0]:
-            self.simple_response("505 HTTP Version Not Supported")
-            return
-        self.request_protocol = req_protocol
-        self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
-        
-        # then all the http headers
-        try:
-            read_headers(self.rfile, self.inheaders)
-        except ValueError, ex:
-            self.simple_response("400 Bad Request", ex.args[0])
-            return
-        
-        mrbs = self.server.max_request_body_size
-        if mrbs and int(self.inheaders.get("Content-Length", 0)) > mrbs:
-            self.simple_response("413 Request Entity Too Large")
-            return
-        
-        # Persistent connection support
-        if self.response_protocol == "HTTP/1.1":
-            # Both server and client are HTTP/1.1
-            if self.inheaders.get("Connection", "") == "close":
-                self.close_connection = True
-        else:
-            # Either the server or client (or both) are HTTP/1.0
-            if self.inheaders.get("Connection", "") != "Keep-Alive":
-                self.close_connection = True
-        
-        # Transfer-Encoding support
-        te = None
-        if self.response_protocol == "HTTP/1.1":
-            te = self.inheaders.get("Transfer-Encoding")
-            if te:
-                te = [x.strip().lower() for x in te.split(",") if x.strip()]
-        
-        self.chunked_read = False
-        
-        if te:
-            for enc in te:
-                if enc == "chunked":
-                    self.chunked_read = True
-                else:
-                    # Note that, even if we see "chunked", we must reject
-                    # if there is an extension we don't recognize.
-                    self.simple_response("501 Unimplemented")
-                    self.close_connection = True
-                    return
-        
-        # From PEP 333:
-        # "Servers and gateways that implement HTTP 1.1 must provide
-        # transparent support for HTTP 1.1's "expect/continue" mechanism.
-        # This may be done in any of several ways:
-        #   1. Respond to requests containing an Expect: 100-continue request
-        #      with an immediate "100 Continue" response, and proceed normally.
-        #   2. Proceed with the request normally, but provide the application
-        #      with a wsgi.input stream that will send the "100 Continue"
-        #      response if/when the application first attempts to read from
-        #      the input stream. The read request must then remain blocked
-        #      until the client responds.
-        #   3. Wait until the client decides that the server does not support
-        #      expect/continue, and sends the request body on its own.
-        #      (This is suboptimal, and is not recommended.)
-        #
-        # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
-        # but it seems like it would be a big slowdown for such a rare case.
-        if self.inheaders.get("Expect", "") == "100-continue":
-            # Don't use simple_response here, because it emits headers
-            # we don't want. See http://www.cherrypy.org/ticket/951
-            msg = self.server.protocol + " 100 Continue\r\n\r\n"
-            try:
-                self.conn.wfile.sendall(msg)
-            except socket.error, x:
-                if x.args[0] not in socket_errors_to_ignore:
-                    raise
-        
-        self.ready = True
-    
-    def parse_request_uri(self, uri):
-        """Parse a Request-URI into (scheme, authority, path).
-        
-        Note that Request-URI's must be one of:
-            
-            Request-URI    = "*" | absoluteURI | abs_path | authority
-        
-        Therefore, a Request-URI which starts with a double forward-slash
-        cannot be a "net_path":
-        
-            net_path      = "//" authority [ abs_path ]
-        
-        Instead, it must be interpreted as an "abs_path" with an empty first
-        path segment:
-        
-            abs_path      = "/"  path_segments
-            path_segments = segment *( "/" segment )
-            segment       = *pchar *( ";" param )
-            param         = *pchar
-        """
-        if uri == "*":
-            return None, None, uri
-        
-        i = uri.find('://')
-        if i > 0 and '?' not in uri[:i]:
-            # An absoluteURI.
-            # If there's a scheme (and it must be http or https), then:
-            # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
-            scheme, remainder = uri[:i].lower(), uri[i + 3:]
-            authority, path = remainder.split("/", 1)
-            return scheme, authority, path
-        
-        if uri.startswith('/'):
-            # An abs_path.
-            return None, None, uri
-        else:
-            # An authority.
-            return None, uri, None
-    
-    def respond(self):
-        """Call the gateway and write its iterable output."""
-        mrbs = self.server.max_request_body_size
-        if self.chunked_read:
-            self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
-        else:
-            cl = int(self.inheaders.get("Content-Length", 0))
-            if mrbs and mrbs < cl:
-                if not self.sent_headers:
-                    self.simple_response("413 Request Entity Too Large")
-                return
-            self.rfile = KnownLengthRFile(self.conn.rfile, cl)
-        
-        self.server.gateway(self).respond()
-        
-        if (self.ready and not self.sent_headers):
-            self.sent_headers = True
-            self.send_headers()
-        if self.chunked_write:
-            self.conn.wfile.sendall("0\r\n\r\n")
-    
-    def simple_response(self, status, msg=""):
-        """Write a simple response back to the client."""
-        status = str(status)
-        buf = [self.server.protocol + " " +
-               status + CRLF,
-               "Content-Length: %s\r\n" % len(msg),
-               "Content-Type: text/plain\r\n"]
-        
-        if status[:3] == "413" and self.response_protocol == 'HTTP/1.1':
-            # Request Entity Too Large
-            self.close_connection = True
-            buf.append("Connection: close\r\n")
-        
-        buf.append(CRLF)
-        if msg:
-            if isinstance(msg, unicode):
-                msg = msg.encode("ISO-8859-1")
-            buf.append(msg)
-        
-        try:
-            self.conn.wfile.sendall("".join(buf))
-        except socket.error, x:
-            if x.args[0] not in socket_errors_to_ignore:
-                raise
-    
-    def write(self, chunk):
-        """Write unbuffered data to the client."""
-        if self.chunked_write and chunk:
-            buf = [hex(len(chunk))[2:], CRLF, chunk, CRLF]
-            self.conn.wfile.sendall("".join(buf))
-        else:
-            self.conn.wfile.sendall(chunk)
-    
-    def send_headers(self):
-        """Assert, process, and send the HTTP response message-headers.
-        
-        You must set self.status, and self.outheaders before calling this.
-        """
-        hkeys = [key.lower() for key, value in self.outheaders]
-        status = int(self.status[:3])
-        
-        if status == 413:
-            # Request Entity Too Large. Close conn to avoid garbage.
-            self.close_connection = True
-        elif "content-length" not in hkeys:
-            # "All 1xx (informational), 204 (no content),
-            # and 304 (not modified) responses MUST NOT
-            # include a message-body." So no point chunking.
-            if status < 200 or status in (204, 205, 304):
-                pass
-            else:
-                if (self.response_protocol == 'HTTP/1.1'
-                    and self.method != 'HEAD'):
-                    # Use the chunked transfer-coding
-                    self.chunked_write = True
-                    self.outheaders.append(("Transfer-Encoding", "chunked"))
-                else:
-                    # Closing the conn is the only way to determine len.
-                    self.close_connection = True
-        
-        if "connection" not in hkeys:
-            if self.response_protocol == 'HTTP/1.1':
-                # Both server and client are HTTP/1.1 or better
-                if self.close_connection:
-                    self.outheaders.append(("Connection", "close"))
-            else:
-                # Server and/or client are HTTP/1.0
-                if not self.close_connection:
-                    self.outheaders.append(("Connection", "Keep-Alive"))
-        
-        if (not self.close_connection) and (not self.chunked_read):
-            # Read any remaining request body data on the socket.
-            # "If an origin server receives a request that does not include an
-            # Expect request-header field with the "100-continue" expectation,
-            # the request includes a request body, and the server responds
-            # with a final status code before reading the entire request body
-            # from the transport connection, then the server SHOULD NOT close
-            # the transport connection until it has read the entire request,
-            # or until the client closes the connection. Otherwise, the client
-            # might not reliably receive the response message. However, this
-            # requirement is not be construed as preventing a server from
-            # defending itself against denial-of-service attacks, or from
-            # badly broken client implementations."
-            remaining = getattr(self.rfile, 'remaining', 0)
-            if remaining > 0:
-                self.rfile.read(remaining)
-        
-        if "date" not in hkeys:
-            self.outheaders.append(("Date", rfc822.formatdate()))
-        
-        if "server" not in hkeys:
-            self.outheaders.append(("Server", self.server.server_name))
-        
-        buf = [self.server.protocol + " " + self.status + CRLF]
-        for k, v in self.outheaders:
-            buf.append(k + ": " + v + CRLF)
-        buf.append(CRLF)
-        self.conn.wfile.sendall("".join(buf))
-
-
-class NoSSLError(Exception):
-    """Exception raised when a client speaks HTTP to an HTTPS socket."""
-    pass
-
-
-class FatalSSLAlert(Exception):
-    """Exception raised when the SSL implementation signals a fatal alert."""
-    pass
-
-
-if not _fileobject_uses_str_type:
-    class CP_fileobject(socket._fileobject):
-        """Faux file object attached to a socket object."""
-
-        def sendall(self, data):
-            """Sendall for non-blocking sockets."""
-            while data:
-                try:
-                    bytes_sent = self.send(data)
-                    data = data[bytes_sent:]
-                except socket.error, e:
-                    if e.args[0] not in socket_errors_nonblocking:
-                        raise
-
-        def send(self, data):
-            return self._sock.send(data)
-
-        def flush(self):
-            if self._wbuf:
-                buffer = "".join(self._wbuf)
-                self._wbuf = []
-                self.sendall(buffer)
-
-        def recv(self, size):
-            while True:
-                try:
-                    return self._sock.recv(size)
-                except socket.error, e:
-                    if (e.args[0] not in socket_errors_nonblocking
-                        and e.args[0] not in socket_error_eintr):
-                        raise
-
-        def read(self, size=-1):
-            # Use max, disallow tiny reads in a loop as they are very inefficient.
-            # We never leave read() with any leftover data from a new recv() call
-            # in our internal buffer.
-            rbufsize = max(self._rbufsize, self.default_bufsize)
-            # Our use of StringIO rather than lists of string objects returned by
-            # recv() minimizes memory usage and fragmentation that occurs when
-            # rbufsize is large compared to the typical return value of recv().
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if size < 0:
-                # Read until EOF
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(rbufsize)
-                    if not data:
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    # Already have size bytes in our buffer?  Extract and return.
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    left = size - buf_len
-                    # recv() will malloc the amount of memory given as its
-                    # parameter even though it often returns much less data
-                    # than that.  The returned data string is short lived
-                    # as we copy it into a StringIO and free it.  This avoids
-                    # fragmentation issues on many platforms.
-                    data = self.recv(left)
-                    if not data:
-                        break
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid buffer data copies when:
-                        # - We have no data in our buffer.
-                        # AND
-                        # - Our call to recv returned exactly the
-                        #   number of bytes we were asked to read.
-                        return data
-                    if n == left:
-                        buf.write(data)
-                        del data  # explicit free
-                        break
-                    assert n <= left, "recv(%d) returned %d bytes" % (left, n)
-                    buf.write(data)
-                    buf_len += n
-                    del data  # explicit free
-                    #assert buf_len == buf.tell()
-                return buf.getvalue()
-
-        def readline(self, size=-1):
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if buf.tell() > 0:
-                # check if we already have it in our buffer
-                buf.seek(0)
-                bline = buf.readline(size)
-                if bline.endswith('\n') or len(bline) == size:
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return bline
-                del bline
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    buf.seek(0)
-                    buffers = [buf.read()]
-                    self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                    data = None
-                    recv = self.recv
-                    while data != "\n":
-                        data = recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return "".join(buffers)
-
-                buf.seek(0, 2)  # seek end
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        buf.write(data[:nl])
-                        self._rbuf.write(data[nl:])
-                        del data
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes first
-                buf.seek(0, 2)  # seek end
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    left = size - buf_len
-                    # did we just receive a newline?
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        # save the excess data to _rbuf
-                        self._rbuf.write(data[nl:])
-                        if buf_len:
-                            buf.write(data[:nl])
-                            break
-                        else:
-                            # Shortcut.  Avoid data copy through buf when returning
-                            # a substring of our first recv().
-                            return data[:nl]
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid data copy through buf when
-                        # returning exactly all of our first recv().
-                        return data
-                    if n >= left:
-                        buf.write(data[:left])
-                        self._rbuf.write(data[left:])
-                        break
-                    buf.write(data)
-                    buf_len += n
-                    #assert buf_len == buf.tell()
-                return buf.getvalue()
-
-else:
-    class CP_fileobject(socket._fileobject):
-        """Faux file object attached to a socket object."""
-
-        def sendall(self, data):
-            """Sendall for non-blocking sockets."""
-            while data:
-                try:
-                    bytes_sent = self.send(data)
-                    data = data[bytes_sent:]
-                except socket.error, e:
-                    if e.args[0] not in socket_errors_nonblocking:
-                        raise
-
-        def send(self, data):
-            return self._sock.send(data)
-
-        def flush(self):
-            if self._wbuf:
-                buffer = "".join(self._wbuf)
-                self._wbuf = []
-                self.sendall(buffer)
-
-        def recv(self, size):
-            while True:
-                try:
-                    return self._sock.recv(size)
-                except socket.error, e:
-                    if (e.args[0] not in socket_errors_nonblocking
-                        and e.args[0] not in socket_error_eintr):
-                        raise
-
-        def read(self, size=-1):
-            if size < 0:
-                # Read until EOF
-                buffers = [self._rbuf]
-                self._rbuf = ""
-                if self._rbufsize <= 1:
-                    recv_size = self.default_bufsize
-                else:
-                    recv_size = self._rbufsize
-
-                while True:
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                return "".join(buffers)
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                data = self._rbuf
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    left = size - buf_len
-                    recv_size = max(self._rbufsize, left)
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return "".join(buffers)
-
-        def readline(self, size=-1):
-            data = self._rbuf
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    assert data == ""
-                    buffers = []
-                    while data != "\n":
-                        data = self.recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return "".join(buffers)
-                nl = data.find('\n')
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                return "".join(buffers)
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes first
-                nl = data.find('\n', 0, size)
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    left = size - buf_len
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return "".join(buffers)
-
-
-class HTTPConnection(object):
-    """An HTTP connection (active socket).
-    
-    server: the Server object which received this connection.
-    socket: the raw socket object (usually TCP) for this connection.
-    makefile: a fileobject class for reading from the socket.
-    """
-    
-    remote_addr = None
-    remote_port = None
-    ssl_env = None
-    rbufsize = -1
-    RequestHandlerClass = HTTPRequest
-    
-    def __init__(self, server, sock, makefile=CP_fileobject):
-        self.server = server
-        self.socket = sock
-        self.rfile = makefile(sock, "rb", self.rbufsize)
-        self.wfile = makefile(sock, "wb", -1)
-    
-    def communicate(self):
-        """Read each request and respond appropriately."""
-        request_seen = False
-        try:
-            while True:
-                # (re)set req to None so that if something goes wrong in
-                # the RequestHandlerClass constructor, the error doesn't
-                # get written to the previous request.
-                req = None
-                req = self.RequestHandlerClass(self.server, self)
-                
-                # This order of operations should guarantee correct pipelining.
-                req.parse_request()
-                if not req.ready:
-                    # Something went wrong in the parsing (and the server has
-                    # probably already made a simple_response). Return and
-                    # let the conn close.
-                    return
-                
-                request_seen = True
-                req.respond()
-                if req.close_connection:
-                    return
-        except socket.error, e:
-            errnum = e.args[0]
-            if errnum == 'timed out':
-                # Don't error if we're between requests; only error
-                # if 1) no request has been started at all, or 2) we're
-                # in the middle of a request.
-                # See http://www.cherrypy.org/ticket/853
-                if (not request_seen) or (req and req.started_request):
-                    # Don't bother writing the 408 if the response
-                    # has already started being written.
-                    if req and not req.sent_headers:
-                        try:
-                            req.simple_response("408 Request Timeout")
-                        except FatalSSLAlert:
-                            # Close the connection.
-                            return
-            elif errnum not in socket_errors_to_ignore:
-                if req and not req.sent_headers:
-                    try:
-                        req.simple_response("500 Internal Server Error",
-                                            format_exc())
-                    except FatalSSLAlert:
-                        # Close the connection.
-                        return
-            return
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except FatalSSLAlert:
-            # Close the connection.
-            return
-        except NoSSLError:
-            if req and not req.sent_headers:
-                # Unwrap our wfile
-                self.wfile = CP_fileobject(self.socket._sock, "wb", -1)
-                req.simple_response("400 Bad Request",
-                    "The client sent a plain HTTP request, but "
-                    "this server only speaks HTTPS on this port.")
-                self.linger = True
-        except Exception:
-            if req and not req.sent_headers:
-                try:
-                    req.simple_response("500 Internal Server Error", format_exc())
-                except FatalSSLAlert:
-                    # Close the connection.
-                    return
-    
-    linger = False
-    
-    def close(self):
-        """Close the socket underlying this connection."""
-        self.rfile.close()
-        
-        if not self.linger:
-            # Python's socket module does NOT call close on the kernel socket
-            # when you call socket.close(). We do so manually here because we
-            # want this server to send a FIN TCP segment immediately. Note this
-            # must be called *before* calling socket.close(), because the latter
-            # drops its reference to the kernel socket.
-            if hasattr(self.socket, '_sock'):
-                self.socket._sock.close()
-            self.socket.close()
-        else:
-            # On the other hand, sometimes we want to hang around for a bit
-            # to make sure the client has a chance to read our entire
-            # response. Skipping the close() calls here delays the FIN
-            # packet until the socket object is garbage-collected later.
-            # Someday, perhaps, we'll do the full lingering_close that
-            # Apache does, but not today.
-            pass
-
-
-def format_exc(limit=None):
-    """Like print_exc() but return a string. Backport for Python 2.3."""
-    try:
-        etype, value, tb = sys.exc_info()
-        return ''.join(traceback.format_exception(etype, value, tb, limit))
-    finally:
-        etype = value = tb = None
-
-
-_SHUTDOWNREQUEST = None
-
-class WorkerThread(threading.Thread):
-    """Thread which continuously polls a Queue for Connection objects.
-    
-    server: the HTTP Server which spawned this thread, and which owns the
-        Queue and is placing active connections into it.
-    ready: a simple flag for the calling server to know when this thread
-        has begun polling the Queue.
-    
-    Due to the timing issues of polling a Queue, a WorkerThread does not
-    check its own 'ready' flag after it has started. To stop the thread,
-    it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
-    (one for each running WorkerThread).
-    """
-    
-    conn = None
-    
-    def __init__(self, server):
-        self.ready = False
-        self.server = server
-        threading.Thread.__init__(self)
-    
-    def run(self):
-        try:
-            self.ready = True
-            while True:
-                conn = self.server.requests.get()
-                if conn is _SHUTDOWNREQUEST:
-                    return
-                
-                self.conn = conn
-                try:
-                    conn.communicate()
-                finally:
-                    conn.close()
-                    self.conn = None
-        except (KeyboardInterrupt, SystemExit), exc:
-            self.server.interrupt = exc
-
-
-class ThreadPool(object):
-    """A Request Queue for the CherryPyWSGIServer which pools threads.
-    
-    ThreadPool objects must provide min, get(), put(obj), start()
-    and stop(timeout) attributes.
-    """
-    
-    def __init__(self, server, min=10, max=-1):
-        self.server = server
-        self.min = min
-        self.max = max
-        self._threads = []
-        self._queue = Queue.Queue()
-        self.get = self._queue.get
-    
-    def start(self):
-        """Start the pool of threads."""
-        for i in range(self.min):
-            self._threads.append(WorkerThread(self.server))
-        for worker in self._threads:
-            worker.setName("CP Server " + worker.getName())
-            worker.start()
-        for worker in self._threads:
-            while not worker.ready:
-                time.sleep(.1)
-    
-    def _get_idle(self):
-        """Number of worker threads which are idle. Read-only."""
-        return len([t for t in self._threads if t.conn is None])
-    idle = property(_get_idle, doc=_get_idle.__doc__)
-    
-    def put(self, obj):
-        self._queue.put(obj)
-        if obj is _SHUTDOWNREQUEST:
-            return
-    
-    def grow(self, amount):
-        """Spawn new worker threads (not above self.max)."""
-        for i in range(amount):
-            if self.max > 0 and len(self._threads) >= self.max:
-                break
-            worker = WorkerThread(self.server)
-            worker.setName("CP Server " + worker.getName())
-            self._threads.append(worker)
-            worker.start()
-    
-    def shrink(self, amount):
-        """Kill off worker threads (not below self.min)."""
-        # Grow/shrink the pool if necessary.
-        # Remove any dead threads from our list
-        for t in self._threads:
-            if not t.isAlive():
-                self._threads.remove(t)
-                amount -= 1
-        
-        if amount > 0:
-            for i in range(min(amount, len(self._threads) - self.min)):
-                # Put a number of shutdown requests on the queue equal
-                # to 'amount'. Once each of those is processed by a worker,
-                # that worker will terminate and be culled from our list
-                # in self.put.
-                self._queue.put(_SHUTDOWNREQUEST)
-    
-    def stop(self, timeout=5):
-        # Must shut down threads here so the code that calls
-        # this method can know when all threads are stopped.
-        for worker in self._threads:
-            self._queue.put(_SHUTDOWNREQUEST)
-        
-        # Don't join currentThread (when stop is called inside a request).
-        current = threading.currentThread()
-        if timeout and timeout >= 0:
-            endtime = time.time() + timeout
-        while self._threads:
-            worker = self._threads.pop()
-            if worker is not current and worker.isAlive():
-                try:
-                    if timeout is None or timeout < 0:
-                        worker.join()
-                    else:
-                        remaining_time = endtime - time.time()
-                        if remaining_time > 0:
-                            worker.join(remaining_time)
-                        if worker.isAlive():
-                            # We exhausted the timeout.
-                            # Forcibly shut down the socket.
-                            c = worker.conn
-                            if c and not c.rfile.closed:
-                                try:
-                                    c.socket.shutdown(socket.SHUT_RD)
-                                except TypeError:
-                                    # pyOpenSSL sockets don't take an arg
-                                    c.socket.shutdown()
-                            worker.join()
-                except (AssertionError,
-                        # Ignore repeated Ctrl-C.
-                        # See http://www.cherrypy.org/ticket/691.
-                        KeyboardInterrupt), exc1:
-                    pass
-
-
-
-try:
-    import fcntl
-except ImportError:
-    try:
-        from ctypes import windll, WinError
-    except ImportError:
-        def prevent_socket_inheritance(sock):
-            """Dummy function, since neither fcntl nor ctypes are available."""
-            pass
-    else:
-        def prevent_socket_inheritance(sock):
-            """Mark the given socket fd as non-inheritable (Windows)."""
-            if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
-                raise WinError()
-else:
-    def prevent_socket_inheritance(sock):
-        """Mark the given socket fd as non-inheritable (POSIX)."""
-        fd = sock.fileno()
-        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
-        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
-
-
-class SSLAdapter(object):
-    
-    def __init__(self, certificate, private_key, certificate_chain=None):
-        self.certificate = certificate
-        self.private_key = private_key
-        self.certificate_chain = certificate_chain
-    
-    def wrap(self, sock):
-        raise NotImplemented
-    
-    def makefile(self, sock, mode='r', bufsize=-1):
-        raise NotImplemented
-
-
-class HTTPServer(object):
-    """An HTTP server.
-    
-    bind_addr: The interface on which to listen for connections.
-        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
-        or IPv6 address, or any valid hostname. The string 'localhost' is a
-        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
-        The string '0.0.0.0' is a special IPv4 entry meaning "any active
-        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
-        IPv6. The empty string or None are not allowed.
-        
-        For UNIX sockets, supply the filename as a string.
-    gateway: a Gateway instance.
-    minthreads: the minimum number of worker threads to create (default 10).
-    maxthreads: the maximum number of worker threads to create (default -1 = no limit).
-    server_name: defaults to socket.gethostname().
-    
-    request_queue_size: the 'backlog' argument to socket.listen();
-        specifies the maximum number of queued connections (default 5).
-    timeout: the timeout in seconds for accepted connections (default 10).
-    nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket
-        option.
-    protocol: the version string to write in the Status-Line of all
-        HTTP responses. For example, "HTTP/1.1" (the default). This
-        also limits the supported features used in the response.
-    
-    
-    SSL/HTTPS
-    ---------
-    You must have an ssl library installed and set self.ssl_adapter to an
-    instance of SSLAdapter (or a subclass) which provides the methods:
-        wrap(sock) -> wrapped socket, ssl environ dict
-        makefile(sock, mode='r', bufsize=-1) -> socket file object
-    """
-    
-    protocol = "HTTP/1.1"
-    _bind_addr = "127.0.0.1"
-    version = "CherryPy/3.2.0rc1"
-    response_header = None
-    ready = False
-    _interrupt = None
-    max_request_header_size = 0
-    max_request_body_size = 0
-    nodelay = True
-    
-    ConnectionClass = HTTPConnection
-    
-    ssl_adapter = None
-    
-    def __init__(self, bind_addr, gateway, minthreads=10, maxthreads=-1,
-                 server_name=None):
-        self.bind_addr = bind_addr
-        self.gateway = gateway
-        
-        self.requests = ThreadPool(self, min=minthreads or 1, max=maxthreads)
-        
-        if not server_name:
-            server_name = socket.gethostname()
-        self.server_name = server_name
-    
-    def __str__(self):
-        return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
-                              self.bind_addr)
-    
-    def _get_bind_addr(self):
-        return self._bind_addr
-    def _set_bind_addr(self, value):
-        if isinstance(value, tuple) and value[0] in ('', None):
-            # Despite the socket module docs, using '' does not
-            # allow AI_PASSIVE to work. Passing None instead
-            # returns '0.0.0.0' like we want. In other words:
-            #     host    AI_PASSIVE     result
-            #      ''         Y         192.168.x.y
-            #      ''         N         192.168.x.y
-            #     None        Y         0.0.0.0
-            #     None        N         127.0.0.1
-            # But since you can get the same effect with an explicit
-            # '0.0.0.0', we deny both the empty string and None as values.
-            raise ValueError("Host values of '' or None are not allowed. "
-                             "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
-                             "to listen on all active interfaces.")
-        self._bind_addr = value
-    bind_addr = property(_get_bind_addr, _set_bind_addr,
-        doc="""The interface on which to listen for connections.
-        
-        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
-        or IPv6 address, or any valid hostname. The string 'localhost' is a
-        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
-        The string '0.0.0.0' is a special IPv4 entry meaning "any active
-        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
-        IPv6. The empty string or None are not allowed.
-        
-        For UNIX sockets, supply the filename as a string.""")
-    
-    def start(self):
-        """Run the server forever."""
-        # We don't have to trap KeyboardInterrupt or SystemExit here,
-        # because cherrpy.server already does so, calling self.stop() for us.
-        # If you're using this server with another framework, you should
-        # trap those exceptions in whatever code block calls start().
-        self._interrupt = None
-        
-        # SSL backward compatibility
-        if (self.ssl_adapter is None and
-            getattr(self, 'ssl_certificate', None) and
-            getattr(self, 'ssl_private_key', None)):
-            warnings.warn(
-                    "SSL attributes are deprecated in CherryPy 3.2, and will "
-                    "be removed in CherryPy 3.3. Use an ssl_adapter attribute "
-                    "instead.",
-                    DeprecationWarning
-                )
-            try:
-                from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
-            except ImportError:
-                pass
-            else:
-                self.ssl_adapter = pyOpenSSLAdapter(
-                    self.ssl_certificate, self.ssl_private_key,
-                    getattr(self, 'ssl_certificate_chain', None))
-        
-        # Select the appropriate socket
-        if isinstance(self.bind_addr, basestring):
-            # AF_UNIX socket
-            
-            # So we can reuse the socket...
-            try: os.unlink(self.bind_addr)
-            except: pass
-            
-            # So everyone can access the socket...
-            try: os.chmod(self.bind_addr, 0777)
-            except: pass
-            
-            info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
-        else:
-            # AF_INET or AF_INET6 socket
-            # Get the correct address family for our host (allows IPv6 addresses)
-            host, port = self.bind_addr
-            try:
-                info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                          socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
-            except socket.gaierror:
-                if ':' in self.bind_addr[0]:
-                    info = [(socket.AF_INET6, socket.SOCK_STREAM,
-                             0, "", self.bind_addr + (0, 0))]
-                else:
-                    info = [(socket.AF_INET, socket.SOCK_STREAM,
-                             0, "", self.bind_addr)]
-        
-        self.socket = None
-        msg = "No socket could be created"
-        for res in info:
-            af, socktype, proto, canonname, sa = res
-            try:
-                self.bind(af, socktype, proto)
-            except socket.error, msg:
-                if self.socket:
-                    self.socket.close()
-                self.socket = None
-                continue
-            break
-        if not self.socket:
-            raise socket.error(msg)
-        
-        # Timeout so KeyboardInterrupt can be caught on Win32
-        self.socket.settimeout(1)
-        self.socket.listen(self.request_queue_size)
-        
-        # Create worker threads
-        self.requests.start()
-        
-        self.ready = True
-        while self.ready:
-            self.tick()
-            if self.interrupt:
-                while self.interrupt is True:
-                    # Wait for self.stop() to complete. See _set_interrupt.
-                    time.sleep(0.1)
-                if self.interrupt:
-                    raise self.interrupt
-    
-    def bind(self, family, type, proto=0):
-        """Create (or recreate) the actual socket object."""
-        self.socket = socket.socket(family, type, proto)
-        prevent_socket_inheritance(self.socket)
-        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        if self.nodelay and not isinstance(self.bind_addr, str):
-            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-        
-        if self.ssl_adapter is not None:
-            self.socket = self.ssl_adapter.bind(self.socket)
-        
-        # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
-        # activate dual-stack. See http://www.cherrypy.org/ticket/871.
-        if (family == socket.AF_INET6
-            and self.bind_addr[0] in ('::', '::0', '::0.0.0.0')):
-            try:
-                self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
-            except (AttributeError, socket.error):
-                # Apparently, the socket option is not available in
-                # this machine's TCP stack
-                pass
-        
-        self.socket.bind(self.bind_addr)
-    
-    def tick(self):
-        """Accept a new connection and put it on the Queue."""
-        try:
-            s, addr = self.socket.accept()
-            if not self.ready:
-                return
-            
-            prevent_socket_inheritance(s)
-            if hasattr(s, 'settimeout'):
-                s.settimeout(self.timeout)
-            
-            if self.response_header is None:
-                self.response_header = "%s Server" % self.version
-            
-            makefile = CP_fileobject
-            ssl_env = {}
-            # if ssl cert and key are set, we try to be a secure HTTP server
-            if self.ssl_adapter is not None:
-                try:
-                    s, ssl_env = self.ssl_adapter.wrap(s)
-                except NoSSLError:
-                    msg = ("The client sent a plain HTTP request, but "
-                           "this server only speaks HTTPS on this port.")
-                    buf = ["%s 400 Bad Request\r\n" % self.protocol,
-                           "Content-Length: %s\r\n" % len(msg),
-                           "Content-Type: text/plain\r\n\r\n",
-                           msg]
-                    
-                    wfile = CP_fileobject(s, "wb", -1)
-                    try:
-                        wfile.sendall("".join(buf))
-                    except socket.error, x:
-                        if x.args[0] not in socket_errors_to_ignore:
-                            raise
-                    return
-                if not s:
-                    return
-                makefile = self.ssl_adapter.makefile
-            
-            conn = self.ConnectionClass(self, s, makefile)
-            
-            if not isinstance(self.bind_addr, basestring):
-                # optional values
-                # Until we do DNS lookups, omit REMOTE_HOST
-                if addr is None: # sometimes this can happen
-                    # figure out if AF_INET or AF_INET6.
-                    if len(s.getsockname()) == 2:
-                        # AF_INET
-                        addr = ('0.0.0.0', 0)
-                    else:
-                        # AF_INET6
-                        addr = ('::', 0)
-                conn.remote_addr = addr[0]
-                conn.remote_port = addr[1]
-            
-            conn.ssl_env = ssl_env
-            
-            self.requests.put(conn)
-        except socket.timeout:
-            # The only reason for the timeout in start() is so we can
-            # notice keyboard interrupts on Win32, which don't interrupt
-            # accept() by default
-            return
-        except socket.error, x:
-            if x.args[0] in socket_error_eintr:
-                # I *think* this is right. EINTR should occur when a signal
-                # is received during the accept() call; all docs say retry
-                # the call, and I *think* I'm reading it right that Python
-                # will then go ahead and poll for and handle the signal
-                # elsewhere. See http://www.cherrypy.org/ticket/707.
-                return
-            if x.args[0] in socket_errors_nonblocking:
-                # Just try again. See http://www.cherrypy.org/ticket/479.
-                return
-            if x.args[0] in socket_errors_to_ignore:
-                # Our socket was closed.
-                # See http://www.cherrypy.org/ticket/686.
-                return
-            raise
-    
-    def _get_interrupt(self):
-        return self._interrupt
-    def _set_interrupt(self, interrupt):
-        self._interrupt = True
-        self.stop()
-        self._interrupt = interrupt
-    interrupt = property(_get_interrupt, _set_interrupt,
-                         doc="Set this to an Exception instance to "
-                             "interrupt the server.")
-    
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        self.ready = False
-        
-        sock = getattr(self, "socket", None)
-        if sock:
-            if not isinstance(self.bind_addr, basestring):
-                # Touch our own socket to make accept() return immediately.
-                try:
-                    host, port = sock.getsockname()[:2]
-                except socket.error, x:
-                    if x.args[0] not in socket_errors_to_ignore:
-                        # Changed to use error code and not message
-                        # See http://www.cherrypy.org/ticket/860.
-                        raise
-                else:
-                    # Note that we're explicitly NOT using AI_PASSIVE,
-                    # here, because we want an actual IP to touch.
-                    # localhost won't work if we've bound to a public IP,
-                    # but it will if we bound to '0.0.0.0' (INADDR_ANY).
-                    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                                  socket.SOCK_STREAM):
-                        af, socktype, proto, canonname, sa = res
-                        s = None
-                        try:
-                            s = socket.socket(af, socktype, proto)
-                            # See http://groups.google.com/group/cherrypy-users/
-                            #        browse_frm/thread/bbfe5eb39c904fe0
-                            s.settimeout(1.0)
-                            s.connect((host, port))
-                            s.close()
-                        except socket.error:
-                            if s:
-                                s.close()
-            if hasattr(sock, "close"):
-                sock.close()
-            self.socket = None
-        
-        self.requests.stop(self.shutdown_timeout)
-
-
-class Gateway(object):
-    
-    def __init__(self, req):
-        self.req = req
-    
-    def respond(self):
-        raise NotImplemented
-
-
-# These may either be wsgiserver.SSLAdapter subclasses or the string names
-# of such classes (in which case they will be lazily loaded).
-ssl_adapters = {
-    'builtin': 'cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
-    'pyopenssl': 'cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
-    }
-
-def get_ssl_adapter_class(name='pyopenssl'):
-    adapter = ssl_adapters[name.lower()]
-    if isinstance(adapter, basestring):
-        last_dot = adapter.rfind(".")
-        attr_name = adapter[last_dot + 1:]
-        mod_path = adapter[:last_dot]
-        
-        try:
-            mod = sys.modules[mod_path]
-            if mod is None:
-                raise KeyError()
-        except KeyError:
-            # The last [''] is important.
-            mod = __import__(mod_path, globals(), locals(), [''])
-        
-        # Let an AttributeError propagate outward.
-        try:
-            adapter = getattr(mod, attr_name)
-        except AttributeError:
-            raise AttributeError("'%s' object has no attribute '%s'"
-                                 % (mod_path, attr_name))
-    
-    return adapter
-
-# -------------------------------- WSGI Stuff -------------------------------- #
-
-
-class CherryPyWSGIServer(HTTPServer):
-    
-    wsgi_version = (1, 1)
-    
-    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
-                 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
-        self.requests = ThreadPool(self, min=numthreads or 1, max=max)
-        self.wsgi_app = wsgi_app
-        self.gateway = wsgi_gateways[self.wsgi_version]
-        
-        self.bind_addr = bind_addr
-        if not server_name:
-            server_name = socket.gethostname()
-        self.server_name = server_name
-        self.request_queue_size = request_queue_size
-        
-        self.timeout = timeout
-        self.shutdown_timeout = shutdown_timeout
-    
-    def _get_numthreads(self):
-        return self.requests.min
-    def _set_numthreads(self, value):
-        self.requests.min = value
-    numthreads = property(_get_numthreads, _set_numthreads)
-
-
-class WSGIGateway(Gateway):
-    
-    def __init__(self, req):
-        self.req = req
-        self.started_response = False
-        self.env = self.get_environ()
-    
-    def get_environ(self):
-        """Return a new environ dict targeting the given wsgi.version"""
-        raise NotImplemented
-    
-    def respond(self):
-        response = self.req.server.wsgi_app(self.env, self.start_response)
-        try:
-            for chunk in response:
-                # "The start_response callable must not actually transmit
-                # the response headers. Instead, it must store them for the
-                # server or gateway to transmit only after the first
-                # iteration of the application return value that yields
-                # a NON-EMPTY string, or upon the application's first
-                # invocation of the write() callable." (PEP 333)
-                if chunk:
-                    if isinstance(chunk, unicode):
-                        chunk = chunk.encode('ISO-8859-1')
-                    self.write(chunk)
-        finally:
-            if hasattr(response, "close"):
-                response.close()
-    
-    def start_response(self, status, headers, exc_info = None):
-        """WSGI callable to begin the HTTP response."""
-        # "The application may call start_response more than once,
-        # if and only if the exc_info argument is provided."
-        if self.started_response and not exc_info:
-            raise AssertionError("WSGI start_response called a second "
-                                 "time with no exc_info.")
-        self.started_response = True
-        
-        # "if exc_info is provided, and the HTTP headers have already been
-        # sent, start_response must raise an error, and should raise the
-        # exc_info tuple."
-        if self.req.sent_headers:
-            try:
-                raise exc_info[0], exc_info[1], exc_info[2]
-            finally:
-                exc_info = None
-        
-        self.req.status = status
-        for k, v in headers:
-            if not isinstance(k, str):
-                raise TypeError("WSGI response header key %r is not a byte string." % k)
-            if not isinstance(v, str):
-                raise TypeError("WSGI response header value %r is not a byte string." % v)
-        self.req.outheaders.extend(headers)
-        
-        return self.write
-    
-    def write(self, chunk):
-        """WSGI callable to write unbuffered data to the client.
-        
-        This method is also used internally by start_response (to write
-        data from the iterable returned by the WSGI application).
-        """
-        if not self.started_response:
-            raise AssertionError("WSGI write called before start_response.")
-        
-        if not self.req.sent_headers:
-            self.req.sent_headers = True
-            self.req.send_headers()
-        
-        self.req.write(chunk)
-
-
-class WSGIGateway_10(WSGIGateway):
-    
-    def get_environ(self):
-        """Return a new environ dict targeting the given wsgi.version"""
-        req = self.req
-        env = {
-            # set a non-standard environ entry so the WSGI app can know what
-            # the *real* server protocol is (and what features to support).
-            # See http://www.faqs.org/rfcs/rfc2145.html.
-            'ACTUAL_SERVER_PROTOCOL': req.server.protocol,
-            'PATH_INFO': req.path,
-            'QUERY_STRING': req.qs,
-            'REMOTE_ADDR': req.conn.remote_addr or '',
-            'REMOTE_PORT': str(req.conn.remote_port or ''),
-            'REQUEST_METHOD': req.method,
-            'REQUEST_URI': req.uri,
-            'SCRIPT_NAME': '',
-            'SERVER_NAME': req.server.server_name,
-            # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
-            'SERVER_PROTOCOL': req.request_protocol,
-            'wsgi.errors': sys.stderr,
-            'wsgi.input': req.rfile,
-            'wsgi.multiprocess': False,
-            'wsgi.multithread': True,
-            'wsgi.run_once': False,
-            'wsgi.url_scheme': req.scheme,
-            'wsgi.version': (1, 0),
-            }
-        
-        if isinstance(req.server.bind_addr, basestring):
-            # AF_UNIX. This isn't really allowed by WSGI, which doesn't
-            # address unix domain sockets. But it's better than nothing.
-            env["SERVER_PORT"] = ""
-        else:
-            env["SERVER_PORT"] = str(req.server.bind_addr[1])
-        
-        # CONTENT_TYPE/CONTENT_LENGTH
-        for k, v in req.inheaders.iteritems():
-            env["HTTP_" + k.upper().replace("-", "_")] = v
-        ct = env.pop("HTTP_CONTENT_TYPE", None)
-        if ct is not None:
-            env["CONTENT_TYPE"] = ct
-        cl = env.pop("HTTP_CONTENT_LENGTH", None)
-        if cl is not None:
-            env["CONTENT_LENGTH"] = cl
-        
-        if req.conn.ssl_env:
-            env.update(req.conn.ssl_env)
-        
-        return env
-
-
-class WSGIGateway_11(WSGIGateway_10):
-    
-    def get_environ(self):
-        env = WSGIGateway_10.get_environ(self)
-        env['wsgi.version'] = (1, 1)
-        return env
-
-
-class WSGIGateway_u0(WSGIGateway_10):
-    
-    def get_environ(self):
-        """Return a new environ dict targeting the given wsgi.version"""
-        req = self.req
-        env_10 = WSGIGateway_10.get_environ(self)
-        env = dict([(k.decode('ISO-8859-1'), v) for k, v in env_10.iteritems()])
-        env[u'wsgi.version'] = ('u', 0)
-        
-        # Request-URI
-        env.setdefault(u'wsgi.url_encoding', u'utf-8')
-        try:
-            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
-                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
-        except UnicodeDecodeError:
-            # Fall back to latin 1 so apps can transcode if needed.
-            env[u'wsgi.url_encoding'] = u'ISO-8859-1'
-            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
-                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
-        
-        for k, v in sorted(env.items()):
-            if isinstance(v, str) and k not in ('REQUEST_URI', 'wsgi.input'):
-                env[k] = v.decode('ISO-8859-1')
-        
-        return env
-
-wsgi_gateways = {
-    (1, 0): WSGIGateway_10,
-    (1, 1): WSGIGateway_11,
-    ('u', 0): WSGIGateway_u0,
-}
-
-class WSGIPathInfoDispatcher(object):
-    """A WSGI dispatcher for dispatch based on the PATH_INFO.
-    
-    apps: a dict or list of (path_prefix, app) pairs.
-    """
-    
-    def __init__(self, apps):
-        try:
-            apps = apps.items()
-        except AttributeError:
-            pass
-        
-        # Sort the apps by len(path), descending
-        apps.sort(cmp=lambda x,y: cmp(len(x[0]), len(y[0])))
-        apps.reverse()
-        
-        # The path_prefix strings must start, but not end, with a slash.
-        # Use "" instead of "/".
-        self.apps = [(p.rstrip("/"), a) for p, a in apps]
-    
-    def __call__(self, environ, start_response):
-        path = environ["PATH_INFO"] or "/"
-        for p, app in self.apps:
-            # The apps list should be sorted by length, descending.
-            if path.startswith(p + "/") or path == p:
-                environ = environ.copy()
-                environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
-                environ["PATH_INFO"] = path[len(p):]
-                return app(environ, start_response)
-        
-        start_response('404 Not Found', [('Content-Type', 'text/plain'),
-                                         ('Content-Length', '0')])
-        return ['']
--- a/bundled/cherrypy/cherrypy/wsgiserver/ssl_builtin.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-"""A library for integrating pyOpenSSL with CherryPy.
-
-The ssl module must be importable for SSL functionality.
-
-To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of
-BuiltinSSLAdapter.
-
-    ssl_adapter.certificate: the filename of the server SSL certificate.
-    ssl_adapter.private_key: the filename of the server's private key file.
-"""
-
-try:
-    import ssl
-except ImportError:
-    ssl = None
-
-from cherrypy import wsgiserver
-
-
-class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
-    """A wrapper for integrating Python's builtin ssl module with CherryPy."""
-    
-    def __init__(self, certificate, private_key, certificate_chain=None):
-        if ssl is None:
-            raise ImportError("You must install the ssl module to use HTTPS.")
-        self.certificate = certificate
-        self.private_key = private_key
-        self.certificate_chain = certificate_chain
-    
-    def bind(self, sock):
-        """Wrap and return the given socket."""
-        return sock
-    
-    def wrap(self, sock):
-        """Wrap and return the given socket, plus WSGI environ entries."""
-        try:
-            s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
-                    server_side=True, certfile=self.certificate,
-                    keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23)
-        except ssl.SSLError, e:
-            if e.errno == ssl.SSL_ERROR_EOF:
-                # This is almost certainly due to the cherrypy engine
-                # 'pinging' the socket to assert it's connectable;
-                # the 'ping' isn't SSL.
-                return None, {}
-            elif e.errno == ssl.SSL_ERROR_SSL:
-                if e.args[1].endswith('http request'):
-                    # The client is speaking HTTP to an HTTPS server.
-                    raise wsgiserver.NoSSLError
-            raise
-        return s, self.get_environ(s)
-    
-    # TODO: fill this out more with mod ssl env
-    def get_environ(self, sock):
-        """Create WSGI environ entries to be merged into each request."""
-        cipher = sock.cipher()
-        ssl_environ = {
-            "wsgi.url_scheme": "https",
-            "HTTPS": "on",
-            'SSL_PROTOCOL': cipher[1],
-            'SSL_CIPHER': cipher[0]
-##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
-##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
-            }
-        return ssl_environ
-    
-    def makefile(self, sock, mode='r', bufsize=-1):
-        return wsgiserver.CP_fileobject(sock, mode, bufsize)
-
--- a/bundled/cherrypy/cherrypy/wsgiserver/ssl_pyopenssl.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-"""A library for integrating pyOpenSSL with CherryPy.
-
-The OpenSSL module must be importable for SSL functionality.
-You can obtain it from http://pyopenssl.sourceforge.net/
-
-To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of
-SSLAdapter. There are two ways to use SSL:
-
-Method One:
-    ssl_adapter.context: an instance of SSL.Context.
-    
-    If this is not None, it is assumed to be an SSL.Context instance,
-    and will be passed to SSL.Connection on bind(). The developer is
-    responsible for forming a valid Context object. This approach is
-    to be preferred for more flexibility, e.g. if the cert and key are
-    streams instead of files, or need decryption, or SSL.SSLv3_METHOD
-    is desired instead of the default SSL.SSLv23_METHOD, etc. Consult
-    the pyOpenSSL documentation for complete options.
-
-Method Two (shortcut):
-    ssl_adapter.certificate: the filename of the server SSL certificate.
-    ssl_adapter.private_key: the filename of the server's private key file.
-    
-    Both are None by default. If ssl_adapter.context is None, but .private_key
-    and .certificate are both given and valid, they will be read, and the
-    context will be automatically created from them.
-    
-    ssl_adapter.certificate_chain: (optional) the filename of CA's intermediate
-        certificate bundle. This is needed for cheaper "chained root" SSL
-        certificates, and should be left as None if not required.
-"""
-
-import socket
-import threading
-import time
-
-from cherrypy import wsgiserver
-
-try:
-    from OpenSSL import SSL
-    from OpenSSL import crypto
-except ImportError:
-    SSL = None
-
-
-class SSL_fileobject(wsgiserver.CP_fileobject):
-    """SSL file object attached to a socket object."""
-    
-    ssl_timeout = 3
-    ssl_retry = .01
-    
-    def _safe_call(self, is_reader, call, *args, **kwargs):
-        """Wrap the given call with SSL error-trapping.
-        
-        is_reader: if False EOF errors will be raised. If True, EOF errors
-            will return "" (to emulate normal sockets).
-        """
-        start = time.time()
-        while True:
-            try:
-                return call(*args, **kwargs)
-            except SSL.WantReadError:
-                # Sleep and try again. This is dangerous, because it means
-                # the rest of the stack has no way of differentiating
-                # between a "new handshake" error and "client dropped".
-                # Note this isn't an endless loop: there's a timeout below.
-                time.sleep(self.ssl_retry)
-            except SSL.WantWriteError:
-                time.sleep(self.ssl_retry)
-            except SSL.SysCallError, e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ""
-                
-                errnum = e.args[0]
-                if is_reader and errnum in wsgiserver.socket_errors_to_ignore:
-                    return ""
-                raise socket.error(errnum)
-            except SSL.Error, e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ""
-                
-                thirdarg = None
-                try:
-                    thirdarg = e.args[0][0][2]
-                except IndexError:
-                    pass
-                
-                if thirdarg == 'http request':
-                    # The client is talking HTTP to an HTTPS server.
-                    raise wsgiserver.NoSSLError()
-                
-                raise wsgiserver.FatalSSLAlert(*e.args)
-            except:
-                raise
-            
-            if time.time() - start > self.ssl_timeout:
-                raise socket.timeout("timed out")
-    
-    def recv(self, *args, **kwargs):
-        buf = []
-        r = super(SSL_fileobject, self).recv
-        while True:
-            data = self._safe_call(True, r, *args, **kwargs)
-            buf.append(data)
-            p = self._sock.pending()
-            if not p:
-                return "".join(buf)
-    
-    def sendall(self, *args, **kwargs):
-        return self._safe_call(False, super(SSL_fileobject, self).sendall,
-                               *args, **kwargs)
-
-    def send(self, *args, **kwargs):
-        return self._safe_call(False, super(SSL_fileobject, self).send,
-                               *args, **kwargs)
-
-
-class SSLConnection:
-    """A thread-safe wrapper for an SSL.Connection.
-    
-    *args: the arguments to create the wrapped SSL.Connection(*args).
-    """
-    
-    def __init__(self, *args):
-        self._ssl_conn = SSL.Connection(*args)
-        self._lock = threading.RLock()
-    
-    for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
-              'renegotiate', 'bind', 'listen', 'connect', 'accept',
-              'setblocking', 'fileno', 'close', 'get_cipher_list',
-              'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
-              'makefile', 'get_app_data', 'set_app_data', 'state_string',
-              'sock_shutdown', 'get_peer_certificate', 'want_read',
-              'want_write', 'set_connect_state', 'set_accept_state',
-              'connect_ex', 'sendall', 'settimeout', 'gettimeout'):
-        exec("""def %s(self, *args):
-        self._lock.acquire()
-        try:
-            return self._ssl_conn.%s(*args)
-        finally:
-            self._lock.release()
-""" % (f, f))
-    
-    def shutdown(self, *args):
-        self._lock.acquire()
-        try:
-            # pyOpenSSL.socket.shutdown takes no args
-            return self._ssl_conn.shutdown()
-        finally:
-            self._lock.release()
-
-
-class pyOpenSSLAdapter(wsgiserver.SSLAdapter):
-    """A wrapper for integrating pyOpenSSL with CherryPy."""
-    
-    def __init__(self, certificate, private_key, certificate_chain=None):
-        if SSL is None:
-            raise ImportError("You must install pyOpenSSL to use HTTPS.")
-        
-        self.context = None
-        self.certificate = certificate
-        self.private_key = private_key
-        self.certificate_chain = certificate_chain
-        self._environ = None
-    
-    def bind(self, sock):
-        """Wrap and return the given socket."""
-        if self.context is None:
-            self.context = self.get_context()
-        conn = SSLConnection(self.context, sock)
-        self._environ = self.get_environ()
-        return conn
-    
-    def wrap(self, sock):
-        """Wrap and return the given socket, plus WSGI environ entries."""
-        return sock, self._environ.copy()
-    
-    def get_context(self):
-        """Return an SSL.Context from self attributes."""
-        # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
-        c = SSL.Context(SSL.SSLv23_METHOD)
-        c.use_privatekey_file(self.private_key)
-        if self.certificate_chain:
-            c.load_verify_locations(self.certificate_chain)
-        c.use_certificate_file(self.certificate)
-        return c
-    
-    def get_environ(self):
-        """Return WSGI environ entries to be merged into each request."""
-        ssl_environ = {
-            "HTTPS": "on",
-            # pyOpenSSL doesn't provide access to any of these AFAICT
-##            'SSL_PROTOCOL': 'SSLv2',
-##            SSL_CIPHER 	string 	The cipher specification name
-##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
-##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
-            }
-        
-        if self.certificate:
-            # Server certificate attributes
-            cert = open(self.certificate, 'rb').read()
-            cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
-            ssl_environ.update({
-                'SSL_SERVER_M_VERSION': cert.get_version(),
-                'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
-##                'SSL_SERVER_V_START': Validity of server's certificate (start time),
-##                'SSL_SERVER_V_END': Validity of server's certificate (end time),
-                })
-            
-            for prefix, dn in [("I", cert.get_issuer()),
-                               ("S", cert.get_subject())]:
-                # X509Name objects don't seem to have a way to get the
-                # complete DN string. Use str() and slice it instead,
-                # because str(dn) == "<X509Name object '/C=US/ST=...'>"
-                dnstr = str(dn)[18:-2]
-                
-                wsgikey = 'SSL_SERVER_%s_DN' % prefix
-                ssl_environ[wsgikey] = dnstr
-                
-                # The DN should be of the form: /k1=v1/k2=v2, but we must allow
-                # for any value to contain slashes itself (in a URL).
-                while dnstr:
-                    pos = dnstr.rfind("=")
-                    dnstr, value = dnstr[:pos], dnstr[pos + 1:]
-                    pos = dnstr.rfind("/")
-                    dnstr, key = dnstr[:pos], dnstr[pos + 1:]
-                    if key and value:
-                        wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
-                        ssl_environ[wsgikey] = value
-        
-        return ssl_environ
-    
-    def makefile(self, sock, mode='r', bufsize=-1):
-        if SSL and isinstance(sock, SSL.ConnectionType):
-            timeout = sock.gettimeout()
-            f = SSL_fileobject(sock, mode, bufsize)
-            f.ssl_timeout = timeout
-            return f
-        else:
-            return wsgiserver.CP_fileobject(sock, mode, bufsize)
-
--- a/bundled/cherrypy/docs/cherryd.1	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,263 +0,0 @@
-.\" Man page generated from reStructuredText.
-.TH cherryd 1 "2009-06-15" "3.2.0" "web"
-.SH NAME
-cherryd \- Starts the CherryPy HTTP server as a daemon
-
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level magin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-
-.SH SYNOPSIS
-\fBcherryd\fP [\-d] [\-f | \-s] [\-e ENV_NAME] [\-p PIDFILE_PATH] [\-P DIRPATH] [\-c CONFIG_FILE] \-i MODULE_NAME
-
-
-.SH DESCRIPTION
-\fBcherryd\fP is a Python script which starts the CherryPy webserver as a daemon.
-
-
-.SH OPTIONS
-.INDENT 0.0
-
-.TP
-.BI \-c\  CONFIG_FILE ,\ \-\-config\fn= CONFIG_FILE
-Specifies a config file which is to be read and merged into the
-CherryPy site\-wide config dict.  This option may be specified
-multiple times.  For each CONFIG_FILE specified, \fBcherryd\fP will perform
-\fBcherrypy.config.update()\fP.
-
-
-.TP
-.B \-d
-Run the server as a daemon.
-
-
-.TP
-.BI \-e\  ENV_NAME ,\ \-\-environment\fn= ENV_NAME
-Specifies the name of an environment to be applied.  An environment is a
-canned set of configuration entries.  See \fI\%ENVIRONMENTS\fP below for a
-list of the built\-in environments.
-
-
-.TP
-.B \-f
-Start a fastcgi server instead of the default HTTP server.
-
-
-.TP
-.B \-s
-Start a scgi server instead of the default HTTP server.
-
-
-.TP
-.BI \-i\  MODULE_NAME ,\ \-\-import\fn= MODULE_NAME
-Specifies a module to import.  This option may be specified multiple times.
-For each MODULE_NAME specified, \fBcherryd\fP will import the module.  This
-is how you tell \fBcherryd\fP to run your application\'s startup code.
-For all practical purposes, \fB\-i\fP is not optional; you will always need to
-specify at least one module.
-
-
-.TP
-.BI \-p\  PIDFILE_PATH ,\ \-\-pidfile\fn= PIDFILE_PATH
-Store the process id in PIDFILE_PATH.
-
-
-.TP
-.BI \-P\  DIRPATH ,\ \-\-Path\  DIRPATH
-Specifies a directory to be inserted at the head of \fBsys.path\fP.  DIRPATH
-should be an absolute path.  This option may be specified multiple times.
-\fBcherryd\fP inserts all the specified DIRPATHs into \fBsys.path\fP before it
-attempts to import modules specified with \fB\-i\fP.
-
-.UNINDENT
-For a terse summary of the options, run \fBcherryd \-\-help\fP.
-
-
-.SH EXAMPLES
-A site\-wide configuration file \fBsite.conf\fP:
-
-.INDENT 0.0
-.INDENT 3.5
-
-[global]
-.br
-server.socket_host = "0.0.0.0"
-.br
-server.socket_port = 8008
-.br
-engine.autoreload_on = False
-.br
-
-.UNINDENT
-.UNINDENT
-The application startup code in \fBstartup.py\fP:
-
-.INDENT 0.0
-.INDENT 3.5
-
-import cherrypy
-.br
-import my_controller
-.br
-cherrypy.log.error_file = \'/var/tmp/myapp\-error.log\'
-.br
-cherrypy.log.access_file = \'/var/tmp/myapp\-access.log\'
-.br
-config_root = { \'tools.encode.encoding\' : \'utf\-8\', }
-.br
-app_conf = { \'/\' : config_root }
-.br
-cherrypy.tree.mount(my_controller.Root(), script_name=\'\', config=app_conf)
-.br
-
-.UNINDENT
-.UNINDENT
-A corresponding \fBcherryd\fP command line:
-
-.INDENT 0.0
-.INDENT 3.5
-
-cherryd \-d \-c site.conf \-i startup \-p /var/log/cherrypy/my_app.pid
-.br
-
-.UNINDENT
-.UNINDENT
-
-.SH DROPPING PRIVILEGES
-If you want to serve your web application on TCP port 80 (or any port lower than
-1024), the CherryPy HTTP server needs to start as root in order to bind to the
-port.  Running a web application as root is reckless, so the application should
-drop privileges from root to some other user and group.  The application must do
-this itself, as \fBcherryd\fP does not do it for you.
-
-To drop privileges, put the following lines into your startup code,
-substituting appropriate values for \fBumask\fP, \fBuid\fP and \fBgid\fP:
-
-.INDENT 0.0
-.INDENT 3.5
-
-from cherrypy.process.plugins import DropPrivileges
-.br
-DropPrivileges(cherrypy.engine, umask=022, uid=\'nobody\', gid=\'nogroup\').subscribe()
-.br
-
-.UNINDENT
-.UNINDENT
-Note that the values for \fBuid\fP and \fBgid\fP may be either user and group
-names, or uid and gid integers.
-
-Note that you must disable the engine Autoreload plugin, because the way
-Autoreload works is by \fBexec()\fPing a new instance of the running process,
-replacing the current instance.  Since root privileges are already dropped, the
-new process instance will fail when it tries to perform a privileged operation
-such as binding to a low\-numbered TCP port.
-
-
-.SH ENVIRONMENTS
-These are the built\-in environment configurations:
-
-
-.SS staging
-.INDENT 0.0
-.INDENT 3.5
-
-\'engine.autoreload_on\': False,
-.br
-\'checker.on\': False,
-.br
-\'tools.log_headers.on\': False,
-.br
-\'request.show_tracebacks\': False,
-.br
-\'request.show_mismatched_params\': False,
-.br
-
-.UNINDENT
-.UNINDENT
-
-.SS production
-.INDENT 0.0
-.INDENT 3.5
-
-\'engine.autoreload_on\': False,
-.br
-\'checker.on\': False,
-.br
-\'tools.log_headers.on\': False,
-.br
-\'request.show_tracebacks\': False,
-.br
-\'request.show_mismatched_params\': False,
-.br
-\'log.screen\': False,
-.br
-
-.UNINDENT
-.UNINDENT
-
-.SS embedded
-.INDENT 0.0
-.INDENT 3.5
-
-# For use with CherryPy embedded in another deployment stack, e.g. Apache mod_wsgi.
-.br
-\'engine.autoreload_on\': False,
-.br
-\'checker.on\': False,
-.br
-\'tools.log_headers.on\': False,
-.br
-\'request.show_tracebacks\': False,
-.br
-\'request.show_mismatched_params\': False,
-.br
-\'log.screen\': False,
-.br
-\'engine.SIGHUP\': None,
-.br
-\'engine.SIGTERM\': None,
-.br
-
-.UNINDENT
-.UNINDENT
-
-.SH BUGS
-\fBcherryd\fP should probably accept command\-line options \fB\-\-uid\fP, \fB\-\-gid\fP, and
-\fB\-\-umask\fP, and handle dropping privileges itself.
-
-
-.SH AUTHOR
-fumanchu
-
-.nf
-cherrypy.org
-.fi
-
-.SH COPYRIGHT
-This man page is placed in the public domain
-
-.\" Generated by docutils manpage writer on 2009-06-15 15:07.
-.\" 
--- a/bundled/cherrypy/ez_setup.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-#!python
-"""Bootstrap setuptools installation
-
-If you want to use setuptools in your package's setup.py, just include this
-file in the same directory with it, and add this to the top of your setup.py::
-
-    from ez_setup import use_setuptools
-    use_setuptools()
-
-If you want to require a specific version of setuptools, set a download
-mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
-
-This file can also be run as a script to install or upgrade setuptools.
-"""
-
-DEFAULT_VERSION = "0.5a13"
-DEFAULT_URL     = "http://www.python.org/packages/source/s/setuptools/"
-
-import sys, os
-
-def use_setuptools(
-    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir
-):
-    """Automatically find/download setuptools and make it available on sys.path
-
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end with
-    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
-    it is not already available.
-
-    If an older version of setuptools is installed, this will print a message
-    to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling
-    script.
-    """
-    try:
-        import setuptools
-        if setuptools.__version__ == '0.0.1':
-            print >>sys.stderr, (
-            "You have an obsolete version of setuptools installed.  Please\n"
-            "remove it from your system entirely before rerunning this script."
-            )
-            sys.exit(2)
-
-    except ImportError:
-        egg = download_setuptools(version, download_base, to_dir)
-        sys.path.insert(0, egg)
-        import setuptools; setuptools.bootstrap_install_from = egg
-
-    import pkg_resources
-    try:
-        pkg_resources.require("setuptools>="+version)
-
-    except pkg_resources.VersionConflict:
-        # XXX could we install in a subprocess here?
-        print >>sys.stderr, (
-            "The required version of setuptools (>=%s) is not available, and\n"
-            "can't be installed while this script is running. Please install\n"
-            " a more recent version first."
-        ) % version
-        sys.exit(2)
-
-def download_setuptools(
-    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir
-):
-    """Download setuptools from a specified location and return its filename
-
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end
-    with a '/'). `to_dir` is the directory where the egg will be downloaded.
-    """
-    import urllib2, shutil
-    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
-    url = download_base + egg_name + '.zip'  # XXX
-    saveto = os.path.join(to_dir, egg_name)
-    src = dst = None
-
-    if not os.path.exists(saveto):  # Avoid repeated downloads
-        try:
-            from distutils import log
-            log.warn("Downloading %s", url)
-            src = urllib2.urlopen(url)
-            # Read/write all in one block, so we don't create a corrupt file
-            # if the download is interrupted.
-            data = src.read()
-            dst = open(saveto,"wb")
-            dst.write(data)
-        finally:
-            if src: src.close()
-            if dst: dst.close()
-
-    return os.path.realpath(saveto)
-
-def main(argv, version=DEFAULT_VERSION):
-    """Install or upgrade setuptools and EasyInstall"""
-
-    try:
-        import setuptools
-    except ImportError:
-        import tempfile, shutil
-        tmpdir = tempfile.mkdtemp(prefix="easy_install-")
-        try:
-            egg = download_setuptools(version, to_dir=tmpdir)
-            sys.path.insert(0,egg)
-            from setuptools.command.easy_install import main
-            main(list(argv)+[egg])
-        finally:
-            shutil.rmtree(tmpdir)
-    else:
-        if setuptools.__version__ == '0.0.1':
-            # tell the user to uninstall obsolete version
-            use_setuptools(version)
-
-    req = "setuptools>="+version
-    import pkg_resources
-    try:
-        pkg_resources.require(req)
-    except pkg_resources.VersionConflict:
-        try:
-            from setuptools.command.easy_install import main
-        except ImportError:
-            from easy_install import main
-        main(list(argv)+[download_setuptools()])
-        sys.exit(0) # try to force an exit
-    else:
-        if argv:
-            from setuptools.command.easy_install import main
-            main(argv)
-        else:
-            print "Setuptools version",version,"or greater has been installed."
-            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
-if __name__=='__main__':
-    main(sys.argv[1:])
--- a/bundled/cherrypy/make-sdist	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-rm MANIFEST
-python setup.py sdist --formats=gztar
--- a/bundled/cherrypy/setup.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-"""Installs CherryPy using distutils
-
-Run:
-    python setup.py install
-
-to install this package.
-"""
-
-try:
-    from setuptools import setup
-except ImportError:
-    from distutils.core import setup
-
-from distutils.command.install import INSTALL_SCHEMES
-import sys
-import os
-import shutil
-
-required_python_version = '2.3'
-
-###############################################################################
-# arguments for the setup command
-###############################################################################
-name = "CherryPy"
-version = "3.2.0rc1"
-desc = "Object-Oriented HTTP framework"
-long_desc = "CherryPy is a pythonic, object-oriented HTTP framework"
-classifiers=[
-    #"Development Status :: 5 - Production/Stable",
-    "Development Status :: 4 - Beta",
-    "Environment :: Web Environment",
-    "Intended Audience :: Developers",
-    "License :: Freely Distributable",
-    "Operating System :: OS Independent",
-    "Programming Language :: Python",
-    "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
-    "Topic :: Software Development :: Libraries :: Application Frameworks",
-]
-author="CherryPy Team"
-author_email="team@cherrypy.org"
-url="http://www.cherrypy.org"
-cp_license="BSD"
-packages=[
-    "cherrypy", "cherrypy.lib",
-    "cherrypy.tutorial", "cherrypy.test",
-    "cherrypy.wsgiserver", "cherrypy.process",
-    "cherrypy.scaffold",
-]
-download_url="http://download.cherrypy.org/cherrypy/3.2.0/"
-data_files=[
-    ('cherrypy', ['cherrypy/cherryd',
-                  'cherrypy/favicon.ico',
-                  'cherrypy/LICENSE.txt',
-                  ]),
-    ('cherrypy/process', []),
-    ('cherrypy/scaffold', ['cherrypy/scaffold/example.conf',
-                           'cherrypy/scaffold/site.conf',
-                           ]),
-    ('cherrypy/scaffold/static', ['cherrypy/scaffold/static/made_with_cherrypy_small.png',
-                                  ]),
-    ('cherrypy/test', ['cherrypy/test/style.css',
-                       'cherrypy/test/test.pem',
-                       ]),
-    ('cherrypy/test/static', ['cherrypy/test/static/index.html',
-                              'cherrypy/test/static/dirback.jpg',]),
-    ('cherrypy/tutorial',
-        [
-            'cherrypy/tutorial/tutorial.conf',
-            'cherrypy/tutorial/README.txt',
-            'cherrypy/tutorial/pdf_file.pdf',
-            'cherrypy/tutorial/custom_error.html',
-        ]
-    ),
-]
-###############################################################################
-# end arguments for setup
-###############################################################################
-
-def fix_data_files(data_files):
-    """
-    bdist_wininst seems to have a bug about where it installs data files.
-    I found a fix the django team used to work around the problem at
-    http://code.djangoproject.com/changeset/8313 .  This function
-    re-implements that solution.
-    Also see http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html
-    for more info.
-    """
-    def fix_dest_path(path):
-        return '\\PURELIB\\%(path)s' % vars()
-    
-    if not 'bdist_wininst' in sys.argv: return
-    
-    data_files[:] = [
-        (fix_dest_path(path), files)
-        for path, files in data_files]
-fix_data_files(data_files)
-
-def main():
-    if sys.version < required_python_version:
-        s = "I'm sorry, but %s %s requires Python %s or later."
-        print s % (name, version, required_python_version)
-        sys.exit(1)
-    # set default location for "data_files" to
-    # platform specific "site-packages" location
-    for scheme in INSTALL_SCHEMES.values():
-        scheme['data'] = scheme['purelib']
-    
-    dist = setup(
-        name=name,
-        version=version,
-        description=desc,
-        long_description=long_desc,
-        classifiers=classifiers,
-        author=author,
-        author_email=author_email,
-        url=url,
-        license=cp_license,
-        packages=packages,
-        download_url=download_url,
-        data_files=data_files,
-        scripts=[os.path.join("cherrypy", "cherryd")],
-    )
-
-
-if __name__ == "__main__":
-    main()
Binary file bundled/cherrypy/visuals/cherrypy_logo_big.png has changed
Binary file bundled/cherrypy/visuals/cherrypy_logo_small.jpg has changed
Binary file bundled/cherrypy/visuals/favicon.ico has changed
Binary file bundled/cherrypy/visuals/made_with_cherrypy_big.png has changed
Binary file bundled/cherrypy/visuals/made_with_cherrypy_small.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/AUTHORS	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,24 @@
+Flask is written and maintained by Armin Ronacher and
+various contributors:
+
+Development Lead
+````````````````
+
+- Armin Ronacher <armin.ronacher@active-4.com>
+
+Patches and Suggestions
+```````````````````````
+
+- Chris Edgemon
+- Chris Grindstaff
+- Florent Xicluna
+- Georg Brandl
+- Justin Quick
+- Kenneth Reitz
+- Marian Sigler
+- Matthew Frazier
+- Ron DuPlain
+- Sebastien Estienne
+- Simon Sapin
+- Stephane Wirtel
+- Zhao Xiaohong
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/CHANGES	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,73 @@
+Flask Changelog
+===============
+
+Here you can see the full list of changes between each Flask release.
+
+Version 0.4
+-----------
+
+Release date to be announced, codename to be selected.
+
+- added the ability to register application wide error handlers
+  from modules.
+- :meth:`~flask.Flask.after_request` handlers are now also invoked
+  if the request dies with an exception and an error handling page
+  kicks in.
+- test client has not the ability to preserve the request context
+  for a little longer.  This can also be used to trigger custom
+  requests that do not pop the request stack for testing.
+- because the Python standard library caches loggers, the name of
+  the logger is configurable now to better support unittests.
+- added `TESTING` switch that can activate unittesting helpers.
+
+Version 0.3.1
+-------------
+
+Bugfix release, released May 28th
+
+- fixed a error reporting bug with :meth:`flask.Config.from_envvar`
+- removed some unused code from flask
+- release does no longer include development leftover files (.git
+  folder for themes, built documentation in zip and pdf file and
+  some .pyc files)
+
+Version 0.3
+-----------
+
+Released on May 28th, codename Schnaps
+
+- added support for categories for flashed messages.
+- the application now configures a :class:`logging.Handler` and will
+  log request handling exceptions to that logger when not in debug
+  mode.  This makes it possible to receive mails on server errors
+  for example.
+- added support for context binding that does not require the use of
+  the with statement for playing in the console.
+- the request context is now available within the with statement making
+  it possible to further push the request context or pop it.
+- added support for configurations.
+
+Version 0.2
+-----------
+
+Released on May 12th, codename Jägermeister
+
+- various bugfixes
+- integrated JSON support
+- added :func:`~flask.get_template_attribute` helper function.
+- :meth:`~flask.Flask.add_url_rule` can now also register a
+  view function.
+- refactored internal request dispatching.
+- server listens on 127.0.0.1 by default now to fix issues with chrome.
+- added external URL support.
+- added support for :func:`~flask.send_file`
+- module support and internal request handling refactoring
+  to better support pluggable applications.
+- sessions can be set to be permanent now on a per-session basis.
+- better error reporting on missing secret keys.
+- added support for Google Appengine.
+
+Version 0.1
+-----------
+
+First public preview release.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/LICENSE	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,32 @@
+Copyright (c) 2010 by Armin Ronacher and contributors.  See AUTHORS
+for more details.
+
+Some rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following
+  disclaimer in the documentation and/or other materials provided
+  with the distribution.
+
+* The names of the contributors may not be used to endorse or
+  promote products derived from this software without specific
+  prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/MANIFEST.in	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,10 @@
+include Makefile CHANGES LICENSE AUTHORS
+recursive-include tests *
+recursive-include examples *
+recursive-include docs *
+recursive-exclude docs *.pyc
+recursive-exclude docs *.pyo
+recursive-exclude examples *.pyc
+recursive-exclude examples *.pyo
+prune docs/_build
+prune docs/_themes/.git
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/Makefile	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,22 @@
+.PHONY: clean-pyc test upload-docs
+
+all: clean-pyc test
+
+test:
+	python setup.py test
+
+release:
+	python setup.py release sdist upload
+
+clean-pyc:
+	find . -name '*.pyc' -exec rm -f {} +
+	find . -name '*.pyo' -exec rm -f {} +
+	find . -name '*~' -exec rm -f {} +
+
+upload-docs:
+	$(MAKE) -C docs html dirhtml latex
+	$(MAKE) -C docs/_build/latex all-pdf
+	cd docs/_build/; mv html flask-docs; zip -r flask-docs.zip flask-docs; mv flask-docs html
+	scp -r docs/_build/dirhtml/* pocoo.org:/var/www/flask.pocoo.org/docs/
+	scp -r docs/_build/latex/Flask.pdf pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.pdf
+	scp -r docs/_build/flask-docs.zip pocoo.org:/var/www/flask.pocoo.org/docs/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/README	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,36 @@
+
+                          // Flask //
+
+              web development, one drop at a time
+
+
+    ~ What is Flask?
+
+      Flask is a microframework for Python based on Werkzeug
+      and Jinja2.  It's intended for small scale applications
+      and was developped with best intentions in mind.
+
+    ~ Is it ready?
+
+      A preview release is out now, and I'm hoping for some
+      input about what you want from a microframework and
+      how it should look like.  Consider the API to slightly
+      improve over time.
+
+    ~ What do I need?
+
+      Jinja 2.4 and Werkzeug 0.6.1.  `easy_install` will
+      install them for you if you do `easy_install Flask==dev`.
+      I encourage you to use a virtualenv.  Check the docs for
+      complete installation and usage instructions.
+
+    ~ Where are the docs?
+
+      Go to http://flask.pocoo.org/ for a prebuilt version of
+      the current documentation.  Otherwise build them yourself
+      from the sphinx sources in the docs folder.
+
+    ~ Where can I get help?
+
+      Either use the #pocoo IRC channel on irc.freenode.net or
+      ask on the mailinglist: http://flask.pocoo.org/mailinglist/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,1512 @@
+# -*- coding: utf-8 -*-
+"""
+    flask
+    ~~~~~
+
+    A microframework based on Werkzeug.  It's extensively documented
+    and follows best practice patterns.
+
+    :copyright: (c) 2010 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from __future__ import with_statement
+import os
+import sys
+import mimetypes
+from datetime import datetime, timedelta
+
+from itertools import chain
+from threading import Lock
+from jinja2 import Environment, PackageLoader, FileSystemLoader
+from werkzeug import Request as RequestBase, Response as ResponseBase, \
+     LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
+     ImmutableDict, cached_property, wrap_file, Headers, \
+     import_string
+from werkzeug.routing import Map, Rule
+from werkzeug.exceptions import HTTPException, InternalServerError
+from werkzeug.contrib.securecookie import SecureCookie
+
+# try to load the best simplejson implementation available.  If JSON
+# is not installed, we add a failing class.
+json_available = True
+try:
+    import simplejson as json
+except ImportError:
+    try:
+        import json
+    except ImportError:
+        json_available = False
+
+# utilities we import from Werkzeug and Jinja2 that are unused
+# in the module but are exported as public interface.
+from werkzeug import abort, redirect
+from jinja2 import Markup, escape
+
+# use pkg_resource if that works, otherwise fall back to cwd.  The
+# current working directory is generally not reliable with the notable
+# exception of google appengine.
+try:
+    import pkg_resources
+    pkg_resources.resource_stream
+except (ImportError, AttributeError):
+    pkg_resources = None
+
+# a lock used for logger initialization
+_logger_lock = Lock()
+
+
+class Request(RequestBase):
+    """The request object used by default in flask.  Remembers the
+    matched endpoint and view arguments.
+
+    It is what ends up as :class:`~flask.request`.  If you want to replace
+    the request object used you can subclass this and set
+    :attr:`~flask.Flask.request_class` to your subclass.
+    """
+
+    endpoint = view_args = routing_exception = None
+
+    @property
+    def module(self):
+        """The name of the current module"""
+        if self.endpoint and '.' in self.endpoint:
+            return self.endpoint.rsplit('.', 1)[0]
+
+    @cached_property
+    def json(self):
+        """If the mimetype is `application/json` this will contain the
+        parsed JSON data.
+        """
+        if __debug__:
+            _assert_have_json()
+        if self.mimetype == 'application/json':
+            return json.loads(self.data)
+
+
+class Response(ResponseBase):
+    """The response object that is used by default in flask.  Works like the
+    response object from Werkzeug but is set to have a HTML mimetype by
+    default.  Quite often you don't have to create this object yourself because
+    :meth:`~flask.Flask.make_response` will take care of that for you.
+
+    If you want to replace the response object used you can subclass this and
+    set :attr:`~flask.Flask.response_class` to your subclass.
+    """
+    default_mimetype = 'text/html'
+
+
+class _RequestGlobals(object):
+    pass
+
+
+class Session(SecureCookie):
+    """Expands the session with support for switching between permanent
+    and non-permanent sessions.
+    """
+
+    def _get_permanent(self):
+        return self.get('_permanent', False)
+
+    def _set_permanent(self, value):
+        self['_permanent'] = bool(value)
+
+    permanent = property(_get_permanent, _set_permanent)
+    del _get_permanent, _set_permanent
+
+
+class _NullSession(Session):
+    """Class used to generate nicer error messages if sessions are not
+    available.  Will still allow read-only access to the empty session
+    but fail on setting.
+    """
+
+    def _fail(self, *args, **kwargs):
+        raise RuntimeError('the session is unavailable because no secret '
+                           'key was set.  Set the secret_key on the '
+                           'application to something unique and secret')
+    __setitem__ = __delitem__ = clear = pop = popitem = \
+        update = setdefault = _fail
+    del _fail
+
+
+class _RequestContext(object):
+    """The request context contains all request relevant information.  It is
+    created at the beginning of the request and pushed to the
+    `_request_ctx_stack` and removed at the end of it.  It will create the
+    URL adapter and request object for the WSGI environment provided.
+    """
+
+    def __init__(self, app, environ):
+        self.app = app
+        self.url_adapter = app.url_map.bind_to_environ(environ)
+        self.request = app.request_class(environ)
+        self.session = app.open_session(self.request)
+        if self.session is None:
+            self.session = _NullSession()
+        self.g = _RequestGlobals()
+        self.flashes = None
+
+        try:
+            self.request.endpoint, self.request.view_args = \
+                self.url_adapter.match()
+        except HTTPException, e:
+            self.request.routing_exception = e
+
+    def push(self):
+        """Binds the request context."""
+        _request_ctx_stack.push(self)
+
+    def pop(self):
+        """Pops the request context."""
+        _request_ctx_stack.pop()
+
+    def __enter__(self):
+        self.push()
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        # do not pop the request stack if we are in debug mode and an
+        # exception happened.  This will allow the debugger to still
+        # access the request object in the interactive shell.  Furthermore
+        # the context can be force kept alive for the test client.
+        if not self.request.environ.get('flask._preserve_context') and \
+           (tb is None or not self.app.debug):
+            self.pop()
+
+
+def url_for(endpoint, **values):
+    """Generates a URL to the given endpoint with the method provided.
+    The endpoint is relative to the active module if modules are in use.
+
+    Here some examples:
+
+    ==================== ======================= =============================
+    Active Module        Target Endpoint         Target Function
+    ==================== ======================= =============================
+    `None`               ``'index'``             `index` of the application
+    `None`               ``'.index'``            `index` of the application
+    ``'admin'``          ``'index'``             `index` of the `admin` module
+    any                  ``'.index'``            `index` of the application
+    any                  ``'admin.index'``       `index` of the `admin` module
+    ==================== ======================= =============================
+
+    Variable arguments that are unknown to the target endpoint are appended
+    to the generated URL as query arguments.
+
+    For more information, head over to the :ref:`Quickstart <url-building>`.
+
+    :param endpoint: the endpoint of the URL (name of the function)
+    :param values: the variable arguments of the URL rule
+    :param _external: if set to `True`, an absolute URL is generated.
+    """
+    ctx = _request_ctx_stack.top
+    if '.' not in endpoint:
+        mod = ctx.request.module
+        if mod is not None:
+            endpoint = mod + '.' + endpoint
+    elif endpoint.startswith('.'):
+        endpoint = endpoint[1:]
+    external = values.pop('_external', False)
+    return ctx.url_adapter.build(endpoint, values, force_external=external)
+
+
+def get_template_attribute(template_name, attribute):
+    """Loads a macro (or variable) a template exports.  This can be used to
+    invoke a macro from within Python code.  If you for example have a
+    template named `_cider.html` with the following contents:
+
+    .. sourcecode:: html+jinja
+
+       {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
+
+    You can access this from Python code like this::
+
+        hello = get_template_attribute('_cider.html', 'hello')
+        return hello('World')
+
+    .. versionadded:: 0.2
+
+    :param template_name: the name of the template
+    :param attribute: the name of the variable of macro to acccess
+    """
+    return getattr(current_app.jinja_env.get_template(template_name).module,
+                   attribute)
+
+
+def flash(message, category='message'):
+    """Flashes a message to the next request.  In order to remove the
+    flashed message from the session and to display it to the user,
+    the template has to call :func:`get_flashed_messages`.
+
+    .. versionchanged: 0.3
+       `category` parameter added.
+
+    :param message: the message to be flashed.
+    :param category: the category for the message.  The following values
+                     are recommended: ``'message'`` for any kind of message,
+                     ``'error'`` for errors, ``'info'`` for information
+                     messages and ``'warning'`` for warnings.  However any
+                     kind of string can be used as category.
+    """
+    session.setdefault('_flashes', []).append((category, message))
+
+
+def get_flashed_messages(with_categories=False):
+    """Pulls all flashed messages from the session and returns them.
+    Further calls in the same request to the function will return
+    the same messages.  By default just the messages are returned,
+    but when `with_categories` is set to `True`, the return value will
+    be a list of tuples in the form ``(category, message)`` instead.
+
+    Example usage:
+
+    .. sourcecode:: html+jinja
+
+        {% for category, msg in get_flashed_messages(with_categories=true) %}
+          <p class=flash-{{ category }}>{{ msg }}
+        {% endfor %}
+
+    .. versionchanged:: 0.3
+       `with_categories` parameter added.
+
+    :param with_categories: set to `True` to also receive categories.
+    """
+    flashes = _request_ctx_stack.top.flashes
+    if flashes is None:
+        _request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
+    if not with_categories:
+        return [x[1] for x in flashes]
+    return flashes
+
+
+def jsonify(*args, **kwargs):
+    """Creates a :class:`~flask.Response` with the JSON representation of
+    the given arguments with an `application/json` mimetype.  The arguments
+    to this function are the same as to the :class:`dict` constructor.
+
+    Example usage::
+
+        @app.route('/_get_current_user')
+        def get_current_user():
+            return jsonify(username=g.user.username,
+                           email=g.user.email,
+                           id=g.user.id)
+
+    This will send a JSON response like this to the browser::
+
+        {
+            "username": "admin",
+            "email": "admin@localhost",
+            "id": 42
+        }
+
+    This requires Python 2.6 or an installed version of simplejson.  For
+    security reasons only objects are supported toplevel.  For more
+    information about this, have a look at :ref:`json-security`.
+
+    .. versionadded:: 0.2
+    """
+    if __debug__:
+        _assert_have_json()
+    return current_app.response_class(json.dumps(dict(*args, **kwargs),
+        indent=None if request.is_xhr else 2), mimetype='application/json')
+
+
+def send_file(filename_or_fp, mimetype=None, as_attachment=False,
+              attachment_filename=None):
+    """Sends the contents of a file to the client.  This will use the
+    most efficient method available and configured.  By default it will
+    try to use the WSGI server's file_wrapper support.  Alternatively
+    you can set the application's :attr:`~Flask.use_x_sendfile` attribute
+    to ``True`` to directly emit an `X-Sendfile` header.  This however
+    requires support of the underlying webserver for `X-Sendfile`.
+
+    By default it will try to guess the mimetype for you, but you can
+    also explicitly provide one.  For extra security you probably want
+    to sent certain files as attachment (HTML for instance).
+
+    Please never pass filenames to this function from user sources without
+    checking them first.  Something like this is usually sufficient to
+    avoid security problems::
+
+        if '..' in filename or filename.startswith('/'):
+            abort(404)
+
+    .. versionadded:: 0.2
+
+    :param filename_or_fp: the filename of the file to send.  This is
+                           relative to the :attr:`~Flask.root_path` if a
+                           relative path is specified.
+                           Alternatively a file object might be provided
+                           in which case `X-Sendfile` might not work and
+                           fall back to the traditional method.
+    :param mimetype: the mimetype of the file if provided, otherwise
+                     auto detection happens.
+    :param as_attachment: set to `True` if you want to send this file with
+                          a ``Content-Disposition: attachment`` header.
+    :param attachment_filename: the filename for the attachment if it
+                                differs from the file's filename.
+    """
+    if isinstance(filename_or_fp, basestring):
+        filename = filename_or_fp
+        file = None
+    else:
+        file = filename_or_fp
+        filename = getattr(file, 'name', None)
+    if filename is not None:
+        filename = os.path.join(current_app.root_path, filename)
+    if mimetype is None and (filename or attachment_filename):
+        mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
+    if mimetype is None:
+        mimetype = 'application/octet-stream'
+
+    headers = Headers()
+    if as_attachment:
+        if attachment_filename is None:
+            if filename is None:
+                raise TypeError('filename unavailable, required for '
+                                'sending as attachment')
+            attachment_filename = os.path.basename(filename)
+        headers.add('Content-Disposition', 'attachment',
+                    filename=attachment_filename)
+
+    if current_app.use_x_sendfile and filename:
+        if file is not None:
+            file.close()
+        headers['X-Sendfile'] = filename
+        data = None
+    else:
+        if file is None:
+            file = open(filename, 'rb')
+        data = wrap_file(request.environ, file)
+
+    return Response(data, mimetype=mimetype, headers=headers,
+                    direct_passthrough=True)
+
+
+def render_template(template_name, **context):
+    """Renders a template from the template folder with the given
+    context.
+
+    :param template_name: the name of the template to be rendered
+    :param context: the variables that should be available in the
+                    context of the template.
+    """
+    current_app.update_template_context(context)
+    return current_app.jinja_env.get_template(template_name).render(context)
+
+
+def render_template_string(source, **context):
+    """Renders a template from the given template source string
+    with the given context.
+
+    :param template_name: the sourcecode of the template to be
+                          rendered
+    :param context: the variables that should be available in the
+                    context of the template.
+    """
+    current_app.update_template_context(context)
+    return current_app.jinja_env.from_string(source).render(context)
+
+
+def _default_template_ctx_processor():
+    """Default template context processor.  Injects `request`,
+    `session` and `g`.
+    """
+    reqctx = _request_ctx_stack.top
+    return dict(
+        request=reqctx.request,
+        session=reqctx.session,
+        g=reqctx.g
+    )
+
+
+def _assert_have_json():
+    """Helper function that fails if JSON is unavailable."""
+    if not json_available:
+        raise RuntimeError('simplejson not installed')
+
+
+def _get_package_path(name):
+    """Returns the path to a package or cwd if that cannot be found."""
+    try:
+        return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
+    except (KeyError, AttributeError):
+        return os.getcwd()
+
+
+# figure out if simplejson escapes slashes.  This behaviour was changed
+# from one version to another without reason.
+if not json_available or '\\/' not in json.dumps('/'):
+
+    def _tojson_filter(*args, **kwargs):
+        if __debug__:
+            _assert_have_json()
+        return json.dumps(*args, **kwargs).replace('/', '\\/')
+else:
+    _tojson_filter = json.dumps
+
+
+class _PackageBoundObject(object):
+
+    def __init__(self, import_name):
+        #: The name of the package or module.  Do not change this once
+        #: it was set by the constructor.
+        self.import_name = import_name
+
+        #: Where is the app root located?
+        self.root_path = _get_package_path(self.import_name)
+
+    def open_resource(self, resource):
+        """Opens a resource from the application's resource folder.  To see
+        how this works, consider the following folder structure::
+
+            /myapplication.py
+            /schemal.sql
+            /static
+                /style.css
+            /templates
+                /layout.html
+                /index.html
+
+        If you want to open the `schema.sql` file you would do the
+        following::
+
+            with app.open_resource('schema.sql') as f:
+                contents = f.read()
+                do_something_with(contents)
+
+        :param resource: the name of the resource.  To access resources within
+                         subfolders use forward slashes as separator.
+        """
+        if pkg_resources is None:
+            return open(os.path.join(self.root_path, resource), 'rb')
+        return pkg_resources.resource_stream(self.import_name, resource)
+
+
+class _ModuleSetupState(object):
+
+    def __init__(self, app, url_prefix=None):
+        self.app = app
+        self.url_prefix = url_prefix
+
+
+class Module(_PackageBoundObject):
+    """Container object that enables pluggable applications.  A module can
+    be used to organize larger applications.  They represent blueprints that,
+    in combination with a :class:`Flask` object are used to create a large
+    application.
+
+    A module is like an application bound to an `import_name`.  Multiple
+    modules can share the same import names, but in that case a `name` has
+    to be provided to keep them apart.  If different import names are used,
+    the rightmost part of the import name is used as name.
+
+    Here an example structure for a larger appliation::
+
+        /myapplication
+            /__init__.py
+            /views
+                /__init__.py
+                /admin.py
+                /frontend.py
+
+    The `myapplication/__init__.py` can look like this::
+
+        from flask import Flask
+        from myapplication.views.admin import admin
+        from myapplication.views.frontend import frontend
+
+        app = Flask(__name__)
+        app.register_module(admin, url_prefix='/admin')
+        app.register_module(frontend)
+
+    And here an example view module (`myapplication/views/admin.py`)::
+
+        from flask import Module
+
+        admin = Module(__name__)
+
+        @admin.route('/')
+        def index():
+            pass
+
+        @admin.route('/login')
+        def login():
+            pass
+
+    For a gentle introduction into modules, checkout the
+    :ref:`working-with-modules` section.
+    """
+
+    def __init__(self, import_name, name=None, url_prefix=None):
+        if name is None:
+            assert '.' in import_name, 'name required if package name ' \
+                'does not point to a submodule'
+            name = import_name.rsplit('.', 1)[1]
+        _PackageBoundObject.__init__(self, import_name)
+        self.name = name
+        self.url_prefix = url_prefix
+        self._register_events = []
+
+    def route(self, rule, **options):
+        """Like :meth:`Flask.route` but for a module.  The endpoint for the
+        :func:`url_for` function is prefixed with the name of the module.
+        """
+        def decorator(f):
+            self.add_url_rule(rule, f.__name__, f, **options)
+            return f
+        return decorator
+
+    def add_url_rule(self, rule, endpoint, view_func=None, **options):
+        """Like :meth:`Flask.add_url_rule` but for a module.  The endpoint for
+        the :func:`url_for` function is prefixed with the name of the module.
+        """
+        def register_rule(state):
+            the_rule = rule
+            if state.url_prefix:
+                the_rule = state.url_prefix + rule
+            state.app.add_url_rule(the_rule, '%s.%s' % (self.name, endpoint),
+                                   view_func, **options)
+        self._record(register_rule)
+
+    def before_request(self, f):
+        """Like :meth:`Flask.before_request` but for a module.  This function
+        is only executed before each request that is handled by a function of
+        that module.
+        """
+        self._record(lambda s: s.app.before_request_funcs
+            .setdefault(self.name, []).append(f))
+        return f
+
+    def before_app_request(self, f):
+        """Like :meth:`Flask.before_request`.  Such a function is executed
+        before each request, even if outside of a module.
+        """
+        self._record(lambda s: s.app.before_request_funcs
+            .setdefault(None, []).append(f))
+        return f
+
+    def after_request(self, f):
+        """Like :meth:`Flask.after_request` but for a module.  This function
+        is only executed after each request that is handled by a function of
+        that module.
+        """
+        self._record(lambda s: s.app.after_request_funcs
+            .setdefault(self.name, []).append(f))
+        return f
+
+    def after_app_request(self, f):
+        """Like :meth:`Flask.after_request` but for a module.  Such a function
+        is executed after each request, even if outside of the module.
+        """
+        self._record(lambda s: s.app.after_request_funcs
+            .setdefault(None, []).append(f))
+        return f
+
+    def context_processor(self, f):
+        """Like :meth:`Flask.context_processor` but for a module.  This
+        function is only executed for requests handled by a module.
+        """
+        self._record(lambda s: s.app.template_context_processors
+            .setdefault(self.name, []).append(f))
+        return f
+
+    def app_context_processor(self, f):
+        """Like :meth:`Flask.context_processor` but for a module.  Such a
+        function is executed each request, even if outside of the module.
+        """
+        self._record(lambda s: s.app.template_context_processors
+            .setdefault(None, []).append(f))
+        return f
+
+    def app_errorhandler(self, code):
+        """Like :meth:`Flask.errorhandler` but for a module.  This
+        handler is used for all requests, even if outside of the module.
+
+        .. versionadded:: 0.4
+        """
+        def decorator(f):
+            self._record(lambda s: s.app.errorhandler(code)(f))
+            return f
+        return decorator
+
+    def _record(self, func):
+        self._register_events.append(func)
+
+
+class ConfigAttribute(object):
+    """Makes an attribute forward to the config"""
+
+    def __init__(self, name):
+        self.__name__ = name
+
+    def __get__(self, obj, type=None):
+        if obj is None:
+            return self
+        return obj.config[self.__name__]
+
+    def __set__(self, obj, value):
+        obj.config[self.__name__] = value
+
+
+class Config(dict):
+    """Works exactly like a dict but provides ways to fill it from files
+    or special dictionaries.  There are two common patterns to populate the
+    config.
+
+    Either you can fill the config from a config file::
+
+        app.config.from_pyfile('yourconfig.cfg')
+
+    Or alternatively you can define the configuration options in the
+    module that calls :meth:`from_object` or provide an import path to
+    a module that should be loaded.  It is also possible to tell it to
+    use the same module and with that provide the configuration values
+    just before the call::
+
+        DEBUG = True
+        SECRET_KEY = 'development key'
+        app.config.from_object(__name__)
+
+    In both cases (loading from any Python file or loading from modules),
+    only uppercase keys are added to the config.  This makes it possible to use
+    lowercase values in the config file for temporary values that are not added
+    to the config or to define the config keys in the same file that implements
+    the application.
+
+    Probably the most interesting way to load configurations is from an
+    environment variable pointing to a file::
+
+        app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+    In this case before launching the application you have to set this
+    environment variable to the file you want to use.  On Linux and OS X
+    use the export statement::
+
+        export YOURAPPLICATION_SETTINGS='/path/to/config/file'
+
+    On windows use `set` instead.
+
+    :param root_path: path to which files are read relative from.  When the
+                      config object is created by the application, this is
+                      the application's :attr:`~flask.Flask.root_path`.
+    :param defaults: an optional dictionary of default values
+    """
+
+    def __init__(self, root_path, defaults=None):
+        dict.__init__(self, defaults or {})
+        self.root_path = root_path
+
+    def from_envvar(self, variable_name, silent=False):
+        """Loads a configuration from an environment variable pointing to
+        a configuration file.  This basically is just a shortcut with nicer
+        error messages for this line of code::
+
+            app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
+
+        :param variable_name: name of the environment variable
+        :param silent: set to `True` if you want silent failing for missing
+                       files.
+        :return: bool. `True` if able to load config, `False` otherwise.
+        """
+        rv = os.environ.get(variable_name)
+        if not rv:
+            if silent:
+                return False
+            raise RuntimeError('The environment variable %r is not set '
+                               'and as such configuration could not be '
+                               'loaded.  Set this variable and make it '
+                               'point to a configuration file' %
+                               variable_name)
+        self.from_pyfile(rv)
+        return True
+
+    def from_pyfile(self, filename):
+        """Updates the values in the config from a Python file.  This function
+        behaves as if the file was imported as module with the
+        :meth:`from_object` function.
+
+        :param filename: the filename of the config.  This can either be an
+                         absolute filename or a filename relative to the
+                         root path.
+        """
+        filename = os.path.join(self.root_path, filename)
+        d = type(sys)('config')
+        d.__file__ = filename
+        execfile(filename, d.__dict__)
+        self.from_object(d)
+
+    def from_object(self, obj):
+        """Updates the values from the given object.  An object can be of one
+        of the following two types:
+
+        -   a string: in this case the object with that name will be imported
+        -   an actual object reference: that object is used directly
+
+        Objects are usually either modules or classes.
+
+        Just the uppercase variables in that object are stored in the config
+        after lowercasing.  Example usage::
+
+            app.config.from_object('yourapplication.default_config')
+            from yourapplication import default_config
+            app.config.from_object(default_config)
+
+        You should not use this function to load the actual configuration but
+        rather configuration defaults.  The actual config should be loaded
+        with :meth:`from_pyfile` and ideally from a location not within the
+        package because the package might be installed system wide.
+
+        :param obj: an import name or object
+        """
+        if isinstance(obj, basestring):
+            obj = import_string(obj)
+        for key in dir(obj):
+            if key.isupper():
+                self[key] = getattr(obj, key)
+
+    def __repr__(self):
+        return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
+
+
+class Flask(_PackageBoundObject):
+    """The flask object implements a WSGI application and acts as the central
+    object.  It is passed the name of the module or package of the
+    application.  Once it is created it will act as a central registry for
+    the view functions, the URL rules, template configuration and much more.
+
+    The name of the package is used to resolve resources from inside the
+    package or the folder the module is contained in depending on if the
+    package parameter resolves to an actual python package (a folder with
+    an `__init__.py` file inside) or a standard module (just a `.py` file).
+
+    For more information about resource loading, see :func:`open_resource`.
+
+    Usually you create a :class:`Flask` instance in your main module or
+    in the `__init__.py` file of your package like this::
+
+        from flask import Flask
+        app = Flask(__name__)
+    """
+
+    #: The class that is used for request objects.  See :class:`~flask.Request`
+    #: for more information.
+    request_class = Request
+
+    #: The class that is used for response objects.  See
+    #: :class:`~flask.Response` for more information.
+    response_class = Response
+
+    #: Path for the static files.  If you don't want to use static files
+    #: you can set this value to `None` in which case no URL rule is added
+    #: and the development server will no longer serve any static files.
+    static_path = '/static'
+
+    #: The debug flag.  Set this to `True` to enable debugging of the
+    #: application.  In debug mode the debugger will kick in when an unhandled
+    #: exception ocurrs and the integrated server will automatically reload
+    #: the application if changes in the code are detected.
+    #:
+    #: This attribute can also be configured from the config with the `DEBUG`
+    #: configuration key.  Defaults to `False`.
+    debug = ConfigAttribute('DEBUG')
+
+    #: The testing flask.  Set this to `True` to enable the test mode of
+    #: Flask extensions (and in the future probably also Flask itself).
+    #: For example this might activate unittest helpers that have an
+    #: additional runtime cost which should not be enabled by default.
+    #:
+    #: This attribute can also be configured from the config with the
+    #: `TESTING` configuration key.  Defaults to `False`.
+    testing = ConfigAttribute('TESTING')
+
+    #: If a secret key is set, cryptographic components can use this to
+    #: sign cookies and other things.  Set this to a complex random value
+    #: when you want to use the secure cookie for instance.
+    #:
+    #: This attribute can also be configured from the config with the
+    #: `SECRET_KEY` configuration key.  Defaults to `None`.
+    secret_key = ConfigAttribute('SECRET_KEY')
+
+    #: The secure cookie uses this for the name of the session cookie.
+    #:
+    #: This attribute can also be configured from the config with the
+    #: `SESSION_COOKIE_NAME` configuration key.  Defaults to ``'session'``
+    session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
+
+    #: A :class:`~datetime.timedelta` which is used to set the expiration
+    #: date of a permanent session.  The default is 31 days which makes a
+    #: permanent session survive for roughly one month.
+    #:
+    #: This attribute can also be configured from the config with the
+    #: `PERMANENT_SESSION_LIFETIME` configuration key.  Defaults to
+    #: ``timedelta(days=31)``
+    permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME')
+
+    #: Enable this if you want to use the X-Sendfile feature.  Keep in
+    #: mind that the server has to support this.  This only affects files
+    #: sent with the :func:`send_file` method.
+    #:
+    #: .. versionadded:: 0.2
+    #:
+    #: This attribute can also be configured from the config with the
+    #: `USE_X_SENDFILE` configuration key.  Defaults to `False`.
+    use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
+
+    #: The name of the logger to use.  By default the logger name is the
+    #: package name passed to the constructor.
+    #:
+    #: .. versionadded:: 0.4
+    logger_name = ConfigAttribute('LOGGER_NAME')
+
+    #: The logging format used for the debug logger.  This is only used when
+    #: the application is in debug mode, otherwise the attached logging
+    #: handler does the formatting.
+    #:
+    #: .. versionadded:: 0.3
+    debug_log_format = (
+        '-' * 80 + '\n' +
+        '%(levelname)s in %(module)s, %(pathname)s:%(lineno)d]:\n' +
+        '%(message)s\n' +
+        '-' * 80
+    )
+
+    #: Options that are passed directly to the Jinja2 environment.
+    jinja_options = ImmutableDict(
+        autoescape=True,
+        extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
+    )
+
+    #: Default configuration parameters.
+    default_config = ImmutableDict({
+        'DEBUG':                                False,
+        'TESTING':                              False,
+        'SECRET_KEY':                           None,
+        'SESSION_COOKIE_NAME':                  'session',
+        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
+        'USE_X_SENDFILE':                       False,
+        'LOGGER_NAME':                          None
+    })
+
+    def __init__(self, import_name):
+        _PackageBoundObject.__init__(self, import_name)
+
+        #: The configuration dictionary as :class:`Config`.  This behaves
+        #: exactly like a regular dictionary but supports additional methods
+        #: to load a config from files.
+        self.config = Config(self.root_path, self.default_config)
+
+        #: Prepare the deferred setup of the logger.
+        self._logger = None
+        self.logger_name = self.import_name
+
+        #: A dictionary of all view functions registered.  The keys will
+        #: be function names which are also used to generate URLs and
+        #: the values are the function objects themselves.
+        #: to register a view function, use the :meth:`route` decorator.
+        self.view_functions = {}
+
+        #: A dictionary of all registered error handlers.  The key is
+        #: be the error code as integer, the value the function that
+        #: should handle that error.
+        #: To register a error handler, use the :meth:`errorhandler`
+        #: decorator.
+        self.error_handlers = {}
+
+        #: A dictionary with lists of functions that should be called at the
+        #: beginning of the request.  The key of the dictionary is the name of
+        #: the module this function is active for, `None` for all requests.
+        #: This can for example be used to open database connections or
+        #: getting hold of the currently logged in user.  To register a
+        #: function here, use the :meth:`before_request` decorator.
+        self.before_request_funcs = {}
+
+        #: A dictionary with lists of functions that should be called after
+        #: each request.  The key of the dictionary is the name of the module
+        #: this function is active for, `None` for all requests.  This can for
+        #: example be used to open database connections or getting hold of the
+        #: currently logged in user.  To register a function here, use the
+        #: :meth:`before_request` decorator.
+        self.after_request_funcs = {}
+
+        #: A dictionary with list of functions that are called without argument
+        #: to populate the template context.  They key of the dictionary is the
+        #: name of the module this function is active for, `None` for all
+        #: requests.  Each returns a dictionary that the template context is
+        #: updated with.  To register a function here, use the
+        #: :meth:`context_processor` decorator.
+        self.template_context_processors = {
+            None: [_default_template_ctx_processor]
+        }
+
+        #: The :class:`~werkzeug.routing.Map` for this instance.  You can use
+        #: this to change the routing converters after the class was created
+        #: but before any routes are connected.  Example::
+        #:
+        #:    from werkzeug import BaseConverter
+        #:
+        #:    class ListConverter(BaseConverter):
+        #:        def to_python(self, value):
+        #:            return value.split(',')
+        #:        def to_url(self, values):
+        #:            return ','.join(BaseConverter.to_url(value)
+        #:                            for value in values)
+        #:
+        #:    app = Flask(__name__)
+        #:    app.url_map.converters['list'] = ListConverter
+        self.url_map = Map()
+
+        if self.static_path is not None:
+            self.add_url_rule(self.static_path + '/<filename>',
+                              build_only=True, endpoint='static')
+            if pkg_resources is not None:
+                target = (self.import_name, 'static')
+            else:
+                target = os.path.join(self.root_path, 'static')
+            self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
+                self.static_path: target
+            })
+
+        #: The Jinja2 environment.  It is created from the
+        #: :attr:`jinja_options` and the loader that is returned
+        #: by the :meth:`create_jinja_loader` function.
+        self.jinja_env = Environment(loader=self.create_jinja_loader(),
+                                     **self.jinja_options)
+        self.jinja_env.globals.update(
+            url_for=url_for,
+            get_flashed_messages=get_flashed_messages
+        )
+        self.jinja_env.filters['tojson'] = _tojson_filter
+
+    @property
+    def logger(self):
+        """A :class:`logging.Logger` object for this application.  The
+        default configuration is to log to stderr if the application is
+        in debug mode.  This logger can be used to (surprise) log messages.
+        Here some examples::
+
+            app.logger.debug('A value for debugging')
+            app.logger.warning('A warning ocurred (%d apples)', 42)
+            app.logger.error('An error occoured')
+
+        .. versionadded:: 0.3
+        """
+        if self._logger and self._logger.name == self.logger_name:
+            return self._logger
+        with _logger_lock:
+            if self._logger and self._logger.name == self.logger_name:
+                return self._logger
+            from logging import getLogger, StreamHandler, Formatter, DEBUG
+            class DebugHandler(StreamHandler):
+                def emit(x, record):
+                    if self.debug:
+                        StreamHandler.emit(x, record)
+            handler = DebugHandler()
+            handler.setLevel(DEBUG)
+            handler.setFormatter(Formatter(self.debug_log_format))
+            logger = getLogger(self.logger_name)
+            logger.addHandler(handler)
+            self._logger = logger
+            return logger
+
+    def create_jinja_loader(self):
+        """Creates the Jinja loader.  By default just a package loader for
+        the configured package is returned that looks up templates in the
+        `templates` folder.  To add other loaders it's possible to
+        override this method.
+        """
+        if pkg_resources is None:
+            return FileSystemLoader(os.path.join(self.root_path, 'templates'))
+        return PackageLoader(self.import_name)
+
+    def update_template_context(self, context):
+        """Update the template context with some commonly used variables.
+        This injects request, session and g into the template context.
+
+        :param context: the context as a dictionary that is updated in place
+                        to add extra variables.
+        """
+        funcs = self.template_context_processors[None]
+        mod = _request_ctx_stack.top.request.module
+        if mod is not None and mod in self.template_context_processors:
+            funcs = chain(funcs, self.template_context_processors[mod])
+        for func in funcs:
+            context.update(func())
+
+    def run(self, host='127.0.0.1', port=5000, **options):
+        """Runs the application on a local development server.  If the
+        :attr:`debug` flag is set the server will automatically reload
+        for code changes and show a debugger in case an exception happened.
+
+        :param host: the hostname to listen on.  set this to ``'0.0.0.0'``
+                     to have the server available externally as well.
+        :param port: the port of the webserver
+        :param options: the options to be forwarded to the underlying
+                        Werkzeug server.  See :func:`werkzeug.run_simple`
+                        for more information.
+        """
+        from werkzeug import run_simple
+        if 'debug' in options:
+            self.debug = options.pop('debug')
+        options.setdefault('use_reloader', self.debug)
+        options.setdefault('use_debugger', self.debug)
+        return run_simple(host, port, self, **options)
+
+    def test_client(self):
+        """Creates a test client for this application.  For information
+        about unit testing head over to :ref:`testing`.
+
+        The test client can be used in a `with` block to defer the closing down
+        of the context until the end of the `with` block.  This is useful if
+        you want to access the context locals for testing::
+
+            with app.test_client() as c:
+                rv = c.get('/?vodka=42')
+                assert request.args['vodka'] == '42'
+
+        .. versionchanged:: 0.4
+           added support for `with` block usage for the client.
+        """
+        from werkzeug import Client
+        class FlaskClient(Client):
+            preserve_context = context_preserved = False
+            def open(self, *args, **kwargs):
+                if self.context_preserved:
+                    _request_ctx_stack.pop()
+                    self.context_preserved = False
+                kwargs.setdefault('environ_overrides', {}) \
+                    ['flask._preserve_context'] = self.preserve_context
+                old = _request_ctx_stack.top
+                try:
+                    return Client.open(self, *args, **kwargs)
+                finally:
+                    self.context_preserved = _request_ctx_stack.top is not old
+            def __enter__(self):
+                self.preserve_context = True
+                return self
+            def __exit__(self, exc_type, exc_value, tb):
+                self.preserve_context = False
+                if self.context_preserved:
+                    _request_ctx_stack.pop()
+        return FlaskClient(self, self.response_class, use_cookies=True)
+
+    def open_session(self, request):
+        """Creates or opens a new session.  Default implementation stores all
+        session data in a signed cookie.  This requires that the
+        :attr:`secret_key` is set.
+
+        :param request: an instance of :attr:`request_class`.
+        """
+        key = self.secret_key
+        if key is not None:
+            return Session.load_cookie(request, self.session_cookie_name,
+                                       secret_key=key)
+
+    def save_session(self, session, response):
+        """Saves the session if it needs updates.  For the default
+        implementation, check :meth:`open_session`.
+
+        :param session: the session to be saved (a
+                        :class:`~werkzeug.contrib.securecookie.SecureCookie`
+                        object)
+        :param response: an instance of :attr:`response_class`
+        """
+        expires = None
+        if session.permanent:
+            expires = datetime.utcnow() + self.permanent_session_lifetime
+        session.save_cookie(response, self.session_cookie_name,
+                            expires=expires, httponly=True)
+
+    def register_module(self, module, **options):
+        """Registers a module with this application.  The keyword argument
+        of this function are the same as the ones for the constructor of the
+        :class:`Module` class and will override the values of the module if
+        provided.
+        """
+        options.setdefault('url_prefix', module.url_prefix)
+        state = _ModuleSetupState(self, **options)
+        for func in module._register_events:
+            func(state)
+
+    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+        """Connects a URL rule.  Works exactly like the :meth:`route`
+        decorator.  If a view_func is provided it will be registered with the
+        endpoint.
+
+        Basically this example::
+
+            @app.route('/')
+            def index():
+                pass
+
+        Is equivalent to the following::
+
+            def index():
+                pass
+            app.add_url_rule('/', 'index', index)
+
+        If the view_func is not provided you will need to connect the endpoint
+        to a view function like so::
+
+            app.view_functions['index'] = index
+
+        .. versionchanged:: 0.2
+           `view_func` parameter added.
+
+        :param rule: the URL rule as string
+        :param endpoint: the endpoint for the registered URL rule.  Flask
+                         itself assumes the name of the view function as
+                         endpoint
+        :param view_func: the function to call when serving a request to the
+                          provided endpoint
+        :param options: the options to be forwarded to the underlying
+                        :class:`~werkzeug.routing.Rule` object
+        """
+        if endpoint is None:
+            assert view_func is not None, 'expected view func if endpoint ' \
+                                          'is not provided.'
+            endpoint = view_func.__name__
+        options['endpoint'] = endpoint
+        options.setdefault('methods', ('GET',))
+        self.url_map.add(Rule(rule, **options))
+        if view_func is not None:
+            self.view_functions[endpoint] = view_func
+
+    def route(self, rule, **options):
+        """A decorator that is used to register a view function for a
+        given URL rule.  Example::
+
+            @app.route('/')
+            def index():
+                return 'Hello World'
+
+        Variables parts in the route can be specified with angular
+        brackets (``/user/<username>``).  By default a variable part
+        in the URL accepts any string without a slash however a different
+        converter can be specified as well by using ``<converter:name>``.
+
+        Variable parts are passed to the view function as keyword
+        arguments.
+
+        The following converters are possible:
+
+        =========== ===========================================
+        `int`       accepts integers
+        `float`     like `int` but for floating point values
+        `path`      like the default but also accepts slashes
+        =========== ===========================================
+
+        Here some examples::
+
+            @app.route('/')
+            def index():
+                pass
+
+            @app.route('/<username>')
+            def show_user(username):
+                pass
+
+            @app.route('/post/<int:post_id>')
+            def show_post(post_id):
+                pass
+
+        An important detail to keep in mind is how Flask deals with trailing
+        slashes.  The idea is to keep each URL unique so the following rules
+        apply:
+
+        1. If a rule ends with a slash and is requested without a slash
+           by the user, the user is automatically redirected to the same
+           page with a trailing slash attached.
+        2. If a rule does not end with a trailing slash and the user request
+           the page with a trailing slash, a 404 not found is raised.
+
+        This is consistent with how web servers deal with static files.  This
+        also makes it possible to use relative link targets safely.
+
+        The :meth:`route` decorator accepts a couple of other arguments
+        as well:
+
+        :param rule: the URL rule as string
+        :param methods: a list of methods this rule should be limited
+                        to (``GET``, ``POST`` etc.).  By default a rule
+                        just listens for ``GET`` (and implicitly ``HEAD``).
+        :param subdomain: specifies the rule for the subdoain in case
+                          subdomain matching is in use.
+        :param strict_slashes: can be used to disable the strict slashes
+                               setting for this rule.  See above.
+        :param options: other options to be forwarded to the underlying
+                        :class:`~werkzeug.routing.Rule` object.
+        """
+        def decorator(f):
+            self.add_url_rule(rule, None, f, **options)
+            return f
+        return decorator
+
+    def errorhandler(self, code):
+        """A decorator that is used to register a function give a given
+        error code.  Example::
+
+            @app.errorhandler(404)
+            def page_not_found(error):
+                return 'This page does not exist', 404
+
+        You can also register a function as error handler without using
+        the :meth:`errorhandler` decorator.  The following example is
+        equivalent to the one above::
+
+            def page_not_found(error):
+                return 'This page does not exist', 404
+            app.error_handlers[404] = page_not_found
+
+        :param code: the code as integer for the handler
+        """
+        def decorator(f):
+            self.error_handlers[code] = f
+            return f
+        return decorator
+
+    def template_filter(self, name=None):
+        """A decorator that is used to register custom template filter.
+        You can specify a name for the filter, otherwise the function
+        name will be used. Example::
+
+          @app.template_filter()
+          def reverse(s):
+              return s[::-1]
+
+        :param name: the optional name of the filter, otherwise the
+                     function name will be used.
+        """
+        def decorator(f):
+            self.jinja_env.filters[name or f.__name__] = f
+            return f
+        return decorator
+
+    def before_request(self, f):
+        """Registers a function to run before each request."""
+        self.before_request_funcs.setdefault(None, []).append(f)
+        return f
+
+    def after_request(self, f):
+        """Register a function to be run after each request."""
+        self.after_request_funcs.setdefault(None, []).append(f)
+        return f
+
+    def context_processor(self, f):
+        """Registers a template context processor function."""
+        self.template_context_processors[None].append(f)
+        return f
+
+    def handle_http_exception(self, e):
+        """Handles an HTTP exception.  By default this will invoke the
+        registered error handlers and fall back to returning the
+        exception as response.
+
+        .. versionadded: 0.3
+        """
+        handler = self.error_handlers.get(e.code)
+        if handler is None:
+            return e
+        return handler(e)
+
+    def handle_exception(self, e):
+        """Default exception handling that kicks in when an exception
+        occours that is not catched.  In debug mode the exception will
+        be re-raised immediately, otherwise it is logged and the handler
+        for a 500 internal server error is used.  If no such handler
+        exists, a default 500 internal server error message is displayed.
+
+        .. versionadded: 0.3
+        """
+        handler = self.error_handlers.get(500)
+        if self.debug:
+            raise
+        self.logger.exception('Exception on %s [%s]' % (
+            request.path,
+            request.method
+        ))
+        if handler is None:
+            return InternalServerError()
+        return handler(e)
+
+    def dispatch_request(self):
+        """Does the request dispatching.  Matches the URL and returns the
+        return value of the view or error handler.  This does not have to
+        be a response object.  In order to convert the return value to a
+        proper response object, call :func:`make_response`.
+        """
+        req = _request_ctx_stack.top.request
+        try:
+            if req.routing_exception is not None:
+                raise req.routing_exception
+            return self.view_functions[req.endpoint](**req.view_args)
+        except HTTPException, e:
+            return self.handle_http_exception(e)
+
+    def make_response(self, rv):
+        """Converts the return value from a view function to a real
+        response object that is an instance of :attr:`response_class`.
+
+        The following types are allowed for `rv`:
+
+        .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
+
+        ======================= ===========================================
+        :attr:`response_class`  the object is returned unchanged
+        :class:`str`            a response object is created with the
+                                string as body
+        :class:`unicode`        a response object is created with the
+                                string encoded to utf-8 as body
+        :class:`tuple`          the response object is created with the
+                                contents of the tuple as arguments
+        a WSGI function         the function is called as WSGI application
+                                and buffered as response object
+        ======================= ===========================================
+
+        :param rv: the return value from the view function
+        """
+        if rv is None:
+            raise ValueError('View function did not return a response')
+        if isinstance(rv, self.response_class):
+            return rv
+        if isinstance(rv, basestring):
+            return self.response_class(rv)
+        if isinstance(rv, tuple):
+            return self.response_class(*rv)
+        return self.response_class.force_type(rv, request.environ)
+
+    def preprocess_request(self):
+        """Called before the actual request dispatching and will
+        call every as :meth:`before_request` decorated function.
+        If any of these function returns a value it's handled as
+        if it was the return value from the view and further
+        request handling is stopped.
+        """
+        funcs = self.before_request_funcs.get(None, ())
+        mod = request.module
+        if mod and mod in self.before_request_funcs:
+            funcs = chain(funcs, self.before_request_funcs[mod])
+        for func in funcs:
+            rv = func()
+            if rv is not None:
+                return rv
+
+    def process_response(self, response):
+        """Can be overridden in order to modify the response object
+        before it's sent to the WSGI server.  By default this will
+        call all the :meth:`after_request` decorated functions.
+
+        :param response: a :attr:`response_class` object.
+        :return: a new response object or the same, has to be an
+                 instance of :attr:`response_class`.
+        """
+        ctx = _request_ctx_stack.top
+        mod = ctx.request.module
+        if not isinstance(ctx.session, _NullSession):
+            self.save_session(ctx.session, response)
+        funcs = ()
+        if mod and mod in self.after_request_funcs:
+            funcs = chain(funcs, self.after_request_funcs[mod])
+        if None in self.after_request_funcs:
+            funcs = chain(funcs, self.after_request_funcs[None])
+        for handler in funcs:
+            response = handler(response)
+        return response
+
+    def wsgi_app(self, environ, start_response):
+        """The actual WSGI application.  This is not implemented in
+        `__call__` so that middlewares can be applied without losing a
+        reference to the class.  So instead of doing this::
+
+            app = MyMiddleware(app)
+
+        It's a better idea to do this instead::
+
+            app.wsgi_app = MyMiddleware(app.wsgi_app)
+
+        Then you still have the original application object around and
+        can continue to call methods on it.
+
+        .. versionchanged:: 0.4
+           The :meth:`after_request` functions are now called even if an
+           error handler took over request processing.  This ensures that
+           even if an exception happens database have the chance to
+           properly close the connection.
+
+        :param environ: a WSGI environment
+        :param start_response: a callable accepting a status code,
+                               a list of headers and an optional
+                               exception context to start the response
+        """
+        with self.request_context(environ):
+            try:
+                rv = self.preprocess_request()
+                if rv is None:
+                    rv = self.dispatch_request()
+                response = self.make_response(rv)
+            except Exception, e:
+                response = self.make_response(self.handle_exception(e))
+            try:
+                response = self.process_response(response)
+            except Exception, e:
+                response = self.make_response(self.handle_exception(e))
+            return response(environ, start_response)
+
+    def request_context(self, environ):
+        """Creates a request context from the given environment and binds
+        it to the current context.  This must be used in combination with
+        the `with` statement because the request is only bound to the
+        current context for the duration of the `with` block.
+
+        Example usage::
+
+            with app.request_context(environ):
+                do_something_with(request)
+
+        The object returned can also be used without the `with` statement
+        which is useful for working in the shell.  The example above is
+        doing exactly the same as this code::
+
+            ctx = app.request_context(environ)
+            ctx.push()
+            try:
+                do_something_with(request)
+            finally:
+                ctx.pop()
+
+        The big advantage of this approach is that you can use it without
+        the try/finally statement in a shell for interactive testing:
+
+        >>> ctx = app.test_request_context()
+        >>> ctx.bind()
+        >>> request.path
+        u'/'
+        >>> ctx.unbind()
+
+        .. versionchanged:: 0.3
+           Added support for non-with statement usage and `with` statement
+           is now passed the ctx object.
+
+        :param environ: a WSGI environment
+        """
+        return _RequestContext(self, environ)
+
+    def test_request_context(self, *args, **kwargs):
+        """Creates a WSGI environment from the given values (see
+        :func:`werkzeug.create_environ` for more information, this
+        function accepts the same arguments).
+        """
+        return self.request_context(create_environ(*args, **kwargs))
+
+    def __call__(self, environ, start_response):
+        """Shortcut for :attr:`wsgi_app`."""
+        return self.wsgi_app(environ, start_response)
+
+
+# context locals
+_request_ctx_stack = LocalStack()
+current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
+request = LocalProxy(lambda: _request_ctx_stack.top.request)
+session = LocalProxy(lambda: _request_ctx_stack.top.session)
+g = LocalProxy(lambda: _request_ctx_stack.top.g)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/setup.cfg	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,6 @@
+[egg_info]
+tag_build = dev
+tag_date = true
+
+[aliases]
+release = egg_info -RDb ''
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/setup.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,79 @@
+"""
+Flask
+-----
+
+Flask is a microframework for Python based on Werkzeug, Jinja 2 and good
+intentions. And before you ask: It's BSD licensed!
+
+Flask is Fun
+````````````
+
+::
+
+    from flask import Flask
+    app = Flask(__name__)
+
+    @app.route("/")
+    def hello():
+        return "Hello World!"
+
+    if __name__ == "__main__":
+        app.run()
+
+And Easy to Setup
+`````````````````
+
+::
+
+    $ easy_install Flask
+    $ python hello.py
+     * Running on http://localhost:5000/
+
+Links
+`````
+
+* `website <http://flask.pocoo.org/>`_
+* `documentation <http://flask.pocoo.org/docs/>`_
+* `development version
+  <http://github.com/mitsuhiko/flask/zipball/master#egg=Flask-dev>`_
+
+"""
+from setuptools import setup
+
+
+def run_tests():
+    import os, sys
+    sys.path.append(os.path.join(os.path.dirname(__file__), 'tests'))
+    from flask_tests import suite
+    return suite()
+
+
+setup(
+    name='Flask',
+    version='0.4',
+    url='http://github.com/mitsuhiko/flask/',
+    license='BSD',
+    author='Armin Ronacher',
+    author_email='armin.ronacher@active-4.com',
+    description='A microframework based on Werkzeug, Jinja2 '
+                'and good intentions',
+    long_description=__doc__,
+    py_modules=['flask'],
+    zip_safe=False,
+    platforms='any',
+    install_requires=[
+        'Werkzeug>=0.6.1',
+        'Jinja2>=2.4'
+    ],
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+        'Topic :: Software Development :: Libraries :: Python Modules'
+    ],
+    test_suite='__main__.run_tests'
+)
--- a/bundled/jinja2/.hgtags	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/.hgtags	Fri Jun 11 21:25:17 2010 -0400
@@ -6,3 +6,10 @@
 b987be13b8f6603a2135e0f912e8aacb72f21181 2.2.1
 2eb624b634a6bf62bc2b8e16a8eca3d41a515b34 2.3
 140c54afc249d855745778f74011978bb9ba3439 2.3.1
+e3f873a9d3ffa75f147446db1cb580be2f6403dd 2.4
+d927a7499531e4c0f324a2dc34039f7c09877399 2.4.1
+3f3eadf15f616bf269a6bc2bd037a14e0faf3477 2.5
+3f3eadf15f616bf269a6bc2bd037a14e0faf3477 2.5
+0000000000000000000000000000000000000000 2.5
+0000000000000000000000000000000000000000 2.5
+0dd0062a64804df19c5bbcfc4fad860b2434e09b 2.5
--- a/bundled/jinja2/CHANGES	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/CHANGES	Fri Jun 11 21:25:17 2010 -0400
@@ -1,16 +1,51 @@
 Jinja2 Changelog
 ================
 
+Version 2.6
+-----------
+(codename to be selected, release date to be announced)
+
+- StopIteration exceptions raised by functions called from templates
+  are now intercepted and converted to undefineds.  This solves a
+  lot of debugging grief.  (StopIteration is used internally to
+  abort template execution)
+- improved performance of macro calls slightly.
+
+Version 2.5
+-----------
+(codename Incoherence, relased on May 29th 2010)
+
+- improved the sort filter (should have worked like this for a
+  long time) by adding support for case insensitive searches.
+- fixed a bug for getattribute constant folding.
+- support for newstyle gettext translations which result in a
+  nicer in-template user interface and more consistent
+  catalogs. (:ref:`newstyle-gettext`)
+- it's now possible to register extensions after an environment
+  was created.
+
+Version 2.4.1
+-------------
+(bugfix release, released on April 20th 2010)
+
+- fixed an error reporting bug for undefineds.
+
 Version 2.4
 -----------
-(codename to be selected, release date unknown)
+(codename Correlation, released on April 13th 2010)
 
 - the environment template loading functions now transparently
   pass through a template object if it was passed to it.  This
   makes it possible to import or extend from a template object
   that was passed to the template.
-
+- added a :class:`ModuleLoader` that can load templates from
+  precompiled sources.  The environment now features a method
+  to compile the templates from a configured loader into a zip
+  file or folder.
 - the _speedups C extension now supports Python 3.
+- added support for autoescaping toggling sections and support
+  for evaluation contexts (:ref:`eval-context`).
+- extensions have a priority now.
 
 Version 2.3.1
 -------------
--- a/bundled/jinja2/MANIFEST.in	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/MANIFEST.in	Fri Jun 11 21:25:17 2010 -0400
@@ -6,3 +6,7 @@
 recursive-include examples *
 recursive-include jinja2/testsuite/res *
 recursive-exclude docs/_build/doctrees *
+recursive-exclude jinja2 *.pyc
+recursive-exclude docs *.pyc
+recursive-exclude jinja2 *.pyo
+recursive-exclude docs *.pyo
--- a/bundled/jinja2/artwork/jinjalogo.svg	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://web.resource.org/cc/"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="300"
-   height="120"
-   id="svg2"
-   sodipodi:version="0.32"
-   inkscape:version="0.45.1"
-   version="1.0"
-   sodipodi:docbase="/Users/mitsuhiko/Development/jinja2/artwork"
-   sodipodi:docname="jinjalogo.svg"
-   inkscape:export-filename="/Users/mitsuhiko/Development/jinja2/docs/_static/jinjabanner.png"
-   inkscape:export-xdpi="60"
-   inkscape:export-ydpi="60"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape">
-  <defs
-     id="defs4">
-    <linearGradient
-       id="linearGradient6558">
-      <stop
-         style="stop-color:#575757;stop-opacity:1;"
-         offset="0"
-         id="stop6560" />
-      <stop
-         style="stop-color:#2f2f2f;stop-opacity:1;"
-         offset="1"
-         id="stop6562" />
-    </linearGradient>
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient6558"
-       id="radialGradient6564"
-       cx="61.297766"
-       cy="60.910986"
-       fx="61.297766"
-       fy="60.910986"
-       r="44.688254"
-       gradientTransform="matrix(1,0,0,0.945104,0,3.343747)"
-       gradientUnits="userSpaceOnUse" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient6558"
-       id="radialGradient6580"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(1,0,0,0.945104,0.355158,3.334402)"
-       cx="61.297766"
-       cy="60.910986"
-       fx="61.297766"
-       fy="60.910986"
-       r="44.688254" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient6558"
-       id="linearGradient4173"
-       x1="255.15521"
-       y1="32.347946"
-       x2="279.8912"
-       y2="32.347946"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(0.8073249,0,0,0.8073249,57.960878,7.4036303)" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient6558"
-       id="linearGradient5145"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(0.7902775,0,0,0.82474,60.019977,8.0684132)"
-       x1="255.15521"
-       y1="32.347946"
-       x2="279.8912"
-       y2="32.347946" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     gridtolerance="10000"
-     guidetolerance="10"
-     objecttolerance="10"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="2.8"
-     inkscape:cx="137.4752"
-     inkscape:cy="57.574575"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     width="300px"
-     height="120px"
-     showguides="true"
-     inkscape:guide-bbox="true"
-     inkscape:window-width="1396"
-     inkscape:window-height="900"
-     inkscape:window-x="0"
-     inkscape:window-y="22" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1">
-    <path
-       style="font-size:12px;font-style:normal;font-weight:normal;fill:#f4f4f4;fill-opacity:1;stroke:#e7e7e7;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-family:Bitstream Vera Sans;stroke-miterlimit:4;stroke-dasharray:none"
-       d="M 165.36463,80.874808 L 165.36463,80.874808 L 153.32556,80.874808 L 153.32556,81.8344 L 147.64994,81.8344 L 147.64994,36.035583 L 165.36463,36.035583 L 165.36463,20.333129 C 170.58154,21.031083 173.07533,22.077914 172.84609,23.473621 C 172.78871,24.055258 172.21545,24.549594 171.12624,24.956624 L 171.12624,36.035583 L 189.09895,36.035583 L 189.09895,82.532286 L 183.33733,82.532286 L 183.33733,80.874808 L 171.12624,80.874808 L 171.12624,102.94548 L 165.36463,102.94548 L 165.36463,80.874808 M 153.32556,55.489173 L 153.32556,55.489173 L 165.36463,55.489173 L 165.36463,41.793146 L 153.32556,41.793146 L 153.32556,55.489173 M 171.12624,55.489173 L 171.12624,55.489173 L 183.33733,55.489173 L 183.33733,41.793146 L 171.12624,41.793146 L 171.12624,55.489173 M 183.33733,61.333977 L 183.33733,61.333977 L 171.12624,61.333977 L 171.12624,75.030006 L 183.33733,75.030006 L 183.33733,61.333977 M 165.36463,61.333977 L 165.36463,61.333977 L 153.32556,61.333977 L 153.32556,75.030006 L 165.36463,75.030006 L 165.36463,61.333977 M 132.85897,59.414792 C 137.33069,63.136883 140.99969,67.934848 143.86618,73.808701 L 139.13654,77.385372 C 137.24467,72.965445 134.6362,69.12707 131.31114,65.87024 L 131.31114,102.94548 L 125.63554,102.94548 L 125.63554,68.57455 C 122.31042,71.947693 118.52671,74.913707 114.28436,77.47261 L 109.64069,73.372526 C 121.50782,67.091566 130.62312,55.489212 136.98668,38.565417 L 116.26221,38.565417 L 116.26221,32.720615 L 125.80754,32.720615 L 125.80754,20.333129 C 130.85245,21.031083 133.31761,22.048838 133.20299,23.386383 C 133.14561,24.026183 132.57235,24.549594 131.48307,24.956624 L 131.48307,32.720615 L 140.77043,32.720615 L 143.60824,36.733469 C 140.68444,45.51526 137.10137,53.075692 132.85897,59.414792 M 254.11016,49.469901 L 254.11016,49.469901 L 254.11016,20.333129 C 259.21243,21.031083 261.67755,22.048838 261.50562,23.386383 C 261.44823,23.909869 261.04699,24.346044 260.30172,24.694917 C 260.30164,24.694986 260.30164,24.694986 260.30172,24.694917 L 260.30172,24.694917 L 259.78578,24.956624 L 259.78578,49.469901 L 277.15652,49.469901 L 277.15652,55.227471 L 259.78578,55.227471 L 259.78578,93.785712 L 281.45616,93.785712 L 281.45616,99.63051 L 232.35378,99.63051 L 232.35378,93.785712 L 254.11016,93.785712 L 254.11016,55.227471 L 236.22346,55.227471 L 236.22346,49.469901 L 254.11016,49.469901 M 225.5603,59.327554 C 231.12111,63.107798 235.62145,67.876693 239.06127,73.634235 L 234.76157,77.647079 C 231.60845,72.180322 227.82475,67.934848 223.41044,64.910648 L 223.41044,102.94548 L 217.73484,102.94548 L 217.73484,67.44049 C 212.91919,71.627831 207.70222,75.030021 202.084,77.647079 L 197.87027,73.198053 C 212.66118,66.917101 224.01239,55.372897 231.92377,38.565417 L 205.35172,38.565417 L 205.35172,32.720615 L 217.99283,32.720615 L 217.99283,20.333129 C 223.03774,21.031083 225.50291,22.048838 225.38829,23.386383 C 225.33089,24.026183 224.75765,24.549594 223.66837,24.956624 L 223.66837,32.720615 L 236.22346,32.720615 L 238.80326,36.733469 C 235.13421,45.51526 230.71987,53.046611 225.5603,59.327554"
-       id="text4761" />
-    <path
-       style="font-size:44.09793472px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#b41717;fill-opacity:1;stroke:#7f2828;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Candara;stroke-miterlimit:4;stroke-dasharray:none"
-       d="M 149.14708,37.774469 C 148.97807,41.117899 148.84526,44.824225 148.74871,48.893456 C 148.67626,52.962754 148.3818,70.641328 148.38184,75.524422 C 148.3818,79.065795 148.05588,81.991266 147.40406,84.300835 C 146.75219,86.610422 145.72612,88.557071 144.32585,90.140779 C 142.94969,91.724494 141.17522,92.901283 139.00239,93.671139 C 136.82953,94.440996 134.22211,94.825935 131.18014,94.825935 C 128.83828,94.825935 126.73787,94.59498 124.87889,94.133049 L 125.4221,89.31593 C 127.13623,90.0418 128.92278,90.404734 130.78177,90.404733 C 132.85805,90.404734 134.66875,90.140782 136.2139,89.612876 C 137.78315,89.062981 139.02651,88.216133 139.94396,87.072335 C 140.8855,85.928548 141.54942,84.520804 141.93572,82.8491 C 142.34613,81.177412 142.55134,78.988811 142.55136,76.283285 C 142.55134,66.297119 142.62852,44.659257 142.26641,37.774469 L 149.14708,37.774469 M 166.38498,80.732697 L 159.83024,80.732697 C 160.16821,76.333498 160.33723,71.307412 160.33723,65.654424 C 160.33723,59.2976 159.91471,53.963567 159.06973,49.652319 L 166.31257,48.761483 C 166.02284,53.358679 165.87799,58.98965 165.87799,65.654424 C 165.87799,70.933479 166.04699,75.959565 166.38498,80.732697 M 167.90601,39.490159 C 167.90598,40.611994 167.5076,41.590815 166.7109,42.42662 C 165.91418,43.240515 164.79155,43.647442 163.343,43.647399 C 162.11172,43.647442 161.146,43.295504 160.44588,42.591595 C 159.76988,41.865769 159.43188,40.996927 159.43188,39.98507 C 159.43188,38.885304 159.84231,37.928485 160.66315,37.114591 C 161.48399,36.30078 162.61869,35.893853 164.06727,35.893811 C 165.25023,35.893853 166.17975,36.256783 166.85575,36.982609 C 167.55588,37.686526 167.90598,38.522373 167.90601,39.490159 M 206.72748,80.732697 L 200.13651,80.732697 C 200.66763,74.947749 200.93319,68.634899 200.9332,61.794122 C 200.93319,58.406756 200.1727,56.097177 198.65174,54.865371 C 197.15487,53.61163 195.00619,52.984747 192.20564,52.984714 C 188.77731,52.984747 185.61465,54.117535 182.71753,56.383099 C 182.71753,63.883761 182.76583,72.000287 182.86238,80.732697 L 176.27142,80.732697 C 176.68182,73.254058 176.88707,67.843042 176.88707,64.499632 C 176.88707,59.352589 176.3559,54.359493 175.29363,49.520339 L 181.66734,48.695493 L 182.35539,52.720761 L 182.64511,52.720761 C 186.21823,49.773323 190.04483,48.299592 194.12499,48.299567 C 198.13265,48.299592 201.23499,49.113454 203.43201,50.741118 C 205.62895,52.346863 206.72747,55.217334 206.72748,59.352563 C 206.72747,59.770507 206.70331,60.595362 206.65507,61.827118 C 206.60675,63.058915 206.5826,63.883761 206.58262,64.30167 C 206.5826,67.975018 206.63088,73.452022 206.72748,80.732697 M 222.69791,48.695493 C 222.28747,55.514282 222.08225,62.355041 222.08225,69.21778 C 222.08225,71.043461 222.14262,73.463019 222.26332,76.476468 C 222.40822,79.467925 222.4806,81.502559 222.48063,82.580363 C 222.4806,89.685068 219.51105,93.996287 213.57195,95.514024 L 211.76124,93.006484 C 213.90995,91.356766 215.2378,89.597085 215.74478,87.727431 C 216.49321,85.043912 216.86743,79.324953 216.86743,70.570535 C 216.86743,61.178248 216.3846,54.16153 215.41887,49.520339 L 222.69791,48.695493 M 224.2551,39.490159 C 224.2551,40.611994 223.85673,41.590815 223.06006,42.42662 C 222.26332,43.240515 221.14069,43.647442 219.69213,43.647399 C 218.46084,43.647442 217.49515,43.295504 216.795,42.591595 C 216.119,41.865769 215.781,40.996927 215.781,39.98507 C 215.781,38.885304 216.19144,37.928485 217.01231,37.114591 C 217.83316,36.30078 218.96785,35.893853 220.4164,35.893811 C 221.5994,35.893853 222.52889,36.256783 223.20492,36.982609 C 223.90503,37.686526 224.2551,38.522373 224.2551,39.490159 M 259.60008,80.732697 L 253.91446,80.930661 C 253.62473,79.852857 253.47987,78.830045 253.4799,77.862216 L 253.11774,77.862216 C 250.14817,80.325772 246.10427,81.557546 240.98606,81.557547 C 238.20962,81.557546 235.8195,80.820682 233.81563,79.346948 C 231.81178,77.851221 230.80988,75.728607 230.80988,72.979099 C 230.80988,69.591724 232.37914,66.875216 235.51769,64.829574 C 238.65625,62.761967 244.48667,61.67316 253.00913,61.563165 C 253.08155,61.035275 253.11772,60.430386 253.11774,59.748497 C 253.11772,57.043003 252.32104,55.239336 250.72765,54.337474 C 249.15832,53.435661 246.76819,52.984747 243.55721,52.984714 C 239.76681,52.984747 236.03678,53.413668 232.3671,54.271484 L 232.9827,49.718301 C 236.60411,48.77251 240.76873,48.299592 245.47658,48.299567 C 249.77395,48.299592 253.09359,49.113454 255.43545,50.741118 C 257.77728,52.346863 258.94819,55.096363 258.94824,58.989625 C 258.94819,60.023469 258.88785,61.904117 258.76715,64.631608 C 258.67054,67.337133 258.62228,69.140806 258.6223,70.042632 C 258.62228,74.045913 258.94819,77.609265 259.60008,80.732697 M 253.19019,74.331856 C 253.06945,70.988469 253.00909,67.986016 253.00913,65.324484 C 248.47027,65.324498 245.01786,65.632443 242.65187,66.248318 C 238.69248,67.348131 236.71278,69.448748 236.71278,72.550177 C 236.71278,75.541643 239.03044,77.037371 243.66588,77.037366 C 247.64942,77.037371 250.82416,76.135534 253.19019,74.331856"
-       id="text3736"
-       sodipodi:nodetypes="ccsscssccscccsccccsccsccsscsssccccscscccsccccscsssccscscccscccsscsssccccccscsccscsccscscsccccssc" />
-    <path
-       style="fill:url(#radialGradient6564);fill-opacity:1.0;fill-rule:evenodd;stroke:#323232;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
-       d="M 105.45673,18.675923 C 105.45673,18.675923 88.211949,26.918461 74.172834,28.737898 C 60.133727,30.557333 33.360434,32.377571 28.045622,31.093256 C 22.730818,29.808941 18.915645,28.309196 18.915645,28.309196 L 20.021441,32.056583 L 16.609513,35.052471 L 17.2144,36.121726 L 18.61792,36.22764 L 22.92773,36.762252 L 23.532621,38.688909 L 25.937975,38.905784 L 27.143021,42.970927 C 27.143021,42.970927 32.254764,43.399628 33.758953,43.399628 C 35.263142,43.399628 38.271966,43.187802 38.271966,43.187802 L 38.371202,44.791657 L 39.477002,45.003495 L 39.477002,46.824227 L 37.066917,48.967759 L 37.671807,49.073671 L 37.671807,49.820127 C 37.671807,49.820127 32.255457,50.252157 30.049301,49.93109 C 27.843157,49.610006 27.440747,49.608286 27.440747,49.608286 L 27.242258,49.820127 L 27.143021,50.783455 L 27.643946,50.783455 L 27.84242,54.959544 L 38.976091,54.530844 L 38.172728,68.980747 L 38.073481,70.796442 L 28.645781,70.261816 L 28.546544,66.408513 L 30.649462,66.408513 L 30.852673,64.910557 L 32.757107,64.481857 L 33.059555,64.058192 L 25.937975,62.343374 L 20.522364,63.947229 L 21.42496,64.698732 L 22.327572,64.698732 L 22.426809,65.984848 L 24.331254,66.09076 L 24.331254,69.838147 L 22.228335,70.372777 L 22.630009,71.225146 L 23.130934,71.547931 L 23.130934,74.437917 L 24.435218,74.437917 L 24.435218,87.813529 L 22.327572,88.13632 L 22.630009,91.989617 L 23.929569,92.206492 L 23.731093,100.98236 L 29.449141,101.08826 L 28.244105,92.418334 L 36.868446,92.206492 L 36.268285,96.912181 L 35.464925,100.23086 L 44.188501,100.33677 L 44.287739,91.777793 L 50.303506,91.243181 L 50.005786,96.700351 L 49.802585,99.90807 L 54.920484,99.90807 L 54.717274,91.132217 L 55.421397,91.243181 L 55.619882,87.067076 L 54.816521,87.067076 L 54.518798,85.352258 L 54.017874,80.429702 L 54.216359,74.760706 L 55.31743,74.760706 L 55.31743,71.336105 L 53.913913,71.442015 L 54.117112,67.402096 L 55.747469,67.240708 L 55.823083,65.929374 L 56.749319,65.793192 L 57.699176,65.071956 L 51.985842,63.896802 L 46.31977,65.15265 L 46.872668,66.060507 L 47.47283,66.010066 L 48.172228,65.984848 L 48.299828,67.639144 L 49.878196,67.563497 L 49.906548,71.144447 L 43.111042,70.988097 L 43.337879,67.160002 L 43.559978,63.679927 L 43.559978,59.105378 L 43.763188,54.288748 L 57.373101,53.592733 L 73.567955,52.659674 L 73.71917,55.736265 L 73.142647,63.120082 L 72.892183,69.9945 L 66.928387,69.888585 L 66.900039,65.071956 L 69.106918,64.991267 L 69.206169,63.629486 L 70.108765,63.493308 L 70.061506,63.226006 L 70.964116,63.175568 L 71.465028,62.504773 L 64.721507,60.926122 L 58.001612,62.368592 L 58.4789,63.200785 L 59.230285,63.1453 L 59.230285,63.523577 L 60.156518,63.523577 L 60.156518,65.046738 L 62.136575,65.071956 L 62.112937,69.298485 L 60.109259,69.298485 L 60.080907,70.261816 L 60.785031,70.342507 L 60.70942,74.009202 L 62.188552,74.089909 L 62.013701,88.620507 L 60.057282,89.018952 L 60.080907,89.714967 L 60.761406,89.714967 L 60.761406,93.437137 L 61.886113,93.437137 L 61.588391,98.52109 L 61.210343,102.95945 L 68.331912,103.14605 L 68.105084,99.29275 L 67.580538,96.085028 L 67.476575,93.300955 L 73.520696,93.195041 L 73.345845,97.502272 L 73.317494,102.05159 L 76.729426,102.3189 L 81.3653,102.1323 L 82.820807,101.70358 L 82.017437,99.26753 L 81.818959,95.439438 L 81.440912,92.710853 L 87.206218,92.499027 L 86.955759,95.842931 L 86.932133,101.08826 L 89.238253,101.30009 L 91.520751,101.24965 L 92.621828,100.90165 L 91.969693,95.923633 L 91.747577,92.176239 L 92.725793,92.070324 L 92.749427,88.726422 L 93.02352,88.670945 L 92.976244,87.949712 L 91.846823,87.949712 L 91.619996,85.488427 L 91.520751,74.811143 L 92.371377,74.785924 L 92.371377,71.280616 L 92.725793,71.336105 L 92.725793,70.640088 L 91.468773,70.529127 L 91.497126,66.463987 L 93.600043,66.277382 L 93.477182,64.910557 L 94.403419,64.829863 L 94.351424,64.562549 L 95.580099,63.947229 L 89.337489,62.69138 L 82.995657,63.977495 L 83.39733,64.723951 L 84.375543,64.643256 L 84.427528,64.966046 L 85.254515,64.966046 L 85.301775,66.569901 L 87.357445,66.544681 L 87.532293,70.478688 L 80.264217,70.423216 L 79.413593,64.512124 L 78.733106,61.380041 L 78.184923,55.761484 L 78.510996,52.473053 L 92.999878,51.373557 L 93.047136,46.476221 L 93.774891,46.289613 L 93.727651,45.543159 L 93.174743,45.220372 C 93.174629,45.220372 85.252181,46.395266 82.745197,46.66284 C 82.0389,46.738209 82.09239,46.733258 81.516524,46.79397 L 81.440912,45.886118 L 78.444837,44.317564 L 78.482644,42.491786 L 79.512842,42.461518 L 79.588444,39.949808 C 79.588444,39.949808 85.728225,39.546834 88.009582,39.0117 C 90.290937,38.476559 93.524432,37.942456 93.524432,37.942456 L 95.055545,33.79662 L 98.089437,32.913987 L 98.339888,32.217972 L 105.20628,30.316548 L 105.98602,29.676006 L 103.37744,23.976741 L 103.62792,22.690624 L 104.95584,21.994611 L 105.91041,19.079404 L 105.45673,18.675923 z M 72.466874,40.403728 L 72.429067,42.476654 L 73.983813,42.542211 L 73.884576,44.509221 L 70.836515,46.506487 L 70.647496,47.081457 L 71.876167,47.091543 L 71.866712,47.575729 L 62.552432,48.029652 L 62.613863,46.652742 L 63.039175,45.966809 L 63.067524,45.528025 L 63.07698,44.579832 L 63.341609,43.949374 L 63.440849,43.439982 L 63.440849,43.076841 L 63.842533,41.47297 L 72.466874,40.403728 z M 52.987688,42.168984 L 52.760853,43.561027 L 53.488599,44.418431 L 53.441349,45.916386 L 54.117112,46.960408 L 53.942262,48.191039 L 54.443185,48.912273 L 44.939872,49.2855 L 44.916247,48.967759 L 46.017333,48.831579 L 46.069307,48.428097 L 43.66394,47.121797 L 43.536351,45.03375 L 44.689411,44.978276 L 44.788661,42.72883 L 52.987688,42.168984 z M 67.051262,74.276518 L 72.81657,74.649742 L 72.618099,82.411833 L 73.36947,88.776857 L 67.254465,88.565018 L 67.051262,74.276518 z M 28.44258,74.599304 L 37.671807,75.078442 L 36.868446,80.429702 L 36.868446,84.928593 L 37.520583,87.440302 L 28.494569,87.869006 L 28.44258,74.599304 z M 87.508658,74.649742 L 87.508658,87.924488 L 81.644113,88.353194 L 81.440912,81.342592 L 80.788764,74.811143 L 87.508658,74.649742 z M 43.087416,74.947312 L 49.906548,74.972531 L 49.977434,87.278902 L 43.611966,87.389863 L 43.285891,83.400379 L 43.262266,79.441156 L 43.087416,74.947312 z "
-       id="path4735" />
-  </g>
-</svg>
--- a/bundled/jinja2/docs/Makefile	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
-
-help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html      to make standalone HTML files"
-	@echo "  pickle    to make pickle files"
-	@echo "  json      to make JSON files"
-	@echo "  htmlhelp  to make HTML files and a HTML help project"
-	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  changes   to make an overview over all changed/added/deprecated items"
-	@echo "  linkcheck to check all external links for integrity"
-
-clean:
-	-rm -rf _build/*
-
-html:
-	mkdir -p _build/html _build/doctrees
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
-	@echo
-	@echo "Build finished. The HTML pages are in _build/html."
-
-pickle:
-	mkdir -p _build/pickle _build/doctrees
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files"
-
-json:
-	mkdir -p _build/json _build/doctrees
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json
-	@echo
-	@echo "Build finished; now you can process the json files"
-
-web: pickle
-
-htmlhelp:
-	mkdir -p _build/htmlhelp _build/doctrees
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in _build/htmlhelp."
-
-latex:
-	mkdir -p _build/latex _build/doctrees
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in _build/latex."
-	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
-	      "run these through (pdf)latex."
-
-changes:
-	mkdir -p _build/changes _build/doctrees
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
-	@echo
-	@echo "The overview file is in _build/changes."
-
-linkcheck:
-	mkdir -p _build/linkcheck _build/doctrees
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in _build/linkcheck/output.txt."
Binary file bundled/jinja2/docs/_static/darkmetal.png has changed
Binary file bundled/jinja2/docs/_static/headerbg.png has changed
Binary file bundled/jinja2/docs/_static/implementation.png has changed
--- a/bundled/jinja2/docs/_static/jinja.js	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-$(function() {
-
-  var
-    toc = $('#toc').show(),
-    items = $('#toc > ul').hide();
-
-  $('#toc h3')
-    .click(function() {
-      if (items.is(':visible')) {
-        items.animate({
-          height:     'hide',
-          opacity:    'hide'
-        }, 300, function() {
-          toc.removeClass('expandedtoc');
-        });
-      }
-      else {
-        items.animate({
-          height:     'show',
-          opacity:    'show'
-        }, 400);
-        toc.addClass('expandedtoc');
-      }
-    });
-
-});
Binary file bundled/jinja2/docs/_static/jinjabanner.png has changed
Binary file bundled/jinja2/docs/_static/metal.png has changed
Binary file bundled/jinja2/docs/_static/navigation.png has changed
Binary file bundled/jinja2/docs/_static/note.png has changed
--- a/bundled/jinja2/docs/_static/print.css	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-div.header, div.relnav, #toc { display: none; }
-#contentwrapper { padding: 0; margin: 0; border: none; }
-body { color: black; background-color: white; }
-div.footer { border-top: 1px solid #888; color: #888; margin-top: 1cm; }
-div.footer a { text-decoration: none; }
--- a/bundled/jinja2/docs/_static/style.css	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,390 +0,0 @@
-body {
-    background-color: #222;
-    margin: 0;
-    padding: 0;
-    font-family: 'Georgia', serif;
-    font-size: 15px;
-    color: #eee;
-}
-
-div.footer {
-    border-top: 1px solid #111;
-    padding: 8px;
-    font-size: 11px;
-    text-align: center;
-    letter-spacing: 0.5px;
-}
-
-div.footer a {
-    color: #eee;
-}
-
-div.header {
-    margin: 0 -15px 0 -15px;
-    background: url(headerbg.png) repeat-x;
-    border-top: 6px solid #D20000;
-}
-
-div.relnav {
-    border-bottom: 1px solid #111;
-    background: url(navigation.png);
-    margin: 0 -15px 0 -15px;
-    padding: 2px 20px 0 28px;
-    line-height: 25px;
-    color: #aaa;
-    font-size: 12px;
-    text-align: center;
-}
-
-div.relnav a {
-    color: #eee;
-    font-weight: bold;
-    text-decoration: none;
-}
-
-div.relnav a:hover {
-    text-decoration: underline;
-}
-
-#content {
-    background-color: white;
-    color: #111;
-    border-bottom: 1px solid black;
-    background: url(watermark.png) center 0;
-    padding: 0 15px 0 15px;
-    margin: 0;
-}
-
-h1 {
-    margin: 0;
-    padding: 15px 0 0 0;
-}
-
-h1.heading {
-    margin: 0;
-    padding: 0;
-    height: 80px;
-}
-
-h1.heading:hover {
-    background: #222;
-}
-
-h1.heading a {
-    background: url(jinjabanner.png) no-repeat center 0;
-    display: block;
-    width: 100%;
-    height: 80px;
-}
-
-h1.heading a:focus {
-    -moz-outline: none;
-    outline: none;
-}
-
-h1.heading span {
-    display: none;
-}
-
-#jinjalogo {
-    background-image: url(jinjalogo.png);
-    background-repeat: no-repeat;
-    width: 400px;
-    height: 160px;
-}
-
-#contentwrapper {
-    max-width: 680px;
-    padding: 0 18px 20px 18px;
-    margin: 0 auto 0 auto;
-    border-right: 1px solid #eee;
-    border-left: 1px solid #eee;
-    background: url(watermark_blur.png) center -114px;
-}
-
-#contentwrapper h2,
-#contentwrapper h2 a {
-    color: #222;
-    font-size: 24px;
-    margin: 20px 0 0 0;
-}
-
-#contentwrapper h3,
-#contentwrapper h3 a {
-    color: #b41717;
-    font-size: 20px;
-    margin: 20px 0 0 0;
-}
-
-table.docutils {
-    border-collapse: collapse;
-    border: 2px solid #aaa;
-    margin: 0.5em 1.5em 0.5em 1.5em;
-}
-
-table.docutils td {
-    padding: 2px;
-    border: 1px solid #ddd;
-}
-
-p, li, dd, dt, blockquote {
-    color: #333;
-}
-
-blockquote {
-    margin: 10px 0 10px 20px;
-}
-
-p {
-    line-height: 20px;
-    margin-bottom: 0;
-    margin-top: 10px;
-}
-
-hr {
-    border-top: 1px solid #ccc;
-    border-bottom: 0;
-    border-right: 0;
-    border-left: 0;
-    margin-bottom: 10px;
-    margin-top: 20px;
-}
-
-dl {
-    margin-left: 10px;
-}
-
-li, dt {
-    margin-top: 5px;
-}
-
-dt {
-    font-weight: bold;
-    color: #000;
-}
-
-dd {
-    margin-top: 10px;
-    line-height: 20px;
-}
-
-th {
-    text-align: left;
-    padding: 3px;
-    background-color: #f2f2f2;
-}
-
-a {
-    color: #b41717;
-}
-
-a:hover {
-    color: #444;
-}
-
-pre {
-    background: #ededed url(metal.png);
-    border-top: 1px solid #ccc;
-    border-bottom: 1px solid #ccc;
-    padding: 5px;
-    font-size: 13px;
-    font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
-}
-
-tt {
-    font-size: 13px;
-    font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
-    color: black;
-    padding: 1px 2px 1px 2px;
-    background-color: #fafafa;
-    border-bottom: 1px solid #eee;
-}
-
-a.reference:hover tt {
-    border-bottom-color: #aaa;
-}
-
-cite {
-    /* abusing <cite>, it's generated by ReST for `x` */
-    font-size: 13px;
-    font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
-    font-weight: bold;
-    font-style: normal;
-}
-
-div.admonition {
-    margin: 10px 0 10px 0;
-    padding: 10px 10px 10px 60px;
-    border: 1px solid #ccc;
-}
-
-div.admonition p.admonition-title {
-    background-color: #b41717;
-    color: white;
-    margin: -10px -10px 10px -60px;
-    padding: 4px 10px 4px 10px;
-    font-weight: bold;
-    font-size: 15px;
-}
-
-div.admonition p.admonition-title a {
-    color: white!important;
-}
-
-div.admonition-note {
-    background: url(note.png) no-repeat 10px 40px;
-}
-
-div.admonition-implementation {
-    background: url(implementation.png) no-repeat 10px 40px;
-}
-
-a.headerlink {
-    color: #B4B4B4!important;
-    font-size: 0.8em;
-    padding: 0 4px 0 4px;
-    text-decoration: none!important;
-    visibility: hidden;
-}
-
-h1:hover > a.headerlink,
-h2:hover > a.headerlink,
-h3:hover > a.headerlink,
-h4:hover > a.headerlink,
-h5:hover > a.headerlink,
-h6:hover > a.headerlink,
-dt:hover > a.headerlink,
-dt:hover > a.headerlink {
-    visibility: visible;
-}
-
-a.headerlink:hover {
-    background-color: #B4B4B4;
-    color: #F0F0F0!important;
-}
-
-table.indextable {
-    width: 100%;
-}
-
-table.indextable td {
-    vertical-align: top;
-    width: 50%;
-}
-
-table.indextable dl dd {
-    font-size: 11px;
-}
-
-table.indextable dl dd a {
-    color: #000;
-}
-
-dl.function dt,
-dl.class dt,
-dl.exception dt,
-dl.method dt,
-dl.attribute dt {
-    font-weight: normal;
-}
-
-dt .descname {
-    font-weight: bold;
-    margin-right: 4px;
-}
-
-dt .descname, dt .descclassname {
-    padding: 0;
-    background: transparent;
-    border-bottom: 1px solid #111;
-}
-
-dt .descclassname {
-    margin-left: 2px;
-}
-
-dl dt big {
-    font-size: 100%;
-}
-
-ul.search {
-    margin: 10px 0 0 30px;
-    padding: 0;
-}
-
-ul.search li {
-    margin: 10px 0 0 0;
-    padding: 0;
-}
-
-ul.search div.context {
-    font-size: 12px;
-    padding: 4px 0 0 20px;
-    color: #888;
-}
-
-span.highlight {
-    background-color: #eee;
-    border: 1px solid #ccc;
-}
-
-#toc {
-    margin: 0 -17px 0 -17px;
-    display: none;
-}
-
-#toc h3 {
-    float: right;
-    margin: 5px 5px 0 0;
-    padding: 0;
-    font-size: 12px;
-    color: #777;
-}
-
-#toc h3:hover {
-    color: #333;
-    cursor: pointer;
-}
-
-.expandedtoc {
-    background: #222 url(darkmetal.png);
-    border-bottom: 1px solid #111;
-    outline-bottom: 1px solid #000;
-    padding: 5px;
-}
-
-.expandedtoc h3 {
-    color: #aaa;
-    margin: 0!important;
-}
-
-.expandedtoc h3:hover {
-    color: white!important;
-}
-
-#tod h3:hover {
-    color: white;
-}
-
-#toc a {
-    color: #ddd;
-    text-decoration: none;
-}
-
-#toc a:hover {
-    color: white;
-    text-decoration: underline;
-}
-
-#toc ul {
-    margin: 5px 0 12px 17px;
-    padding: 0 7px 0 7px;
-}
-
-#toc ul ul {
-    margin-bottom: 0;
-}
-
-#toc ul li {
-    margin: 2px 0 0 0;
-}
Binary file bundled/jinja2/docs/_static/watermark.png has changed
Binary file bundled/jinja2/docs/_static/watermark_blur.png has changed
--- a/bundled/jinja2/docs/_templates/genindex.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-{% extends "layout.html" %}
-{% set title = 'Index' %}
-{% block body %}
-
-  <h1 id="index">Index</h1>
-
-  {% for key, dummy in genindexentries -%}
-  <a href="#{{ key }}"><strong>{{ key }}</strong></a> {% if not loop.last %}| {% endif %}
-  {%- endfor %}
-  <hr>
-
-  {% for key, entries in genindexentries %}
-    <h2 id="{{ key }}">{{ key }}</h2>
-    <table class="indextable"><tr>
-    {%- for column in entries|slice(2) if column %}
-      <td><dl>
-      {%- for entryname, (links, subitems) in column %}
-        <dt>{% if links %}<a href="{{ links[0] }}">{{ entryname|e }}</a>
-          {% for link in links[1:] %}, <a href="{{ link }}">[Link]</a>{% endfor %}
-          {%- else %}{{ entryname|e }}{% endif %}</dt>
-        {%- if subitems %}
-        <dd><dl>
-          {%- for subentryname, subentrylinks in subitems %}
-          <dt><a href="{{ subentrylinks[0] }}">{{ subentryname|e }}</a>
-          {%- for link in subentrylinks[1:] %}, <a href="{{ link }}">[Link]</a>{% endfor -%}
-          </dt>
-          {%- endfor %}
-        </dl></dd>
-        {%- endif -%}
-      {%- endfor %}
-      </dl></td>
-    {%- endfor %}
-    </tr></table>
-  {% endfor %}
-
-{% endblock %}
--- a/bundled/jinja2/docs/_templates/layout.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-  <head>
-    <title>Jinja2 Documentation</title>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-    <link rel="stylesheet" href="{{ pathto('_static/style.css', 1) }}" type="text/css">
-    <link rel="stylesheet" href="{{ pathto('_static/print.css', 1) }}" type="text/css" media="print">
-    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css">
-    {%- if builder != 'htmlhelp' %}
-    <script type="text/javascript">
-      var DOCUMENTATION_OPTIONS = {
-        URL_ROOT:   '{{ pathto("", 1) }}',
-        VERSION:    '{{ release }}'
-      };
-    </script>
-    <script type="text/javascript" src="{{ pathto('_static/jquery.js', 1) }}"></script>
-    <script type="text/javascript" src="{{ pathto('_static/interface.js', 1) }}"></script>
-    <script type="text/javascript" src="{{ pathto('_static/doctools.js', 1) }}"></script>
-    <script type="text/javascript" src="{{ pathto('_static/jinja.js', 1) }}"></script>
-    {%- endif %}
-    {%- if use_opensearch and builder != 'htmlhelp' %}
-    <link rel="search" type="application/opensearchdescription+xml"
-          title="Search within {{ docstitle }}"
-          href="{{ pathto('_static/opensearch.xml', 1) }}">
-    {%- endif %}
-    {%- if hasdoc('about') %}
-    <link rel="author" title="About these documents" href="{{ pathto('about') }}">
-    {%- endif %}
-    <link rel="contents" title="Global table of contents" href="{{ pathto('contents') }}">
-    <link rel="index" title="Global index" href="{{ pathto('genindex') }}">
-    <link rel="search" title="Search" href="{{ pathto('search') }}">
-    {%- if hasdoc('copyright') %}
-    <link rel="copyright" title="Copyright" href="{{ pathto('copyright') }}">
-    {%- endif %}
-    <link rel="top" title="{{ docstitle }}" href="{{ pathto('index') }}">
-    {%- if parents %}
-    <link rel="up" title="{{ parents[-1].title|striptags }}" href="{{ parents[-1].link|e }}">
-    {%- endif %}
-    {%- if next %}
-    <link rel="next" title="{{ next.title|striptags }}" href="{{ next.link|e }}">
-    {%- endif %}
-    {%- if prev %}
-    <link rel="prev" title="{{ prev.title|striptags }}" href="{{ prev.link|e }}">
-    {%- endif %}
-    {% block extrahead %}{% endblock %}
-  </head>
-  <body>
-    <div id="content">
-      <div class="header">
-        <h1 class="heading"><a href="{{ pathto('index') }}"
-          title="back to the documentation overview"><span>Jinja</span></a></h1>
-      </div>
-      <div class="relnav">
-        {%- if prev %}
-        <a href="{{ prev.link|e }}">&laquo; {{ prev.title }}</a> |
-        {%- endif %}
-        <a href="{{ pathto(current_page_name) if current_page_name else '#' }}">{{ title }}</a>
-        {%- if next %}
-        | <a href="{{ next.link|e }}">{{ next.title }} &raquo;</a>
-        {%- endif %}
-      </div>
-      <div id="contentwrapper">
-        {%- if display_toc %}
-        <div id="toc">
-          <h3>Table Of Contents</h3>
-          {{ toc }}
-        </div>
-        {%- endif %}
-        {% block body %}{% endblock %}
-      </div>
-    </div>
-    <div class="footer">
-      © Copyright 2008 by the <a href="http://pocoo.org/">Pocoo Team</a>,
-      documentation generated by <a href="http://sphinx.pocoo.org/">Sphinx</a>
-    </div>
-  </body>
-</html>
--- a/bundled/jinja2/docs/_templates/opensearch.xml	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
-  <ShortName>{{ project }}</ShortName>
-  <Description>Search {{ docstitle }}</Description>
-  <InputEncoding>utf-8</InputEncoding>
-  <Url type="text/html" method="get"
-       template="{{ use_opensearch }}/{{ pathto('search') }}?q={searchTerms}&amp;check_keywords=yes&amp;area=default"/>
-  <LongName>{{ docstitle }}</LongName>
-</OpenSearchDescription>
--- a/bundled/jinja2/docs/_templates/page.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-{% extends 'layout.html' %}
-{% block body %}
-  {{ body }}
-{% endblock %}
--- a/bundled/jinja2/docs/_templates/search.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-{% extends "layout.html" %}
-{% set title = 'Search' %}
-{% block extrahead %}
-    <script type="text/javascript" src="{{ pathto('_static/searchtools.js', 1) }}"></script>
-{% endblock %}
-{% block body %}
-  <h1 id="search-documentation">Search</h1>
-  <p>
-    From here you can search these documents. Enter your search
-    words into the box below and click "search". Note that the search
-    function will automatically search for all of the words. Pages
-    containing less words won't appear in the result list.
-  </p>
-  <form action="" method="get"><p>
-    <input type="text" name="q" value="">
-    <input type="submit" value="search">
-  </p></form>
-  {% if search_performed %}
-    <h2>Search Results</h2>
-    {% if not search_results %}
-      <p>Your search did not match any results.</p>
-    {% endif %}
-  {% endif %}
-  <div id="search-results">
-  {% if search_results %}
-    <ul>
-    {% for href, caption, context in search_results %}
-      <li><a href="{{ pathto(item.href) }}">{{ caption }}</a>
-        <div class="context">{{ context|e }}</div>
-      </li>
-    {% endfor %}
-    </ul>
-  {% endif %}
-  </div>
-{% endblock %}
--- a/bundled/jinja2/docs/api.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,684 +0,0 @@
-API
-===
-
-.. module:: jinja2
-    :synopsis: public Jinja2 API
-
-This document describes the API to Jinja2 and not the template language.  It
-will be most useful as reference to those implementing the template interface
-to the application and not those who are creating Jinja2 templates.
-
-Basics
-------
-
-Jinja2 uses a central object called the template :class:`Environment`.
-Instances of this class are used to store the configuration, global objects
-and are used to load templates from the file system or other locations.
-Even if you are creating templates from strings by using the constructor of
-:class:`Template` class, an environment is created automatically for you,
-albeit a shared one.
-
-Most applications will create one :class:`Environment` object on application
-initialization and use that to load templates.  In some cases it's however
-useful to have multiple environments side by side, if different configurations
-are in use.
-
-The simplest way to configure Jinja2 to load templates for your application
-looks roughly like this::
-
-    from jinja2 import Environment, PackageLoader
-    env = Environment(loader=PackageLoader('yourapplication', 'templates'))
-
-This will create a template environment with the default settings and a
-loader that looks up the templates in the `templates` folder inside the
-`yourapplication` python package.  Different loaders are available
-and you can also write your own if you want to load templates from a
-database or other resources.
-
-To load a template from this environment you just have to call the
-:meth:`get_template` method which then returns the loaded :class:`Template`::
-
-    template = env.get_template('mytemplate.html')
-
-To render it with some variables, just call the :meth:`render` method::
-
-    print template.render(the='variables', go='here')
-
-Using a template loader rather then passing strings to :class:`Template`
-or :meth:`Environment.from_string` has multiple advantages.  Besides being
-a lot easier to use it also enables template inheritance.
-
-
-Unicode
--------
-
-Jinja2 is using Unicode internally which means that you have to pass Unicode
-objects to the render function or bytestrings that only consist of ASCII
-characters.  Additionally newlines are normalized to one end of line
-sequence which is per default UNIX style (``\n``).
-
-Python 2.x supports two ways of representing string objects.  One is the
-`str` type and the other is the `unicode` type, both of which extend a type
-called `basestring`.  Unfortunately the default is `str` which should not
-be used to store text based information unless only ASCII characters are
-used.  With Python 2.6 it is possible to make `unicode` the default on a per
-module level and with Python 3 it will be the default.
-
-To explicitly use a Unicode string you have to prefix the string literal
-with a `u`: ``u'Hänsel und Gretel sagen Hallo'``.  That way Python will
-store the string as Unicode by decoding the string with the character
-encoding from the current Python module.  If no encoding is specified this
-defaults to 'ASCII' which means that you can't use any non ASCII identifier.
-
-To set a better module encoding add the following comment to the first or
-second line of the Python module using the Unicode literal::
-
-    # -*- coding: utf-8 -*-
-
-We recommend utf-8 as Encoding for Python modules and templates as it's
-possible to represent every Unicode character in utf-8 and because it's
-backwards compatible to ASCII.  For Jinja2 the default encoding of templates
-is assumed to be utf-8.
-
-It is not possible to use Jinja2 to process non-Unicode data.  The reason
-for this is that Jinja2 uses Unicode already on the language level.  For
-example Jinja2 treats the non-breaking space as valid whitespace inside
-expressions which requires knowledge of the encoding or operating on an
-Unicode string.
-
-For more details about Unicode in Python have a look at the excellent
-`Unicode documentation`_.
-
-Another important thing is how Jinja2 is handling string literals in
-templates.  A naive implementation would be using Unicode strings for
-all string literals but it turned out in the past that this is problematic
-as some libraries are typechecking against `str` explicitly.  For example
-`datetime.strftime` does not accept Unicode arguments.  To not break it
-completely Jinja2 is returning `str` for strings that fit into ASCII and
-for everything else `unicode`:
-
->>> m = Template(u"{% set a, b = 'foo', 'föö' %}").module
->>> m.a
-'foo'
->>> m.b
-u'f\xf6\xf6'
-
-
-.. _Unicode documentation: http://docs.python.org/dev/howto/unicode.html
-
-High Level API
---------------
-
-The high-level API is the API you will use in the application to load and
-render Jinja2 templates.  The :ref:`low-level-api` on the other side is only
-useful if you want to dig deeper into Jinja2 or :ref:`develop extensions
-<jinja-extensions>`.
-
-.. autoclass:: Environment([options])
-    :members: from_string, get_template, select_template,
-              get_or_select_template, join_path, extend, compile_expression
-
-    .. attribute:: shared
-
-        If a template was created by using the :class:`Template` constructor
-        an environment is created automatically.  These environments are
-        created as shared environments which means that multiple templates
-        may have the same anonymous environment.  For all shared environments
-        this attribute is `True`, else `False`.
-
-    .. attribute:: sandboxed
-
-        If the environment is sandboxed this attribute is `True`.  For the
-        sandbox mode have a look at the documentation for the
-        :class:`~jinja2.sandbox.SandboxedEnvironment`.
-
-    .. attribute:: filters
-
-        A dict of filters for this environment.  As long as no template was
-        loaded it's safe to add new filters or remove old.  For custom filters
-        see :ref:`writing-filters`.  For valid filter names have a look at
-        :ref:`identifier-naming`.
-
-    .. attribute:: tests
-
-        A dict of test functions for this environment.  As long as no
-        template was loaded it's safe to modify this dict.  For custom tests
-        see :ref:`writing-tests`.  For valid test names have a look at
-        :ref:`identifier-naming`.
-
-    .. attribute:: globals
-
-        A dict of global variables.  These variables are always available
-        in a template.  As long as no template was loaded it's safe
-        to modify this dict.  For more details see :ref:`global-namespace`.
-        For valid object names have a look at :ref:`identifier-naming`.
-
-    .. automethod:: overlay([options])
-
-    .. method:: undefined([hint, obj, name, exc])
-
-        Creates a new :class:`Undefined` object for `name`.  This is useful
-        for filters or functions that may return undefined objects for
-        some operations.  All parameters except of `hint` should be provided
-        as keyword parameters for better readability.  The `hint` is used as
-        error message for the exception if provided, otherwise the error
-        message will be generated from `obj` and `name` automatically.  The exception
-        provided as `exc` is raised if something with the generated undefined
-        object is done that the undefined object does not allow.  The default
-        exception is :exc:`UndefinedError`.  If a `hint` is provided the
-        `name` may be ommited.
-
-        The most common way to create an undefined object is by providing
-        a name only::
-
-            return environment.undefined(name='some_name')
-
-        This means that the name `some_name` is not defined.  If the name
-        was from an attribute of an object it makes sense to tell the
-        undefined object the holder object to improve the error message::
-
-            if not hasattr(obj, 'attr'):
-                return environment.undefined(obj=obj, name='attr')
-
-        For a more complex example you can provide a hint.  For example
-        the :func:`first` filter creates an undefined object that way::
-
-            return environment.undefined('no first item, sequence was empty')            
-
-        If it the `name` or `obj` is known (for example because an attribute
-        was accessed) it shold be passed to the undefined object, even if
-        a custom `hint` is provided.  This gives undefined objects the
-        possibility to enhance the error message.
-
-.. autoclass:: Template
-    :members: module, make_module
-
-    .. attribute:: globals
-
-        The dict with the globals of that template.  It's unsafe to modify
-        this dict as it may be shared with other templates or the environment
-        that loaded the template.
-
-    .. attribute:: name
-
-        The loading name of the template.  If the template was loaded from a
-        string this is `None`.
-
-    .. attribute:: filename
-
-        The filename of the template on the file system if it was loaded from
-        there.  Otherwise this is `None`.
-
-    .. automethod:: render([context])
-
-    .. automethod:: generate([context])
-
-    .. automethod:: stream([context])
-
-
-.. autoclass:: jinja2.environment.TemplateStream()
-    :members: disable_buffering, enable_buffering, dump
-
-
-.. _identifier-naming:
-
-Notes on Identifiers
---------------------
-
-Jinja2 uses the regular Python 2.x naming rules.  Valid identifiers have to
-match ``[a-zA-Z_][a-zA-Z0-9_]*``.  As a matter of fact non ASCII characters
-are currently not allowed.  This limitation will probably go away as soon as
-unicode identifiers are fully specified for Python 3.
-
-Filters and tests are looked up in separate namespaces and have slightly
-modified identifier syntax.  Filters and tests may contain dots to group
-filters and tests by topic.  For example it's perfectly valid to add a
-function into the filter dict and call it `to.unicode`.  The regular
-expression for filter and test identifiers is
-``[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*```.
-
-
-Undefined Types
----------------
-
-These classes can be used as undefined types.  The :class:`Environment`
-constructor takes an `undefined` parameter that can be one of those classes
-or a custom subclass of :class:`Undefined`.  Whenever the template engine is
-unable to look up a name or access an attribute one of those objects is
-created and returned.  Some operations on undefined values are then allowed,
-others fail.
-
-The closest to regular Python behavior is the `StrictUndefined` which
-disallows all operations beside testing if it's an undefined object.
-
-.. autoclass:: jinja2.Undefined()
-
-    .. attribute:: _undefined_hint
-
-        Either `None` or an unicode string with the error message for
-        the undefined object.
-
-    .. attribute:: _undefined_obj
-
-        Either `None` or the owner object that caused the undefined object
-        to be created (for example because an attribute does not exist).
-
-    .. attribute:: _undefined_name
-
-        The name for the undefined variable / attribute or just `None`
-        if no such information exists.
-
-    .. attribute:: _undefined_exception
-
-        The exception that the undefined object wants to raise.  This
-        is usually one of :exc:`UndefinedError` or :exc:`SecurityError`.
-
-    .. method:: _fail_with_undefined_error(\*args, \**kwargs)
-
-        When called with any arguments this method raises
-        :attr:`_undefined_exception` with an error message generated
-        from the undefined hints stored on the undefined object.
-
-.. autoclass:: jinja2.DebugUndefined()
-
-.. autoclass:: jinja2.StrictUndefined()
-
-Undefined objects are created by calling :attr:`undefined`.
-
-.. admonition:: Implementation
-
-    :class:`Undefined` objects are implemented by overriding the special
-    `__underscore__` methods.  For example the default :class:`Undefined`
-    class implements `__unicode__` in a way that it returns an empty
-    string, however `__int__` and others still fail with an exception.  To
-    allow conversion to int by returning ``0`` you can implement your own::
-
-        class NullUndefined(Undefined):
-            def __int__(self):
-                return 0
-            def __float__(self):
-                return 0.0
-
-    To disallow a method, just override it and raise
-    :attr:`~Undefined._undefined_exception`.  Because this is a very common
-    idom in undefined objects there is the helper method
-    :meth:`~Undefined._fail_with_undefined_error` that does the error raising
-    automatically.  Here a class that works like the regular :class:`Undefined`
-    but chokes on iteration::
-
-        class NonIterableUndefined(Undefined):
-            __iter__ = Undefined._fail_with_undefined_error
-
-
-The Context
------------
-
-.. autoclass:: jinja2.runtime.Context()
-    :members: resolve, get_exported, get_all
-
-    .. attribute:: parent
-
-        A dict of read only, global variables the template looks up.  These
-        can either come from another :class:`Context`, from the
-        :attr:`Environment.globals` or :attr:`Template.globals` or points
-        to a dict created by combining the globals with the variables
-        passed to the render function.  It must not be altered.
-
-    .. attribute:: vars
-
-        The template local variables.  This list contains environment and
-        context functions from the :attr:`parent` scope as well as local
-        modifications and exported variables from the template.  The template
-        will modify this dict during template evaluation but filters and
-        context functions are not allowed to modify it.
-
-    .. attribute:: environment
-
-        The environment that loaded the template.
-
-    .. attribute:: exported_vars
-
-        This set contains all the names the template exports.  The values for
-        the names are in the :attr:`vars` dict.  In order to get a copy of the
-        exported variables as dict, :meth:`get_exported` can be used.
-
-    .. attribute:: name
-
-        The load name of the template owning this context.
-
-    .. attribute:: blocks
-
-        A dict with the current mapping of blocks in the template.  The keys
-        in this dict are the names of the blocks, and the values a list of
-        blocks registered.  The last item in each list is the current active
-        block (latest in the inheritance chain).
-
-    .. automethod:: jinja2.runtime.Context.call(callable, \*args, \**kwargs)
-
-
-.. admonition:: Implementation
-
-    Context is immutable for the same reason Python's frame locals are
-    immutable inside functions.  Both Jinja2 and Python are not using the
-    context / frame locals as data storage for variables but only as primary
-    data source.
-
-    When a template accesses a variable the template does not define, Jinja2
-    looks up the variable in the context, after that the variable is treated
-    as if it was defined in the template.
-
-
-.. _loaders:
-
-Loaders
--------
-
-Loaders are responsible for loading templates from a resource such as the
-file system.  The environment will keep the compiled modules in memory like
-Python's `sys.modules`.  Unlike `sys.modules` however this cache is limited in
-size by default and templates are automatically reloaded.
-All loaders are subclasses of :class:`BaseLoader`.  If you want to create your
-own loader, subclass :class:`BaseLoader` and override `get_source`.
-
-.. autoclass:: jinja2.BaseLoader
-    :members: get_source, load
-
-Here a list of the builtin loaders Jinja2 provides:
-
-.. autoclass:: jinja2.FileSystemLoader
-
-.. autoclass:: jinja2.PackageLoader
-
-.. autoclass:: jinja2.DictLoader
-
-.. autoclass:: jinja2.FunctionLoader
-
-.. autoclass:: jinja2.PrefixLoader
-
-.. autoclass:: jinja2.ChoiceLoader
-
-
-.. _bytecode-cache:
-
-Bytecode Cache
---------------
-
-Jinja 2.1 and higher support external bytecode caching.  Bytecode caches make
-it possible to store the generated bytecode on the file system or a different
-location to avoid parsing the templates on first use.
-
-This is especially useful if you have a web application that is initialized on
-the first request and Jinja compiles many templates at once which slows down
-the application.
-
-To use a bytecode cache, instanciate it and pass it to the :class:`Environment`.
-
-.. autoclass:: jinja2.BytecodeCache
-    :members: load_bytecode, dump_bytecode, clear
-
-.. autoclass:: jinja2.bccache.Bucket
-    :members: write_bytecode, load_bytecode, bytecode_from_string,
-              bytecode_to_string, reset
-
-    .. attribute:: environment
-
-        The :class:`Environment` that created the bucket.
-
-    .. attribute:: key
-
-        The unique cache key for this bucket
-
-    .. attribute:: code
-
-        The bytecode if it's loaded, otherwise `None`.
-
-
-Builtin bytecode caches:
-
-.. autoclass:: jinja2.FileSystemBytecodeCache
-
-.. autoclass:: jinja2.MemcachedBytecodeCache
-
-
-Utilities
----------
-
-These helper functions and classes are useful if you add custom filters or
-functions to a Jinja2 environment.
-
-.. autofunction:: jinja2.environmentfilter
-
-.. autofunction:: jinja2.contextfilter
-
-.. autofunction:: jinja2.environmentfunction
-
-.. autofunction:: jinja2.contextfunction
-
-.. function:: escape(s)
-
-    Convert the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in string `s`
-    to HTML-safe sequences.  Use this if you need to display text that might
-    contain such characters in HTML.  This function will not escaped objects
-    that do have an HTML representation such as already escaped data.
-
-    The return value is a :class:`Markup` string.
-
-.. autofunction:: jinja2.clear_caches
-
-.. autofunction:: jinja2.is_undefined
-
-.. autoclass:: jinja2.Markup([string])
-    :members: escape, unescape, striptags
-
-.. admonition:: Note
-
-    The Jinja2 :class:`Markup` class is compatible with at least Pylons and
-    Genshi.  It's expected that more template engines and framework will pick
-    up the `__html__` concept soon.
-
-
-Exceptions
-----------
-
-.. autoexception:: jinja2.TemplateError
-
-.. autoexception:: jinja2.UndefinedError
-
-.. autoexception:: jinja2.TemplateNotFound
-
-.. autoexception:: jinja2.TemplatesNotFound
-
-.. autoexception:: jinja2.TemplateSyntaxError
-
-    .. attribute:: message
-
-        The error message as utf-8 bytestring.
-
-    .. attribute:: lineno
-
-        The line number where the error occurred
-
-    .. attribute:: name
-
-        The load name for the template as unicode string.
-
-    .. attribute:: filename
-
-        The filename that loaded the template as bytestring in the encoding
-        of the file system (most likely utf-8 or mbcs on Windows systems).
-
-    The reason why the filename and error message are bytestrings and not
-    unicode strings is that Python 2.x is not using unicode for exceptions
-    and tracebacks as well as the compiler.  This will change with Python 3.
-
-.. autoexception:: jinja2.TemplateAssertionError
-
-
-.. _writing-filters:
-
-Custom Filters
---------------
-
-Custom filters are just regular Python functions that take the left side of
-the filter as first argument and the the arguments passed to the filter as
-extra arguments or keyword arguments.
-
-For example in the filter ``{{ 42|myfilter(23) }}`` the function would be
-called with ``myfilter(42, 23)``.  Here for example a simple filter that can
-be applied to datetime objects to format them::
-
-    def datetimeformat(value, format='%H:%M / %d-%m-%Y'):
-        return value.strftime(format)
-
-You can register it on the template environment by updating the
-:attr:`~Environment.filters` dict on the environment::
-
-    environment.filters['datetimeformat'] = datetimeformat
-
-Inside the template it can then be used as follows:
-
-.. sourcecode:: jinja
-
-    written on: {{ article.pub_date|datetimeformat }}
-    publication date: {{ article.pub_date|datetimeformat('%d-%m-%Y') }}
-
-Filters can also be passed the current template context or environment.  This
-is useful if a filter wants to return an undefined value or check the current
-:attr:`~Environment.autoescape` setting.  For this purpose two decorators
-exist: :func:`environmentfilter` and :func:`contextfilter`.
-
-Here a small example filter that breaks a text into HTML line breaks and
-paragraphs and marks the return value as safe HTML string if autoescaping is
-enabled::
-
-    import re
-    from jinja2 import environmentfilter, Markup, escape
-
-    _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
-
-    @environmentfilter
-    def nl2br(environment, value):
-        result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n')
-                              for p in _paragraph_re.split(escape(value)))
-        if environment.autoescape:
-            result = Markup(result)
-        return result
-
-Context filters work the same just that the first argument is the current
-active :class:`Context` rather then the environment.
-
-
-.. _writing-tests:
-
-Custom Tests
-------------
-
-Tests work like filters just that there is no way for a test to get access
-to the environment or context and that they can't be chained.  The return
-value of a test should be `True` or `False`.  The purpose of a test is to
-give the template designers the possibility to perform type and conformability
-checks.
-
-Here a simple test that checks if a variable is a prime number::
-
-    import math
-
-    def is_prime(n):
-        if n == 2:
-            return True
-        for i in xrange(2, int(math.ceil(math.sqrt(n))) + 1):
-            if n % i == 0:
-                return False
-        return True
-        
-
-You can register it on the template environment by updating the
-:attr:`~Environment.tests` dict on the environment::
-
-    environment.tests['prime'] = is_prime
-
-A template designer can then use the test like this:
-
-.. sourcecode:: jinja
-
-    {% if 42 is prime %}
-        42 is a prime number
-    {% else %}
-        42 is not a prime number
-    {% endif %}
-
-
-.. _global-namespace:
-
-The Global Namespace
---------------------
-
-Variables stored in the :attr:`Environment.globals` dict are special as they
-are available for imported templates too, even if they are imported without
-context.  This is the place where you can put variables and functions
-that should be available all the time.  Additionally :attr:`Template.globals`
-exist that are variables available to a specific template that are available
-to all :meth:`~Template.render` calls.
-
-
-.. _low-level-api:
-
-Low Level API
--------------
-
-The low level API exposes functionality that can be useful to understand some
-implementation details, debugging purposes or advanced :ref:`extension
-<jinja-extensions>` techniques.  Unless you know exactly what you are doing we
-don't recommend using any of those.
-
-.. automethod:: Environment.lex
-
-.. automethod:: Environment.parse
-
-.. automethod:: Environment.preprocess
-
-.. automethod:: Template.new_context
-
-.. method:: Template.root_render_func(context)
-
-    This is the low level render function.  It's passed a :class:`Context`
-    that has to be created by :meth:`new_context` of the same template or
-    a compatible template.  This render function is generated by the
-    compiler from the template code and returns a generator that yields
-    unicode strings.
-
-    If an exception in the template code happens the template engine will
-    not rewrite the exception but pass through the original one.  As a
-    matter of fact this function should only be called from within a
-    :meth:`render` / :meth:`generate` / :meth:`stream` call.
-
-.. attribute:: Template.blocks
-
-    A dict of block render functions.  Each of these functions works exactly
-    like the :meth:`root_render_func` with the same limitations.
-
-.. attribute:: Template.is_up_to_date
-
-    This attribute is `False` if there is a newer version of the template
-    available, otherwise `True`.
-
-.. admonition:: Note
-
-    The low-level API is fragile.  Future Jinja2 versions will try not to
-    change it in a backwards incompatible way but modifications in the Jinja2
-    core may shine through.  For example if Jinja2 introduces a new AST node
-    in later versions that may be returned by :meth:`~Environment.parse`.
-
-The Meta API
-------------
-
-.. versionadded:: 2.2
-
-The meta API returns some information about abstract syntax trees that
-could help applications to implement more advanced template concepts.  All
-the functions of the meta API operate on an abstract syntax tree as
-returned by the :meth:`Environment.parse` method.
-
-.. autofunction:: jinja2.meta.find_undeclared_variables
-
-.. autofunction:: jinja2.meta.find_referenced_templates
--- a/bundled/jinja2/docs/cache_extension.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-from jinja2 import nodes
-from jinja2.ext import Extension
-
-
-class FragmentCacheExtension(Extension):
-    # a set of names that trigger the extension.
-    tags = set(['cache'])
-
-    def __init__(self, environment):
-        super(FragmentCacheExtension, self).__init__(environment)
-
-        # add the defaults to the environment
-        environment.extend(
-            fragment_cache_prefix='',
-            fragment_cache=None
-        )
-
-    def parse(self, parser):
-        # the first token is the token that started the tag.  In our case
-        # we only listen to ``'cache'`` so this will be a name token with
-        # `cache` as value.  We get the line number so that we can give
-        # that line number to the nodes we create by hand.
-        lineno = parser.stream.next().lineno
-
-        # now we parse a single expression that is used as cache key.
-        args = [parser.parse_expression()]
-
-        # if there is a comma, the user provided a timeout.  If not use
-        # None as second parameter.
-        if parser.stream.skip_if('comma'):
-            args.append(parser.parse_expression())
-        else:
-            args.append(nodes.Const(None))
-
-        # now we parse the body of the cache block up to `endcache` and
-        # drop the needle (which would always be `endcache` in that case)
-        body = parser.parse_statements(['name:endcache'], drop_needle=True)
-
-        # now return a `CallBlock` node that calls our _cache_support
-        # helper method on this extension.
-        return nodes.CallBlock(self.call_method('_cache_support', args),
-                               [], [], body).set_lineno(lineno)
-
-    def _cache_support(self, name, timeout, caller):
-        """Helper callback."""
-        key = self.environment.fragment_cache_prefix + name
-
-        # try to load the block from the cache
-        # if there is no fragment in the cache, render it and store
-        # it in the cache.
-        rv = self.environment.fragment_cache.get(key)
-        if rv is not None:
-            return rv
-        rv = caller()
-        self.environment.fragment_cache.add(key, rv, timeout)
-        return rv
--- a/bundled/jinja2/docs/changelog.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-.. module:: jinja2
-
-.. include:: ../CHANGES
--- a/bundled/jinja2/docs/conf.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Jinja2 documentation build configuration file, created by
-# sphinx-quickstart on Sun Apr 27 21:42:41 2008.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# The contents of this file are pickled, so don't put values in the namespace
-# that aren't pickleable (module imports are okay, they're removed automatically).
-#
-# All configuration values have a default value; values that are commented out
-# serve to show the default value.
-
-import sys, os
-
-# If your extensions are in another directory, add it here. If the directory
-# is relative to the documentation root, use os.path.abspath to make it
-# absolute, like shown here.
-sys.path.append(os.path.dirname(os.path.abspath(__file__)))
-
-# General configuration
-# ---------------------
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'jinjaext']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General substitutions.
-project = 'Jinja2'
-copyright = '2008, Armin Ronacher'
-
-# The default replacements for |version| and |release|, also used in various
-# other places throughout the built documents.
-#
-# The short X.Y version.
-version = '2.0'
-# The full version, including alpha/beta/rc tags.
-release = '2.0'
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-today_fmt = '%B %d, %Y'
-
-# List of documents that shouldn't be included in the build.
-#unused_docs = []
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'jinjaext.JinjaStyle'
-
-
-# Options for HTML output
-# -----------------------
-
-# The style sheet to use for HTML and HTML Help pages. A file of that name
-# must exist either in Sphinx' static/ path, or in one of the custom paths
-# given in html_static_path.
-html_style = 'style.css'
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# no modindex
-html_use_modindex = False
-
-# If true, the reST sources are included in the HTML build as _sources/<name>.
-#html_copy_source = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.
-#html_use_opensearch = False
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Jinja2doc'
-
-
-# Options for LaTeX output
-# ------------------------
-
-# The paper size ('letter' or 'a4').
-latex_paper_size = 'a4'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, document class [howto/manual]).
-latex_documents = [
-  ('index', 'Jinja2.tex', 'Jinja2 Documentation', 'Armin Ronacher', 'manual', 'toctree_only'),
-]
-
-# Additional stuff for the LaTeX preamble.
-latex_preamble = '''
-\usepackage{palatino}
-\definecolor{TitleColor}{rgb}{0.7,0,0}
-\definecolor{InnerLinkColor}{rgb}{0.7,0,0}
-\definecolor{OuterLinkColor}{rgb}{0.8,0,0}
-\definecolor{VerbatimColor}{rgb}{0.985,0.985,0.985}
-\definecolor{VerbatimBorderColor}{rgb}{0.8,0.8,0.8}
-'''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-latex_use_modindex = False
--- a/bundled/jinja2/docs/extensions.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,268 +0,0 @@
-.. _jinja-extensions:
-
-Extensions
-==========
-
-Jinja2 supports extensions that can add extra filters, tests, globals or even
-extend the parser.  The main motivation of extensions is it to move often used
-code into a reusable class like adding support for internationalization.
-
-
-Adding Extensions
------------------
-
-Extensions are added to the Jinja2 environment at creation time.  Once the
-environment is created additional extensions cannot be added.  To add an
-extension pass a list of extension classes or import paths to the
-`environment` parameter of the :class:`Environment` constructor.  The following
-example creates a Jinja2 environment with the i18n extension loaded::
-
-    jinja_env = Environment(extensions=['jinja2.ext.i18n'])
-
-
-.. _i18n-extension:
-
-i18n Extension
---------------
-
-**Import name:** `jinja2.ext.i18n`
-
-Jinja2 currently comes with one extension, the i18n extension.  It can be
-used in combination with `gettext`_ or `babel`_.  If the i18n extension is
-enabled Jinja2 provides a `trans` statement that marks the wrapped string as
-translatable and calls `gettext`.
-
-After enabling dummy `_` function that forwards calls to `gettext` is added
-to the environment globals.  An internationalized application then has to
-provide at least an `gettext` and optoinally a `ngettext` function into the
-namespace.  Either globally or for each rendering.
-
-After enabling of the extension the environment provides the following
-additional methods:
-
-.. method:: jinja2.Environment.install_gettext_translations(translations)
-
-    Installs a translation globally for that environment.  The tranlations
-    object provided must implement at least `ugettext` and `ungettext`.
-    The `gettext.NullTranslations` and `gettext.GNUTranslations` classes
-    as well as `Babel`_\s `Translations` class are supported.
-
-.. method:: jinja2.Environment.install_null_translations()
-
-    Install dummy gettext functions.  This is useful if you want to prepare
-    the application for internationalization but don't want to implement the
-    full internationalization system yet.
-
-.. method:: jinja2.Environment.uninstall_gettext_translations()
-
-    Uninstall the translations again.
-
-.. method:: jinja2.Environment.extract_translations(source)
-
-    Extract localizable strings from the given template node or source.
-
-    For every string found this function yields a ``(lineno, function,
-    message)`` tuple, where:
-
-    * `lineno` is the number of the line on which the string was found,
-    * `function` is the name of the `gettext` function used (if the
-      string was extracted from embedded Python code), and
-    *  `message` is the string itself (a `unicode` object, or a tuple
-       of `unicode` objects for functions with multiple string arguments).
-
-    If `Babel`_ is installed :ref:`the babel integration <babel-integration>`
-    can be used to extract strings for babel.
-
-For a web application that is available in multiple languages but gives all
-the users the same language (for example a multilingual forum software
-installed for a French community) may load the translations once and add the
-translation methods to the environment at environment generation time::
-
-    translations = get_gettext_translations()
-    env = Environment(extensions=['jinja2.ext.i18n'])
-    env.install_gettext_translations(translations)
-
-The `get_gettext_translations` function would return the translator for the
-current configuration.  (For example by using `gettext.find`)
-
-The usage of the `i18n` extension for template designers is covered as part
-:ref:`of the template documentation <i18n-in-templates>`.
-
-.. _gettext: http://docs.python.org/dev/library/gettext
-.. _Babel: http://babel.edgewall.org/
-
-
-Expression Statement
---------------------
-
-**Import name:** `jinja2.ext.do`
-
-The "do" aka expression-statement extension adds a simple `do` tag to the
-template engine that works like a variable expression but ignores the
-return value.
-
-.. _loopcontrols-extension:
-
-Loop Controls
--------------
-
-**Import name:** `jinja2.ext.loopcontrols`
-
-This extension adds support for `break` and `continue` in loops.  After
-enabling Jinja2 provides those two keywords which work exactly like in
-Python.
-
-.. _with-extension:
-
-With Statement
---------------
-
-**Import name:** `jinja2.ext.with_`
-
-.. versionadded:: 2.3
-
-This extension adds support for the with keyword.  Using this keyword it
-is possible to enforce a nested scope in a template.  Variables can be
-declared directly in the opening block of the with statement or using a
-standard `set` statement directly within.
-
-
-.. _writing-extensions:
-
-Writing Extensions
-------------------
-
-.. module:: jinja2.ext
-
-By writing extensions you can add custom tags to Jinja2.  This is a non trival
-task and usually not needed as the default tags and expressions cover all
-common use cases.  The i18n extension is a good example of why extensions are
-useful, another one would be fragment caching.
-
-When writing extensions you have to keep in mind that you are working with the
-Jinja2 template compiler which does not validate the node tree you are possing
-to it.  If the AST is malformed you will get all kinds of compiler or runtime
-errors that are horrible to debug.  Always make sure you are using the nodes
-you create correctly.  The API documentation below shows which nodes exist and
-how to use them.
-
-Example Extension
-~~~~~~~~~~~~~~~~~
-
-The following example implements a `cache` tag for Jinja2 by using the
-`Werkzeug`_ caching contrib module:
-
-.. literalinclude:: cache_extension.py
-    :language: python
-
-And here is how you use it in an environment::
-
-    from jinja2 import Environment
-    from werkzeug.contrib.cache import SimpleCache
-
-    env = Environment(extensions=[FragmentCacheExtension])
-    env.fragment_cache = SimpleCache()
-
-Inside the template it's then possible to mark blocks as cacheable.  The
-following example caches a sidebar for 300 seconds:
-
-.. sourcecode:: html+jinja
-
-    {% cache 'sidebar', 300 %}
-    <div class="sidebar">
-        ...
-    </div>
-    {% endcache %}
-
-.. _Werkzeug: http://werkzeug.pocoo.org/
-
-Extension API
-~~~~~~~~~~~~~
-
-Extensions always have to extend the :class:`jinja2.ext.Extension` class:
-
-.. autoclass:: Extension
-    :members: preprocess, filter_stream, parse, attr, call_method
-
-    .. attribute:: identifier
-
-        The identifier of the extension.  This is always the true import name
-        of the extension class and must not be changed.
-
-    .. attribute:: tags
-
-        If the extension implements custom tags this is a set of tag names
-        the extension is listening for.
-
-Parser API
-~~~~~~~~~~
-
-The parser passed to :meth:`Extension.parse` provides ways to parse
-expressions of different types.  The following methods may be used by
-extensions:
-
-.. autoclass:: jinja2.parser.Parser
-    :members: parse_expression, parse_tuple, parse_assign_target,
-              parse_statements, free_identifier, fail
-
-    .. attribute:: filename
-
-        The filename of the template the parser processes.  This is **not**
-        the load name of the template.  For the load name see :attr:`name`.
-        For templates that were not loaded form the file system this is
-        `None`.
-
-    .. attribute:: name
-
-        The load name of the template.
-
-    .. attribute:: stream
-
-        The current :class:`~jinja2.lexer.TokenStream`
-
-.. autoclass:: jinja2.lexer.TokenStream
-   :members: push, look, eos, skip, next, next_if, skip_if, expect
-
-   .. attribute:: current
-
-        The current :class:`~jinja2.lexer.Token`.
-
-.. autoclass:: jinja2.lexer.Token
-    :members: test, test_any
-
-    .. attribute:: lineno
-
-        The line number of the token
-
-    .. attribute:: type
-
-        The type of the token.  This string is interned so you may compare
-        it with arbitrary strings using the `is` operator.
-
-    .. attribute:: value
-
-        The value of the token.
-
-There is also a utility function in the lexer module that can count newline
-characters in strings:
-
-.. autofunction:: jinja2.lexer.count_newlines
-
-AST
-~~~
-
-The AST (Abstract Syntax Tree) is used to represent a template after parsing.
-It's build of nodes that the compiler then converts into executable Python
-code objects.  Extensions that provide custom statements can return nodes to
-execute custom Python code.
-
-The list below describes all nodes that are currently available.  The AST may
-change between Jinja2 versions but will stay backwards compatible.
-
-For more information have a look at the repr of :meth:`jinja2.Environment.parse`.
-
-.. module:: jinja2.nodes
-
-.. jinjanodes::
-
-.. autoexception:: Impossible
--- a/bundled/jinja2/docs/faq.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-Frequently Asked Questions
-==========================
-
-This page answers some of the often asked questions about Jinja.
-
-.. highlight:: html+jinja
-
-Why is it called Jinja?
------------------------
-
-The name Jinja was chosen because it's the name of a Japanese temple and
-temple and template share a similar pronunciation.  It is not named after
-the capital city of Uganda.
-
-How fast is it?
----------------
-
-We really hate benchmarks especially since they don't reflect much.  The
-performance of a template depends on many factors and you would have to
-benchmark different engines in different situations.  The benchmarks from the
-testsuite show that Jinja2 has a similar performance to `Mako`_ and is between
-10 and 20 times faster than Django's template engine or Genshi.  These numbers
-should be taken with tons of salt as the benchmarks that took these numbers
-only test a few performance related situations such as looping.  Generally
-speaking the performance of a template engine doesn't matter much as the
-usual bottleneck in a web application is either the database or the application
-code.
-
-.. _Mako: http://www.makotemplates.org/
-
-How Compatible is Jinja2 with Django?
--------------------------------------
-
-The default syntax of Jinja2 matches Django syntax in many ways.  However
-this similarity doesn't mean that you can use a Django template unmodified
-in Jinja2.  For example filter arguments use a function call syntax rather
-than a colon to separate filter name and arguments.  Additionally the
-extension interface in Jinja is fundamentally different from the Django one
-which means that your custom tags won't work any longer.
-
-Generally speaking you will use much less custom extensions as the Jinja
-template system allows you to use a certain subset of Python expressions
-which can replace most Django extensions.  For example instead of using
-something like this::
-
-    {% load comments %}
-    {% get_latest_comments 10 as latest_comments %}
-    {% for comment in latest_comments %}
-        ...
-    {% endfor %}
-
-You will most likely provide an object with attributes to retrieve
-comments from the database::
-
-    {% for comment in models.comments.latest(10) %}
-        ...
-    {% endfor %}
-
-Or directly provide the model for quick testing::
-
-    {% for comment in Comment.objects.order_by('-pub_date')[:10] %}
-        ...
-    {% endfor %}
-
-Please keep in mind that even though you may put such things into templates
-it still isn't a good idea.  Queries should go into the view code and not
-the template!
-
-Isn't it a terrible idea to put Logic into Templates?
------------------------------------------------------
-
-Without a doubt you should try to remove as much logic from templates as
-possible.  But templates without any logic mean that you have to do all
-the processing in the code which is boring and stupid.  A template engine
-that does that is shipped with Python and called `string.Template`.  Comes
-without loops and if conditions and is by far the fastest template engine
-you can get for Python.
-
-So some amount of logic is required in templates to keep everyone happy.
-And Jinja leaves it pretty much to you how much logic you want to put into
-templates.  There are some restrictions in what you can do and what not.
-
-Jinja2 neither allows you to put arbitrary Python code into templates nor
-does it allow all Python expressions.  The operators are limited to the
-most common ones and more advanced expressions such as list comprehensions
-and generator expressions are not supported.  This keeps the template engine
-easier to maintain and templates more readable.
-
-Why is Autoescaping not the Default?
-------------------------------------
-
-There are multiple reasons why automatic escaping is not the default mode
-and also not the recommended one.  While automatic escaping of variables
-means that you will less likely have an XSS problem it also causes a huge
-amount of extra processing in the template engine which can cause serious
-performance problems.  As Python doesn't provide a way to mark strings as
-unsafe Jinja has to hack around that limitation by providing a custom
-string class (the :class:`Markup` string) that safely interacts with safe
-and unsafe strings.
-
-With explicit escaping however the template engine doesn't have to perform
-any safety checks on variables.  Also a human knows not to escape integers
-or strings that may never contain characters one has to escape or already
-HTML markup.  For example when iterating over a list over a table of
-integers and floats for a table of statistics the template designer can
-omit the escaping because he knows that integers or floats don't contain
-any unsafe parameters.
-
-Additionally Jinja2 is a general purpose template engine and not only used
-for HTML/XML generation.  For example you may generate LaTeX, emails,
-CSS, JavaScript, or configuration files.
-
-Why is the Context immutable?
------------------------------
-
-When writing a :func:`contextfunction` or something similar you may have
-noticed that the context tries to stop you from modifying it.  If you have
-managed to modify the context by using an internal context API you may
-have noticed that changes in the context don't seem to be visible in the
-template.  The reason for this is that Jinja uses the context only as
-primary data source for template variables for performance reasons.
-
-If you want to modify the context write a function that returns a variable
-instead that one can assign to a variable by using set::
-
-    {% set comments = get_latest_comments() %}
-
-I don't have the _speedups Module.  Is Jinja slower now?
---------------------------------------------------------
-
-To achieve a good performance with automatic escaping enabled, the escaping
-function is also implemented in pure C and used if Jinja2 was installed with
-the speedups module.  This happens automatically if a C compiler is available
-on the system during installation.
-
-My tracebacks look weird.  What's happening?
---------------------------------------------
-
-If the speedups module is not compiled and you are using a Python installation
-without ctypes (Python 2.4 without ctypes, Jython or Google's AppEngine)
-Jinja2 is unable to provide correct debugging information and the traceback
-may be incomplete.  There is currently no good workaround for Jython or
-the AppEngine as ctypes is unavailable there and it's not possible to use
-the speedups extension.
-
-Why is there no Python 2.3 support?
------------------------------------
-
-Python 2.3 is missing a lot of features that are used heavily in Jinja2.  This
-decision was made as with the upcoming Python 2.6 and 3.0 versions it becomes
-harder to maintain the code for older Python versions.  If you really need
-Python 2.3 support you either have to use `Jinja 1`_ or other templating
-engines that still support 2.3.
-
-My Macros are overriden by something
-------------------------------------
-
-In some situations the Jinja scoping appears arbitrary:
-
-layout.tmpl:
-
-.. sourcecode:: jinja
-
-    {% macro foo() %}LAYOUT{% endmacro %}
-    {% block body %}{% endblock %}
-
-child.tmpl:
-
-.. sourcecode:: jinja
-
-    {% extends 'layout.tmpl' %}
-    {% macro foo() %}CHILD{% endmacro %}
-    {% block body %}{{ foo() }}{% endblock %}
-
-This will print ``LAYOUT`` in Jinja2.  This is a side effect of having
-the parent template evaluated after the child one.  This allows child
-templates passing information to the parent template.  To avoid this
-issue rename the macro or variable in the parent template to have an
-uncommon prefix.
-
-.. _Jinja 1: http://jinja.pocoo.org/1/
--- a/bundled/jinja2/docs/index.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-Jinja2 Documentation
-====================
-
-This is the documentation for the Jinja2 general purpose templating language.
-Jinja2 is a library for Python 2.4 and onwards that is designed to be flexible,
-fast and secure.
-
-.. toctree::
-   :maxdepth: 2
-
-   intro
-   api
-   sandbox
-   templates
-   extensions
-   integration
-   switching
-   tricks
-
-   faq
-   changelog
-
-If you can't find the information you're looking for, have a look at the
-index of try to find it using the search function:
-
-* :ref:`genindex`
-* :ref:`search`
--- a/bundled/jinja2/docs/integration.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-Integration
-===========
-
-Jinja2 provides some code for integration into other tools such as frameworks,
-the `Babel`_ library or your favourite editor for fancy code highlighting.
-This is a brief description of whats included.
-
-.. _babel-integration:
-
-Babel Integration
------------------
-
-Jinja provides support for extracting gettext messages from templates via a
-`Babel`_ extractor entry point called `jinja2.ext.babel_extract`.  The Babel
-support is implemented as part of the :ref:`i18n-extension` extension.
-
-Gettext messages extracted from both `trans` tags and code expressions.
-
-To extract gettext messages from templates, the project needs a Jinja2 section
-in its Babel extraction method `mapping file`_:
-
-.. sourcecode:: ini
-
-    [jinja2: **/templates/**.html]
-    encoding = utf-8
-
-The syntax related options of the :class:`Environment` are also available as
-configuration values in the mapping file.  For example to tell the extraction
-that templates use ``%`` as `line_statement_prefix` you can use this code:
-
-.. sourcecode:: ini
-
-    [jinja2: **/templates/**.html]
-    encoding = utf-8
-    line_statement_prefix = %
-
-:ref:`jinja-extensions` may also be defined by passing a comma separated list
-of import paths as `extensions` value.  The i18n extension is added
-automatically.
-
-.. _mapping file: http://babel.edgewall.org/wiki/Documentation/messages.html#extraction-method-mapping-and-configuration
-
-Pylons
-------
-
-With `Pylons`_ 0.9.7 onwards it's incredible easy to integrate Jinja into a
-Pylons powered application.
-
-The template engine is configured in `config/environment.py`.  The configuration
-for Jinja2 looks something like that::
-
-    from jinja2 import Environment, PackageLoader
-    config['pylons.app_globals'].jinja_env = Environment(
-        loader=PackageLoader('yourapplication', 'templates')
-    )
-
-After that you can render Jinja templates by using the `render_jinja` function
-from the `pylons.templating` module.
-
-Additionally it's a good idea to set the Pylons' `c` object into strict mode.
-Per default any attribute to not existing attributes on the `c` object return
-an empty string and not an undefined object.  To change this just use this
-snippet and add it into your `config/environment.py`::
-
-    config['pylons.strict_c'] = True
-
-.. _Pylons: http://www.pylonshq.com/
-
-TextMate
---------
-
-Inside the `ext` folder of Jinja2 there is a bundle for TextMate that supports
-syntax highlighting for Jinja1 and Jinja2 for text based templates as well as
-HTML.  It also contains a few often used snippets.
-
-Vim
----
-
-A syntax plugin for `Vim`_ exists in the Vim-scripts directory as well as the
-ext folder of Jinja2.  `The script <http://www.vim.org/scripts/script.php?script_id=1856>`_
-supports Jinja1 and Jinja2.  Once installed two file types are available `jinja`
-and `htmljinja`.  The first one for text based templates, the latter for HTML
-templates.
-
-Copy the files into your `syntax` folder.
-
-.. _Babel: http://babel.edgewall.org/
-.. _Vim: http://www.vim.org/
--- a/bundled/jinja2/docs/intro.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-Introduction
-============
-
-This is the documentation for the Jinja2 general purpose templating language.
-Jinja2 is a library for Python 2.4 and onwards that is designed to be flexible,
-fast and secure.
-
-If you have any exposure to other text-based template languages, such as Smarty or
-Django, you should feel right at home with Jinja2.  It's both designer and
-developer friendly by sticking to Python's principles and adding functionality
-useful for templating environments.
-
-The key-features are...
-
--   ... **configurable syntax**.  If you are generating LaTeX or other formats
-    with Jinja2 you can change the delimiters to something that integrates better
-    into the LaTeX markup.
-
--   ... **fast**.  While performance is not the primarily target of Jinja2 it's
-    surprisingly fast.  The overhead compared to regular Python code was reduced
-    to the very minimum.
-
--   ... **easy to debug**.  Jinja2 integrates directly into the python traceback
-    system which allows you to debug Jinja2 templates with regular python
-    debugging helpers.
-
--   ... **secure**.  It's possible to evaluate untrusted template code if the
-    optional sandbox is enabled.  This allows Jinja2 to be used as templating
-    language for applications where users may modify the template design.
-
-
-Prerequisites
--------------
-
-Jinja2 needs at least **Python 2.4** to run.  Additionally a working C-compiler
-that can create python extensions should be installed for the debugger.  If no
-C-compiler is available and you are using Python 2.4 the `ctypes`_ module
-should be installed.
-
-If you don't have a working C-compiler and you are trying to install the source
-release with the speedups you will get a compiler error.  This however can be
-circumvented by passing the ``--without-speedups`` command line argument to the
-setup script::
-
-    $ python setup.py --with-speedups install
-
-(As of Jinja 2.2, the speedups are disabled by default and can be enabled
-with ``--with-speedups``.  See :ref:`enable-speedups`)
-
-.. _ctypes: http://python.net/crew/theller/ctypes/
-
-
-Installation
-------------
-
-You have multiple ways to install Jinja2.  If you are unsure what to do, go
-with the Python egg or tarball.
-
-As a Python egg (via easy_install)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can install the most recent Jinja2 version using `easy_install`_ or `pip`_::
-
-    easy_install Jinja2
-    pip install Jinja2
-
-This will install a Jinja2 egg in your Python installation's site-packages
-directory.
-
-(If you are installing from the windows command line omit the `sudo` and make
-sure to run the command as user with administrator rights)
-
-From the tarball release
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-1.  Download the most recent tarball from the `download page`_
-2.  Unpack the tarball
-3.  ``sudo python setup.py install``
-
-Note that you either have to have setuptools or `distribute`_ installed,
-the latter is preferred.
-
-This will install Jinja2 into your Python installation's site-packages directory.
-
-.. _distribute: http://pypi.python.org/pypi/distribute
-
-Installing the development version
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-1.  Install `mercurial`_
-2.  ``hg clone http://dev.pocoo.org/hg/jinja2-main jinja2``
-3.  ``cd jinja2``
-4.  ``ln -s jinja2 /usr/lib/python2.X/site-packages``
-
-As an alternative to steps 4 you can also do ``python setup.py develop``
-which will install the package via distribute in development mode.  This also
-has the advantage that the C extensions are compiled.
-
-Alternative you can use `pip`_ to install the current development
-snapshot::
-
-    sudo pip install Jinja2==dev
-
-Or the `easy_install`_ command::
-
-    sudo easy_install Jinja2==dev
-
-.. _download page: http://pypi.python.org/pypi/Jinja2
-.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
-.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
-.. _pip: http://pypi.python.org/pypi/pip
-.. _mercurial: http://www.selenic.com/mercurial/
-
-.. _enable-speedups:
-
-Enable the speedups Module
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-By default Jinja2 will not compile the speedups module.  Enabling this
-will fail if you don't have the Python headers or a working compiler.  This
-is often the case if you are installing Jinja2 from a windows machine.
-
-You can enable the speedups extension when installing using the
-``--with-speedups`` flag::
-
-    sudo python setup.py --with-speedups install
-
-
-
-Basic API Usage
----------------
-
-This section gives you a brief introduction to the Python API for Jinja2
-templates.
-
-The most basic way to create a template and render it is through
-:class:`~jinja2.Template`.  This however is not the recommended way to
-work with it if your templates are not loaded from strings but the file
-system or another data source:
-
->>> from jinja2 import Template
->>> template = Template('Hello {{ name }}!')
->>> template.render(name='John Doe')
-u'Hello John Doe!'
-
-By creating an instance of :class:`~jinja2.Template` you get back a new template
-object that provides a method called :meth:`~jinja2.Template.render` which when
-called with a dict or keyword arguments expands the template.  The dict
-or keywords arguments passed to the template are the so-called "context"
-of the template.
-
-What you can see here is that Jinja2 is using unicode internally and the
-return value is an unicode string.  So make sure that your application is
-indeed using unicode internally.
-
-
-Experimental Python 3 Support
------------------------------
-
-Jinja 2.3 brings experimental support for Python 3.  It means that all
-unittests pass on the new version, but there might still be small bugs in
-there and behavior might be inconsistent.  If you notice any bugs, please
-provide feedback in the `Jinja bug tracker`_.
-
-Also please keep in mind that the documentation is written with Python 2
-in mind, you will have to adapt the shown code examples to Python 3 syntax
-for yourself.
-
-
-.. _Jinja bug tracker: http://dev.pocoo.org/projects/jinja/
--- a/bundled/jinja2/docs/jinjaext.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,192 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    Jinja Documentation Extensions
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Support for automatically documenting filters and tests.
-
-    :copyright: Copyright 2008 by Armin Ronacher.
-    :license: BSD.
-"""
-import os
-import re
-import inspect
-import jinja2
-from itertools import islice
-from types import BuiltinFunctionType
-from docutils import nodes
-from docutils.statemachine import ViewList
-from sphinx.ext.autodoc import prepare_docstring
-from sphinx.application import TemplateBridge
-from pygments.style import Style
-from pygments.token import Keyword, Name, Comment, String, Error, \
-     Number, Operator, Generic
-from jinja2 import Environment, FileSystemLoader
-
-
-def parse_rst(state, content_offset, doc):
-    node = nodes.section()
-    # hack around title style bookkeeping
-    surrounding_title_styles = state.memo.title_styles
-    surrounding_section_level = state.memo.section_level
-    state.memo.title_styles = []
-    state.memo.section_level = 0
-    state.nested_parse(doc, content_offset, node, match_titles=1)
-    state.memo.title_styles = surrounding_title_styles
-    state.memo.section_level = surrounding_section_level
-    return node.children
-
-
-class JinjaStyle(Style):
-    title = 'Jinja Style'
-    default_style = ""
-    styles = {
-        Comment:                    'italic #aaaaaa',
-        Comment.Preproc:            'noitalic #B11414',
-        Comment.Special:            'italic #505050',
-
-        Keyword:                    'bold #B80000',
-        Keyword.Type:               '#808080',
-
-        Operator.Word:              'bold #B80000',
-
-        Name.Builtin:               '#333333',
-        Name.Function:              '#333333',
-        Name.Class:                 'bold #333333',
-        Name.Namespace:             'bold #333333',
-        Name.Entity:                'bold #363636',
-        Name.Attribute:             '#686868',
-        Name.Tag:                   'bold #686868',
-        Name.Decorator:             '#686868',
-
-        String:                     '#AA891C',
-        Number:                     '#444444',
-
-        Generic.Heading:            'bold #000080',
-        Generic.Subheading:         'bold #800080',
-        Generic.Deleted:            '#aa0000',
-        Generic.Inserted:           '#00aa00',
-        Generic.Error:              '#aa0000',
-        Generic.Emph:               'italic',
-        Generic.Strong:             'bold',
-        Generic.Prompt:             '#555555',
-        Generic.Output:             '#888888',
-        Generic.Traceback:          '#aa0000',
-
-        Error:                      '#F00 bg:#FAA'
-    }
-
-
-_sig_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*(\(.*?\))')
-
-
-def format_function(name, aliases, func):
-    lines = inspect.getdoc(func).splitlines()
-    signature = '()'
-    if isinstance(func, BuiltinFunctionType):
-        match = _sig_re.match(lines[0])
-        if match is not None:
-            del lines[:1 + bool(lines and not lines[0])]
-            signature = match.group(1)
-    else:
-        try:
-            argspec = inspect.getargspec(func)
-            if getattr(func, 'environmentfilter', False) or \
-               getattr(func, 'contextfilter', False):
-                del argspec[0][0]
-            signature = inspect.formatargspec(*argspec)
-        except:
-            pass
-    result = ['.. function:: %s%s' % (name, signature), '']
-    result.extend('    ' + line for line in lines)
-    if aliases:
-        result.extend(('', '    :aliases: %s' % ', '.join(
-                      '``%s``' % x for x in sorted(aliases))))
-    return result
-
-
-def dump_functions(mapping):
-    def directive(dirname, arguments, options, content, lineno,
-                      content_offset, block_text, state, state_machine):
-        reverse_mapping = {}
-        for name, func in mapping.iteritems():
-            reverse_mapping.setdefault(func, []).append(name)
-        filters = []
-        for func, names in reverse_mapping.iteritems():
-            aliases = sorted(names, key=lambda x: len(x))
-            name = aliases.pop()
-            filters.append((name, aliases, func))
-        filters.sort()
-
-        result = ViewList()
-        for name, aliases, func in filters:
-            for item in format_function(name, aliases, func):
-                result.append(item, '<jinjaext>')
-
-        node = nodes.paragraph()
-        state.nested_parse(result, content_offset, node)
-        return node.children
-    return directive
-
-
-from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS
-jinja_filters = dump_functions(DEFAULT_FILTERS)
-jinja_tests = dump_functions(DEFAULT_TESTS)
-
-
-def jinja_nodes(dirname, arguments, options, content, lineno,
-                content_offset, block_text, state, state_machine):
-    from jinja2.nodes import Node
-    doc = ViewList()
-    def walk(node, indent):
-        p = ' ' * indent
-        sig = ', '.join(node.fields)
-        doc.append(p + '.. autoclass:: %s(%s)' % (node.__name__, sig), '')
-        if node.abstract:
-            members = []
-            for key, name in node.__dict__.iteritems():
-                if not key.startswith('_') and \
-                   not hasattr(node.__base__, key) and callable(name):
-                    members.append(key)
-            if members:
-                members.sort()
-                doc.append('%s :members: %s' % (p, ', '.join(members)), '')
-        if node.__base__ != object:
-            doc.append('', '')
-            doc.append('%s :Node type: :class:`%s`' %
-                       (p, node.__base__.__name__), '')
-        doc.append('', '')
-        children = node.__subclasses__()
-        children.sort(key=lambda x: x.__name__.lower())
-        for child in children:
-            walk(child, indent)
-    walk(Node, 0)
-    return parse_rst(state, content_offset, doc)
-
-
-def inject_toc(app, doctree, docname):
-    titleiter = iter(doctree.traverse(nodes.title))
-    try:
-        # skip first title, we are not interested in that one
-        titleiter.next()
-        title = titleiter.next()
-        # and check if there is at least another title
-        titleiter.next()
-    except StopIteration:
-        return
-    tocnode = nodes.section('')
-    tocnode['classes'].append('toc')
-    toctitle = nodes.section('')
-    toctitle['classes'].append('toctitle')
-    toctitle.append(nodes.title(text='Table Of Contents'))
-    tocnode.append(toctitle)
-    tocnode += doctree.document.settings.env.get_toc_for(docname)[0][1]
-    title.parent.insert(title.parent.children.index(title), tocnode)
-
-
-def setup(app):
-    app.add_directive('jinjafilters', jinja_filters, 0, (0, 0, 0))
-    app.add_directive('jinjatests', jinja_tests, 0, (0, 0, 0))
-    app.add_directive('jinjanodes', jinja_nodes, 0, (0, 0, 0))
-    # uncomment for inline toc.  links are broken unfortunately
-    ##app.connect('doctree-resolved', inject_toc)
--- a/bundled/jinja2/docs/sandbox.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-Sandbox
-=======
-
-The Jinja2 sandbox can be used to evaluate untrusted code.  Access to unsafe
-attributes and methods is prohibited.
-
-Assuming `env` is a :class:`SandboxedEnvironment` in the default configuration
-the following piece of code shows how it works:
-
->>> env.from_string("{{ func.func_code }}").render(func=lambda:None)
-u''
->>> env.from_string("{{ func.func_code.do_something }}").render(func=lambda:None)
-Traceback (most recent call last):
-  ...
-SecurityError: access to attribute 'func_code' of 'function' object is unsafe.
-
-
-.. module:: jinja2.sandbox
-
-.. autoclass:: SandboxedEnvironment([options])
-    :members: is_safe_attribute, is_safe_callable
-
-.. autoclass:: ImmutableSandboxedEnvironment([options])
-
-.. autoexception:: SecurityError
-
-.. autofunction:: unsafe
-
-.. autofunction:: is_internal_attribute
-
-.. autofunction:: modifies_known_mutable
-
-.. admonition:: Note
-
-    The Jinja2 sandbox alone is no solution for perfect security.  Especially
-    for web applications you have to keep in mind that users may create
-    templates with arbitrary HTML in so it's crucial to ensure that (if you
-    are running multiple users on the same server) they can't harm each other
-    via JavaScript insertions and much more.
-
-    Also the sandbox is only as good as the configuration.  We stronly
-    recommend only passing non-shared resources to the template and use
-    some sort of whitelisting for attributes.
-
-    Also keep in mind that templates may raise runtime or compile time errors,
-    so make sure to catch them.
--- a/bundled/jinja2/docs/switching.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +0,0 @@
-Switching from other Template Engines
-=====================================
-
-.. highlight:: html+jinja
-
-If you have used a different template engine in the past and want to swtich
-to Jinja2 here is a small guide that shows the basic syntatic and semantic
-changes between some common, similar text template engines for Python.
-
-Jinja1
-------
-
-Jinja2 is mostly compatible with Jinja1 in terms of API usage and template
-syntax.  The differences between Jinja1 and 2 are explained in the following
-list.
-
-API
-~~~
-
-Loaders
-    Jinja2 uses a different loader API.  Because the internal representation
-    of templates changed there is no longer support for external caching
-    systems such as memcached.  The memory consumed by templates is comparable
-    with regular Python modules now and external caching doesn't give any
-    advantage.  If you have used a custom loader in the past have a look at
-    the new :ref:`loader API <loaders>`.
-
-Loading templates from strings
-    In the past it was possible to generate templates from a string with the
-    default environment configuration by using `jinja.from_string`.  Jinja2
-    provides a :class:`Template` class that can be used to do the same, but
-    with optional additional configuration.
-
-Automatic unicode conversion
-    Jinja1 performed automatic conversion of bytestrings in a given encoding
-    into unicode objects.  This conversion is no longer implemented as it
-    was inconsistent as most libraries are using the regular Python ASCII
-    bytestring to Unicode conversion.  An application powered by Jinja2
-    *has to* use unicode internally everywhere or make sure that Jinja2 only
-    gets unicode strings passed.
-
-i18n
-    Jinja1 used custom translators for internationalization.  i18n is now
-    available as Jinja2 extension and uses a simpler, more gettext friendly
-    interface and has support for babel.  For more details see
-    :ref:`i18n-extension`.
-
-Internal methods
-    Jinja1 exposed a few internal methods on the environment object such
-    as `call_function`, `get_attribute` and others.  While they were marked
-    as being an internal method it was possible to override them.  Jinja2
-    doesn't have equivalent methods.
-
-Sandbox
-    Jinja1 was running sandbox mode by default.  Few applications actually
-    used that feature so it became optional in Jinja2.  For more details
-    about the sandboxed execution see :class:`SandboxedEnvironment`.
-
-Context
-    Jinja1 had a stacked context as storage for variables passed to the
-    environment.  In Jinja2 a similar object exists but it doesn't allow
-    modifications nor is it a singleton.  As inheritance is dynamic now
-    multiple context objects may exist during template evaluation.
-
-Filters and Tests
-    Filters and tests are regular functions now.  It's no longer necessary
-    and allowed to use factory functions.
-
-
-Templates
-~~~~~~~~~
-
-Jinja2 has mostly the same syntax as Jinja1.  What's different is that
-macros require parentheses around the argument list now.
-
-Additionally Jinja2 allows dynamic inheritance now and dynamic includes.
-The old helper function `rendertemplate` is gone now, `include` can be used
-instead.  Includes no longer import macros and variable assignments, for
-that the new `import` tag is used.  This concept is explained in the
-:ref:`import` documentation.
-
-Another small change happened in the `for`-tag.  The special loop variable
-doesn't have a `parent` attribute, instead you have to alias the loop
-yourself.  See :ref:`accessing-the-parent-loop` for more details.
-
-
-Django
-------
-
-If you have previously worked with Django templates, you should find
-Jinja2 very familiar.  In fact, most of the syntax elements look and
-work the same.
-
-However, Jinja2 provides some more syntax elements covered in the
-documentation and some work a bit different.
-
-This section covers the template changes.  As the API is fundamentally
-different we won't cover it here.
-
-Method Calls
-~~~~~~~~~~~~
-
-In Django method calls work implicitly.  With Jinja2 you have to specify that
-you want to call an object.  Thus this Django code::
-
-    {% for page in user.get_created_pages %}
-        ...
-    {% endfor %}
-    
-will look like this in Jinja::
-
-    {% for page in user.get_created_pages() %}
-        ...
-    {% endfor %}
-
-This allows you to pass variables to the function which is also used for macros
-which is not possible in Django.
-
-Conditions
-~~~~~~~~~~
-
-In Django you can use the following constructs to check for equality::
-
-    {% ifequal foo "bar" %}
-        ...
-    {% else %}
-        ...
-    {% endifequal %}
-
-In Jinja2 you can use the normal if statement in combination with operators::
-
-    {% if foo == 'bar' %}
-        ...
-    {% else %}
-        ...
-    {% endif %}
-
-You can also have multiple elif branches in your template::
-
-    {% if something %}
-        ...
-    {% elif otherthing %}
-        ...
-    {% elif foothing %}
-        ...
-    {% else %}
-        ...
-    {% endif %}
-
-Filter Arguments
-~~~~~~~~~~~~~~~~
-
-Jinja2 provides more than one argument for filters.  Also the syntax for
-argument passing is different.  A template that looks like this in Django::
-
-    {{ items|join:", " }}
-
-looks like this in Jinja2::
-
-    {{ items|join(', ') }}
-
-In fact it's a bit more verbose but it allows different types of arguments -
-including variables - and more than one of them.
-
-Tests
-~~~~~
-
-In addition to filters there also are tests you can perform using the is
-operator.  Here are some examples::
-
-    {% if user.user_id is odd %}
-        {{ user.username|e }} is odd
-    {% else %}
-        hmm. {{ user.username|e }} looks pretty normal
-    {% endif %}
-
-Loops
-~~~~~
-
-For loops work very similar to Django, the only incompatibility is that in
-Jinja2 the special variable for the loop context is called `loop` and not
-`forloop` like in Django.
-
-Cycle
-~~~~~
-
-The ``{% cycle %}`` tag does not exist in Jinja because of it's implicit
-nature.  However you can achieve mostly the same by using the `cycle`
-method on a loop object.
-
-The following Django template::
-
-    {% for user in users %}
-        <li class="{% cycle 'odd' 'even' %}">{{ user }}</li>
-    {% endfor %}
-
-Would look like this in Jinja::
-
-    {% for user in users %}
-        <li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
-    {% endfor %}
-
-There is no equivalent of ``{% cycle ... as variable %}``.
-
-
-Mako
-----
-
-.. highlight:: html+mako
-
-If you have used Mako so far and want to switch to Jinja2 you can configure
-Jinja2 to look more like Mako:
-
-.. sourcecode:: python
-
-    env = Environment('<%', '%>', '${', '}', '%')
-
-Once the environment is configure like that Jinja2 should be able to interpret
-a small subset of Mako templates.  Jinja2 does not support embedded Python code
-so you would have to move that out of the template.  The syntax for defs (in
-Jinja2 defs are called macros) and template inheritance is different too.  The
-following Mako template::
-
-    <%inherit file="layout.html" />
-    <%def name="title()">Page Title</%def>
-    <ul>
-    % for item in list:
-        <li>${item}</li>
-    % endfor
-    </ul>
-
-Looks like this in Jinja2 with the above configuration::
-
-    <% extends "layout.html" %>
-    <% block title %>Page Title<% endblock %>
-    <% block body %>
-    <ul>
-    % for item in list:
-        <li>${item}</li>
-    % endfor
-    </ul>
-    <% endblock %>
--- a/bundled/jinja2/docs/templates.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1310 +0,0 @@
-Template Designer Documentation
-===============================
-
-.. highlight:: html+jinja
-
-This document describes the syntax and semantics of the template engine and
-will be most useful as reference to those creating Jinja templates.  As the
-template engine is very flexible the configuration from the application might
-be slightly different from here in terms of delimiters and behavior of
-undefined values.
-
-
-Synopsis
---------
-
-A template is simply a text file.  It can generate any text-based format
-(HTML, XML, CSV, LaTeX, etc.).  It doesn't have a specific extension,
-``.html`` or ``.xml`` are just fine.
-
-A template contains **variables** or **expressions**, which get replaced with
-values when the template is evaluated, and tags, which control the logic of
-the template.  The template syntax is heavily inspired by Django and Python.
-
-Below is a minimal template that illustrates a few basics.  We will cover
-the details later in that document::
-
-    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-    <html lang="en">
-    <head>
-        <title>My Webpage</title>
-    </head>
-    <body>
-        <ul id="navigation">
-        {% for item in navigation %}
-            <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
-        {% endfor %}
-        </ul>
-
-        <h1>My Webpage</h1>
-        {{ a_variable }}
-    </body>
-    </html>
-
-This covers the default settings.  The application developer might have
-changed the syntax from ``{% foo %}`` to ``<% foo %>`` or something similar.
-
-There are two kinds of delimiers. ``{% ... %}`` and ``{{ ... }}``.  The first
-one is used to execute statements such as for-loops or assign values, the
-latter prints the result of the expression to the template.
-
-.. _variables:
-
-Variables
----------
-
-The application passes variables to the templates you can mess around in the
-template.  Variables may have attributes or elements on them you can access
-too.  How a variable looks like, heavily depends on the application providing
-those.
-
-You can use a dot (``.``) to access attributes of a variable, alternative the
-so-called "subscript" syntax (``[]``) can be used.  The following lines do
-the same::
-
-    {{ foo.bar }}
-    {{ foo['bar'] }}
-
-It's important to know that the curly braces are *not* part of the variable
-but the print statement.  If you access variables inside tags don't put the
-braces around.
-
-If a variable or attribute does not exist you will get back an undefined
-value.  What you can do with that kind of value depends on the application
-configuration, the default behavior is that it evaluates to an empty string
-if printed and that you can iterate over it, but every other operation fails.
-
-.. _notes-on-subscriptions:
-
-.. admonition:: Implementation
-
-    For convenience sake ``foo.bar`` in Jinja2 does the following things on
-    the Python layer:
-
-    -   check if there is an attribute called `bar` on `foo`.
-    -   if there is not, check if there is an item ``'bar'`` in `foo`.
-    -   if there is not, return an undefined object.
-
-    ``foo['bar']`` on the other hand works mostly the same with the a small
-    difference in the order:
-
-    -   check if there is an item ``'bar'`` in `foo`.
-    -   if there is not, check if there is an attribute called `bar` on `foo`.
-    -   if there is not, return an undefined object.
-
-    This is important if an object has an item or attribute with the same
-    name.  Additionally there is the :func:`attr` filter that just looks up
-    attributes.
-
-.. _filters:
-
-Filters
--------
-
-Variables can by modified by **filters**.  Filters are separated from the
-variable by a pipe symbol (``|``) and may have optional arguments in
-parentheses.  Multiple filters can be chained.  The output of one filter is
-applied to the next.
-
-``{{ name|striptags|title }}`` for example will remove all HTML Tags from the
-`name` and title-cases it.  Filters that accept arguments have parentheses
-around the arguments, like a function call.  This example will join a list
-by commas:  ``{{ list|join(', ') }}``.
-
-The :ref:`builtin-filters` below describes all the builtin filters.
-
-.. _tests:
-
-Tests
------
-
-Beside filters there are also so called "tests" available.  Tests can be used
-to test a variable against a common expression.  To test a variable or
-expression you add `is` plus the name of the test after the variable.  For
-example to find out if a variable is defined you can do ``name is defined``
-which will then return true or false depending on if `name` is defined.
-
-Tests can accept arguments too.  If the test only takes one argument you can
-leave out the parentheses to group them.  For example the following two
-expressions do the same::
-
-    {% if loop.index is divisibleby 3 %}
-    {% if loop.index is divisibleby(3) %}
-
-The :ref:`builtin-tests` below describes all the builtin tests.
-
-
-Comments
---------
-
-To comment-out part of a line in a template, use the comment syntax which is
-by default set to ``{# ... #}``.  This is useful to comment out parts of the
-template for debugging or to add information for other template designers or
-yourself::
-
-    {# note: disabled template because we no longer use this
-        {% for user in users %}
-            ...
-        {% endfor %}
-    #}
-
-
-Whitespace Control
-------------------
-
-In the default configuration whitespace is not further modified by the
-template engine, so each whitespace (spaces, tabs, newlines etc.) is returned
-unchanged.  If the application configures Jinja to `trim_blocks` the first
-newline after a a template tag is removed automatically (like in PHP).
-
-But you can also strip whitespace in templates by hand.  If you put an minus
-sign (``-``) to the start or end of an block (for example a for tag), a
-comment or variable expression you can remove the whitespaces after or before
-that block::
-
-    {% for item in seq -%}
-        {{ item }}
-    {%- endfor %}
-    
-This will yield all elements without whitespace between them.  If `seq` was
-a list of numbers from ``1`` to ``9`` the output would be ``123456789``.
-
-If :ref:`line-statements` are enabled they strip leading whitespace
-automatically up to the beginning of the line.
-
-.. admonition:: Note
-
-    You must not use a whitespace between the tag and the minus sign.
-
-    **valid**::
-
-        {%- if foo -%}...{% endif %}
-
-    **invalid**::
-
-        {% - if foo - %}...{% endif %}
-
-
-Escaping
---------
-
-It is sometimes desirable or even necessary to have Jinja ignore parts it
-would otherwise handle as variables or blocks.  For example if the default
-syntax is used and you want to use ``{{`` as raw string in the template and
-not start a variable you have to use a trick.
-
-The easiest way is to output the variable delimiter (``{{``) by using a
-variable expression::
-
-    {{ '{{' }}
-
-For bigger sections it makes sense to mark a block `raw`.  For example to
-put Jinja syntax as example into a template you can use this snippet::
-
-    {% raw %}
-        <ul>
-        {% for item in seq %}
-            <li>{{ item }}</li>
-        {% endfor %}
-        </ul>
-    {% endraw %}
-
-
-.. _line-statements:
-
-Line Statements
----------------
-
-If line statements are enabled by the application it's possible to mark a
-line as a statement.  For example if the line statement prefix is configured
-to ``#`` the following two examples are equivalent::
-
-    <ul>
-    # for item in seq
-        <li>{{ item }}</li>
-    # endfor
-    </ul>
-
-    <ul>
-    {% for item in seq %}
-        <li>{{ item }}</li>
-    {% endfor %}
-    </ul>
-
-The line statement prefix can appear anywhere on the line as long as no text
-precedes it.  For better readability statements that start a block (such as
-`for`, `if`, `elif` etc.) may end with a colon::
-
-    # for item in seq:
-        ...
-    # endfor
-
-
-.. admonition:: Note
-
-    Line statements can span multiple lines if there are open parentheses,
-    braces or brackets::
-
-        <ul>
-        # for href, caption in [('index.html', 'Index'),
-                                ('about.html', 'About')]:
-            <li><a href="{{ href }}">{{ caption }}</a></li>
-        # endfor
-        </ul>
-
-Since Jinja 2.2 line-based comments are available as well.  For example if
-the line-comment prefix is configured to be ``##`` everything from ``##`` to
-the end of the line is ignored (excluding the newline sign)::
-
-    # for item in seq:
-        <li>{{ item }}</li>     ## this comment is ignored
-    # endfor
-
-
-.. _template-inheritance:
-
-Template Inheritance
---------------------
-
-The most powerful part of Jinja is template inheritance. Template inheritance
-allows you to build a base "skeleton" template that contains all the common
-elements of your site and defines **blocks** that child templates can override.
-
-Sounds complicated but is very basic. It's easiest to understand it by starting
-with an example.
-
-
-Base Template
-~~~~~~~~~~~~~
-
-This template, which we'll call ``base.html``, defines a simple HTML skeleton
-document that you might use for a simple two-column page. It's the job of
-"child" templates to fill the empty blocks with content::
-
-    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-    <html lang="en">
-    <html xmlns="http://www.w3.org/1999/xhtml">
-    <head>
-        {% block head %}
-        <link rel="stylesheet" href="style.css" />
-        <title>{% block title %}{% endblock %} - My Webpage</title>
-        {% endblock %}
-    </head>
-    <body>
-        <div id="content">{% block content %}{% endblock %}</div>
-        <div id="footer">
-            {% block footer %}
-            &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
-            {% endblock %}
-        </div>
-    </body>
-
-In this example, the ``{% block %}`` tags define four blocks that child templates
-can fill in. All the `block` tag does is to tell the template engine that a
-child template may override those portions of the template.
-
-Child Template
-~~~~~~~~~~~~~~
-
-A child template might look like this::
-
-    {% extends "base.html" %}
-    {% block title %}Index{% endblock %}
-    {% block head %}
-        {{ super() }}
-        <style type="text/css">
-            .important { color: #336699; }
-        </style>
-    {% endblock %}
-    {% block content %}
-        <h1>Index</h1>
-        <p class="important">
-          Welcome on my awesome homepage.
-        </p>
-    {% endblock %}
-
-The ``{% extends %}`` tag is the key here. It tells the template engine that
-this template "extends" another template.  When the template system evaluates
-this template, first it locates the parent.  The extends tag should be the
-first tag in the template.  Everything before it is printed out normally and
-may cause confusion.  For details about this behavior and how to take
-advantage of it, see :ref:`null-master-fallback`.
-
-The filename of the template depends on the template loader.  For example the
-:class:`FileSystemLoader` allows you to access other templates by giving the
-filename.  You can access templates in subdirectories with an slash::
-
-    {% extends "layout/default.html" %}
-
-But this behavior can depend on the application embedding Jinja.  Note that
-since the child template doesn't define the ``footer`` block, the value from
-the parent template is used instead.
-
-You can't define multiple ``{% block %}`` tags with the same name in the
-same template.  This limitation exists because a block tag works in "both"
-directions.  That is, a block tag doesn't just provide a hole to fill - it
-also defines the content that fills the hole in the *parent*.  If there
-were two similarly-named ``{% block %}`` tags in a template, that template's
-parent wouldn't know which one of the blocks' content to use.
-
-If you want to print a block multiple times you can however use the special
-`self` variable and call the block with that name::
-
-    <title>{% block title %}{% endblock %}</title>
-    <h1>{{ self.title() }}</h1>
-    {% block body %}{% endblock %}
-
-
-Super Blocks
-~~~~~~~~~~~~
-
-It's possible to render the contents of the parent block by calling `super`.
-This gives back the results of the parent block::
-
-    {% block sidebar %}
-        <h3>Table Of Contents</h3>
-        ...
-        {{ super() }}
-    {% endblock %}
-
-
-Named Block End-Tags
-~~~~~~~~~~~~~~~~~~~~
-
-Jinja2 allows you to put the name of the block after the end tag for better
-readability::
-
-    {% block sidebar %}
-        {% block inner_sidebar %}
-            ...
-        {% endblock inner_sidebar %}
-    {% endblock sidebar %}
-
-However the name after the `endblock` word must match the block name.
-
-
-Block Nesting and Scope
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Blocks can be nested for more complex layouts.  However per default blocks
-may not access variables from outer scopes::
-
-    {% for item in seq %}
-        <li>{% block loop_item %}{{ item }}{% endblock %}</li>
-    {% endfor %}
-
-This example would output empty ``<li>`` items because `item` is unavailable
-inside the block.  The reason for this is that if the block is replaced by
-a child template a variable would appear that was not defined in the block or
-passed to the context.
-
-Starting with Jinja 2.2 you can explicitly specify that variables are
-available in a block by setting the block to "scoped" by adding the `scoped`
-modifier to a block declaration::
-
-    {% for item in seq %}
-        <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li>
-    {% endfor %}
-
-When overriding a block the `scoped` modifier does not have to be provided.
-
-
-Template Objects
-~~~~~~~~~~~~~~~~
-
-.. versionchanged:: 2.4
-
-If a template object was passed to the template context you can
-extend from that object as well.  Assuming the calling code passes
-a layout template as `layout_template` to the environment, this
-code works::
-
-    {% extends layout_template %}
-
-Previously the `layout_template` variable had to be a string with
-the layout template's filename for this to work.
-
-
-HTML Escaping
--------------
-
-When generating HTML from templates, there's always a risk that a variable will
-include characters that affect the resulting HTML.  There are two approaches:
-manually escaping each variable or automatically escaping everything by default.
-
-Jinja supports both, but what is used depends on the application configuration.
-The default configuaration is no automatic escaping for various reasons:
-
--   escaping everything except of safe values will also mean that Jinja is
-    escaping variables known to not include HTML such as numbers which is
-    a huge performance hit.
-
--   The information about the safety of a variable is very fragile.  It could
-    happen that by coercing safe and unsafe values the return value is double
-    escaped HTML.
-
-Working with Manual Escaping
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If manual escaping is enabled it's **your** responsibility to escape
-variables if needed.  What to escape?  If you have a variable that *may*
-include any of the following chars (``>``, ``<``, ``&``, or ``"``) you
-**have to** escape it unless the variable contains well-formed and trusted
-HTML.  Escaping works by piping the variable through the ``|e`` filter:
-``{{ user.username|e }}``.
-
-Working with Automatic Escaping
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When automatic escaping is enabled everything is escaped by default except
-for values explicitly marked as safe.  Those can either be marked by the
-application or in the template by using the `|safe` filter.  The main
-problem with this approach is that Python itself doesn't have the concept
-of tainted values so the information if a value is safe or unsafe can get
-lost.  If the information is lost escaping will take place which means that
-you could end up with double escaped contents.
-
-Double escaping is easy to avoid however, just rely on the tools Jinja2
-provides and don't use builtin Python constructs such as the string modulo
-operator.
-
-Functions returning template data (macros, `super`, `self.BLOCKNAME`) return
-safe markup always.
-
-String literals in templates with automatic escaping are considered unsafe
-too.  The reason for this is that the safe string is an extension to Python
-and not every library will work properly with it.
-
-
-List of Control Structures
---------------------------
-
-A control structure refers to all those things that control the flow of a
-program - conditionals (i.e. if/elif/else), for-loops, as well as things like
-macros and blocks.  Control structures appear inside ``{% ... %}`` blocks
-in the default syntax.
-
-For
-~~~
-
-Loop over each item in a sequence.  For example, to display a list of users
-provided in a variable called `users`::
-
-    <h1>Members</h1>
-    <ul>
-    {% for user in users %}
-      <li>{{ user.username|e }}</li>
-    {% endfor %}
-    </ul>
-
-Inside of a for loop block you can access some special variables:
-
-+-----------------------+---------------------------------------------------+
-| Variable              | Description                                       |
-+=======================+===================================================+
-| `loop.index`          | The current iteration of the loop. (1 indexed)    |
-+-----------------------+---------------------------------------------------+
-| `loop.index0`         | The current iteration of the loop. (0 indexed)    |
-+-----------------------+---------------------------------------------------+
-| `loop.revindex`       | The number of iterations from the end of the loop |
-|                       | (1 indexed)                                       |
-+-----------------------+---------------------------------------------------+
-| `loop.revindex0`      | The number of iterations from the end of the loop |
-|                       | (0 indexed)                                       |
-+-----------------------+---------------------------------------------------+
-| `loop.first`          | True if first iteration.                          |
-+-----------------------+---------------------------------------------------+
-| `loop.last`           | True if last iteration.                           |
-+-----------------------+---------------------------------------------------+
-| `loop.length`         | The number of items in the sequence.              |
-+-----------------------+---------------------------------------------------+
-| `loop.cycle`          | A helper function to cycle between a list of      |
-|                       | sequences.  See the explanation below.            |
-+-----------------------+---------------------------------------------------+
-
-Within a for-loop, it's possible to cycle among a list of strings/variables
-each time through the loop by using the special `loop.cycle` helper::
-
-    {% for row in rows %}
-        <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
-    {% endfor %}
-
-With Jinja 2.1 an extra `cycle` helper exists that allows loop-unbound
-cycling.  For more information have a look at the :ref:`builtin-globals`.
-
-.. _loop-filtering:
-
-Unlike in Python it's not possible to `break` or `continue` in a loop.  You
-can however filter the sequence during iteration which allows you to skip
-items.  The following example skips all the users which are hidden::
-
-    {% for user in users if not user.hidden %}
-        <li>{{ user.username|e }}</li>
-    {% endfor %}
-
-The advantage is that the special `loop` variable will count correctly thus
-not counting the users not iterated over.
-
-If no iteration took place because the sequence was empty or the filtering
-removed all the items from the sequence you can render a replacement block
-by using `else`::
-
-    <ul>
-    {% for user in users %}
-        <li>{{ user.username|e }}</li>
-    {% else %}
-        <li><em>no users found</em></li>
-    {% endfor %}
-    </ul>
-
-It is also possible to use loops recursively.  This is useful if you are
-dealing with recursive data such as sitemaps.  To use loops recursively you
-basically have to add the `recursive` modifier to the loop definition and
-call the `loop` variable with the new iterable where you want to recurse.
-
-The following example implements a sitemap with recursive loops::
-
-    <ul class="sitemap">
-    {%- for item in sitemap recursive %}
-        <li><a href="{{ item.href|e }}">{{ item.title }}</a>
-        {%- if item.children -%}
-            <ul class="submenu">{{ loop(item.children) }}</ul>
-        {%- endif %}</li>
-    {%- endfor %}
-    </ul>
-
-
-If
-~~
-
-The `if` statement in Jinja is comparable with the if statements of Python.
-In the simplest form you can use it to test if a variable is defined, not
-empty or not false::
-
-    {% if users %}
-    <ul>
-    {% for user in users %}
-        <li>{{ user.username|e }}</li>
-    {% endfor %}
-    </ul>
-    {% endif %}
-
-For multiple branches `elif` and `else` can be used like in Python.  You can
-use more complex :ref:`expressions` there too::
-
-    {% if kenny.sick %}
-        Kenny is sick.
-    {% elif kenny.dead %}
-        You killed Kenny!  You bastard!!!
-    {% else %}
-        Kenny looks okay --- so far
-    {% endif %}
-
-If can also be used as :ref:`inline expression <if-expression>` and for
-:ref:`loop filtering <loop-filtering>`.
-
-
-Macros
-~~~~~~
-
-Macros are comparable with functions in regular programming languages.  They
-are useful to put often used idioms into reusable functions to not repeat
-yourself.
-
-Here a small example of a macro that renders a form element::
-
-    {% macro input(name, value='', type='text', size=20) -%}
-        <input type="{{ type }}" name="{{ name }}" value="{{
-            value|e }}" size="{{ size }}">
-    {%- endmacro %}
-
-The macro can then be called like a function in the namespace::
-
-    <p>{{ input('username') }}</p>
-    <p>{{ input('password', type='password') }}</p>
-
-If the macro was defined in a different template you have to
-:ref:`import <import>` it first.
-
-Inside macros you have access to three special variables:
-
-`varargs`
-    If more positional arguments are passed to the macro than accepted by the
-    macro they end up in the special `varargs` variable as list of values.
-
-`kwargs`
-    Like `varargs` but for keyword arguments.  All unconsumed keyword
-    arguments are stored in this special variable.
-
-`caller`
-    If the macro was called from a :ref:`call<call>` tag the caller is stored
-    in this variable as macro which can be called.
-
-Macros also expose some of their internal details.  The following attributes
-are available on a macro object:
-
-`name`
-    The name of the macro.  ``{{ input.name }}`` will print ``input``.
-
-`arguments`
-    A tuple of the names of arguments the macro accepts.
-
-`defaults`
-    A tuple of default values.
-
-`catch_kwargs`
-    This is `true` if the macro accepts extra keyword arguments (ie: accesses
-    the special `kwargs` variable).
-
-`catch_varargs`
-    This is `true` if the macro accepts extra positional arguments (ie:
-    accesses the special `varargs` variable).
-
-`caller`
-    This is `true` if the macro accesses the special `caller` variable and may
-    be called from a :ref:`call<call>` tag.
-
-If a macro name starts with an underscore it's not exported and can't
-be imported.
-
-
-.. _call:
-
-Call
-~~~~
-
-In some cases it can be useful to pass a macro to another macro.  For this
-purpose you can use the special `call` block.  The following example shows
-a macro that takes advantage of the call functionality and how it can be
-used::
-
-    {% macro render_dialog(title, class='dialog') -%}
-        <div class="{{ class }}">
-            <h2>{{ title }}</h2>
-            <div class="contents">
-                {{ caller() }}
-            </div>
-        </div>
-    {%- endmacro %}
-
-    {% call render_dialog('Hello World') %}
-        This is a simple dialog rendered by using a macro and
-        a call block.
-    {% endcall %}
-
-It's also possible to pass arguments back to the call block.  This makes it
-useful as replacement for loops.  Generally speaking a call block works
-exactly like an macro, just that it doesn't have a name.
-
-Here an example of how a call block can be used with arguments::
-
-    {% macro dump_users(users) -%}
-        <ul>
-        {%- for user in users %}
-            <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
-        {%- endfor %}
-        </ul>
-    {%- endmacro %}
-
-    {% call(user) dump_users(list_of_user) %}
-        <dl>
-            <dl>Realname</dl>
-            <dd>{{ user.realname|e }}</dd>
-            <dl>Description</dl>
-            <dd>{{ user.description }}</dd>
-        </dl>
-    {% endcall %}
-
-
-Filters
-~~~~~~~
-
-Filter sections allow you to apply regular Jinja2 filters on a block of
-template data.  Just wrap the code in the special `filter` section::
-
-    {% filter upper %}
-        This text becomes uppercase
-    {% endfilter %}
-
-
-Assignments
-~~~~~~~~~~~
-
-Inside code blocks you can also assign values to variables.  Assignments at
-top level (outside of blocks, macros or loops) are exported from the template
-like top level macros and can be imported by other templates.
-
-Assignments use the `set` tag and can have multiple targets::
-
-    {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
-    {% set key, value = call_something() %}
-
-
-Extends
-~~~~~~~
-
-The `extends` tag can be used to extend a template from another one.  You
-can have multiple of them in a file but only one of them may be executed
-at the time.  See the section about :ref:`template-inheritance` above.
-
-
-Block
-~~~~~
-
-Blocks are used for inheritance and act as placeholders and replacements
-at the same time.  They are documented in detail as part of the section
-about :ref:`template-inheritance`.
-
-
-Include
-~~~~~~~
-
-The `include` statement is useful to include a template and return the
-rendered contents of that file into the current namespace::
-
-    {% include 'header.html' %}
-        Body
-    {% include 'footer.html' %}
-
-Included templates have access to the variables of the active context by
-default.  For more details about context behavior of imports and includes
-see :ref:`import-visibility`.
-
-From Jinja 2.2 onwards you can mark an include with ``ignore missing`` in
-which case Jinja will ignore the statement if the template to be ignored
-does not exist.  When combined with ``with`` or ``without context`` it has
-to be placed *before* the context visibility statement.  Here some valid
-examples::
-
-    {% include "sidebar.html" ignore missing %}
-    {% include "sidebar.html" ignore missing with context %}
-    {% include "sidebar.html" ignore missing without context %}
-
-.. versionadded:: 2.2
-
-You can also provide a list of templates that are checked for existence
-before inclusion.  The first template that exists will be included.  If
-`ignore missing` is given, it will fall back to rendering nothing if
-none of the templates exist, otherwise it will raise an exception.
-
-Example::
-
-    {% include ['page_detailed.html', 'page.html'] %}
-    {% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}
-
-.. versionchanged:: 2.4
-   If a template object was passed to the template context you can
-   include that object using `include`.
-
-.. _import:
-
-Import
-~~~~~~
-
-Jinja2 supports putting often used code into macros.  These macros can go into
-different templates and get imported from there.  This works similar to the
-import statements in Python.  It's important to know that imports are cached
-and imported templates don't have access to the current template variables,
-just the globals by defualt.  For more details about context behavior of
-imports and includes see :ref:`import-visibility`.
-
-There are two ways to import templates.  You can import the complete template
-into a variable or request specific macros / exported variables from it.
-
-Imagine we have a helper module that renders forms (called `forms.html`)::
-
-    {% macro input(name, value='', type='text') -%}
-        <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
-    {%- endmacro %}
-
-    {%- macro textarea(name, value='', rows=10, cols=40) -%}
-        <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
-            }}">{{ value|e }}</textarea>
-    {%- endmacro %}
-
-The easiest and most flexible is importing the whole module into a variable.
-That way you can access the attributes::
-
-    {% import 'forms.html' as forms %}
-    <dl>
-        <dt>Username</dt>
-        <dd>{{ forms.input('username') }}</dd>
-        <dt>Password</dt>
-        <dd>{{ forms.input('password', type='password') }}</dd>
-    </dl>
-    <p>{{ forms.textarea('comment') }}</p>
-
-
-Alternatively you can import names from the template into the current
-namespace::
-
-    {% from 'forms.html' import input as input_field, textarea %}
-    <dl>
-        <dt>Username</dt>
-        <dd>{{ input_field('username') }}</dd>
-        <dt>Password</dt>
-        <dd>{{ input_field('password', type='password') }}</dd>
-    </dl>
-    <p>{{ textarea('comment') }}</p>
-
-Macros and variables starting with one ore more underscores are private and
-cannot be imported.
-
-.. versionchanged:: 2.4
-   If a template object was passed to the template context you can
-   import from that object.
-
-
-.. _import-visibility:
-
-Import Context Behavior
------------------------
-
-Per default included templates are passed the current context and imported
-templates not.  The reason for this is that imports unlike includes are
-cached as imports are often used just as a module that holds macros.
-
-This however can be changed of course explicitly.  By adding `with context`
-or `without context` to the import/include directive the current context
-can be passed to the template and caching is disabled automatically.
-
-Here two examples::
-
-    {% from 'forms.html' import input with context %}
-    {% include 'header.html' without context %}
-
-.. admonition:: Note
-
-    In Jinja 2.0 the context that was passed to the included template
-    did not include variables defined in the template.  As a matter of
-    fact this did not work::
-
-        {% for box in boxes %}
-            {% include "render_box.html" %}
-        {% endfor %}
-
-    The included template ``render_box.html`` is not able to access
-    `box` in Jinja 2.0, but in Jinja 2.1.
-
-
-.. _expressions:
-
-Expressions
------------
-
-Jinja allows basic expressions everywhere.  These work very similar to regular
-Python and even if you're not working with Python you should feel comfortable
-with it.
-
-Literals
-~~~~~~~~
-
-The simplest form of expressions are literals.  Literals are representations
-for Python objects such as strings and numbers.  The following literals exist:
-
-"Hello World":
-    Everything between two double or single quotes is a string.  They are
-    useful whenever you need a string in the template (for example as
-    arguments to function calls, filters or just to extend or include a
-    template).
-
-42 / 42.23:
-    Integers and floating point numbers are created by just writing the
-    number down.  If a dot is present the number is a float, otherwise an
-    integer.  Keep in mind that for Python ``42`` and ``42.0`` is something
-    different.
-
-['list', 'of', 'objects']:
-    Everything between two brackets is a list.  Lists are useful to store
-    sequential data in or to iterate over them.  For example you can easily
-    create a list of links using lists and tuples with a for loop::
-
-        <ul>
-        {% for href, caption in [('index.html', 'Index'), ('about.html', 'About'),
-                                 ('downloads.html', 'Downloads')] %}
-            <li><a href="{{ href }}">{{ caption }}</a></li>
-        {% endfor %}
-        </ul>
-
-('tuple', 'of', 'values'):
-    Tuples are like lists, just that you can't modify them.  If the tuple
-    only has one item you have to end it with a comma.  Tuples are usually
-    used to represent items of two or more elements.  See the example above
-    for more details.
-
-{'dict': 'of', 'key': 'and', 'value': 'pairs'}:
-    A dict in Python is a structure that combines keys and values.  Keys must
-    be unique and always have exactly one value.  Dicts are rarely used in
-    templates, they are useful in some rare cases such as the :func:`xmlattr`
-    filter.
-
-true / false:
-    true is always true and false is always false.
-
-.. admonition:: Note
-
-    The special constants `true`, `false` and `none` are indeed lowercase.
-    Because that caused confusion in the past, when writing `True` expands
-    to an undefined variable that is considered false, all three of them can
-    be written in title case too (`True`, `False`, and `None`).  However for
-    consistency (all Jinja identifiers are lowercase) you should use the
-    lowercase versions.
-
-Math
-~~~~
-
-Jinja allows you to calculate with values.  This is rarely useful in templates
-but exists for completeness' sake.  The following operators are supported:
-
-\+
-    Adds two objects together.  Usually the objects are numbers but if both are
-    strings or lists you can concatenate them this way.  This however is not
-    the preferred way to concatenate strings!  For string concatenation have
-    a look at the ``~`` operator.  ``{{ 1 + 1 }}`` is ``2``.
-
-\-
-    Substract the second number from the first one.  ``{{ 3 - 2 }}`` is ``1``.
-
-/
-    Divide two numbers.  The return value will be a floating point number.
-    ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``.
-
-//
-    Divide two numbers and return the truncated integer result.
-    ``{{ 20 / 7 }}`` is ``2``.
-
-%
-    Calculate the remainder of an integer division.  ``{{ 11 % 7 }}`` is ``4``.
-
-\*
-    Multiply the left operand with the right one.  ``{{ 2 * 2 }}`` would
-    return ``4``.  This can also be used to repeat a string multiple times.
-    ``{{ '=' * 80 }}`` would print a bar of 80 equal signs.
-
-\**
-    Raise the left operand to the power of the right operand.  ``{{ 2**3 }}``
-    would return ``8``.
-
-Logic
-~~~~~
-
-For `if` statements, `for` filtering or `if` expressions it can be useful to
-combine multiple expressions:
-
-and
-    Return true if the left and the right operand is true.
-
-or
-    Return true if the left or the right operand is true.
-
-not
-    negate a statement (see below).
-
-(expr)
-    group an expression.
-
-.. admonition:: Note
-
-    The ``is`` and ``in`` operators support negation using an infix notation
-    too: ``foo is not bar`` and ``foo not in bar`` instead of ``not foo is bar``
-    and ``not foo in bar``.  All other expressions require a prefix notation:
-    ``not (foo and bar).``
-
-
-Other Operators
-~~~~~~~~~~~~~~~
-
-The following operators are very useful but don't fit into any of the other
-two categories:
-
-in
-    Perform sequence / mapping containment test.  Returns true if the left
-    operand is contained in the right.  ``{{ 1 in [1, 2, 3] }}`` would for
-    example return true.
-
-is
-    Performs a :ref:`test <tests>`.
-
-\|
-    Applies a :ref:`filter <filters>`.
-
-~
-    Converts all operands into strings and concatenates them.
-    ``{{ "Hello " ~ name ~ "!" }}`` would return (assuming `name` is
-    ``'John'``) ``Hello John!``.
-
-()
-    Call a callable: ``{{ post.render() }}``.  Inside of the parentheses you
-    can use positional arguments and keyword arguments like in python:
-    ``{{ post.render(user, full=true) }}``.
-
-. / []
-    Get an attribute of an object.  (See :ref:`variables`)
-
-
-.. _if-expression:
-
-If Expression
-~~~~~~~~~~~~~
-
-It is also possible to use inline `if` expressions.  These are useful in some
-situations.  For example you can use this to extend from one template if a
-variable is defined, otherwise from the default layout template::
-
-    {% extends layout_template if layout_template is defined else 'master.html' %}
-
-The general syntax is ``<do something> if <something is true> else <do
-something else>``.
-
-The `else` part is optional.  If not provided the else block implicitly
-evaluates into an undefined object::
-
-    {{ '[%s]' % page.title if page.title }}
-
-
-.. _builtin-filters:
-
-List of Builtin Filters
------------------------
-
-.. jinjafilters::
-
-
-.. _builtin-tests:
-
-List of Builtin Tests
----------------------
-
-.. jinjatests::
-
-.. _builtin-globals:
-
-List of Global Functions
-------------------------
-
-The following functions are available in the global scope by default:
-
-.. function:: range([start,] stop[, step])
-
-    Return a list containing an arithmetic progression of integers.
-    range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
-    When step is given, it specifies the increment (or decrement).
-    For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
-    These are exactly the valid indices for a list of 4 elements.
-
-    This is useful to repeat a template block multiple times for example
-    to fill a list.  Imagine you have 7 users in the list but you want to
-    render three empty items to enforce a height with CSS::
-
-        <ul>
-        {% for user in users %}
-            <li>{{ user.username }}</li>
-        {% endfor %}
-        {% for number in range(10 - users|count) %}
-            <li class="empty"><span>...</span></li>
-        {% endfor %}
-        </ul>
-
-.. function:: lipsum(n=5, html=True, min=20, max=100)
-
-    Generates some lorem ipsum for the template.  Per default five paragraphs
-    with HTML are generated each paragraph between 20 and 100 words.  If html
-    is disabled regular text is returned.  This is useful to generate simple
-    contents for layout testing.
-
-.. function:: dict(\**items)
-
-    A convenient alternative to dict literals.  ``{'foo': 'bar'}`` is the same
-    as ``dict(foo='bar')``.
-
-.. class:: cycler(\*items)
-
-    The cycler allows you to cycle among values similar to how `loop.cycle`
-    works.  Unlike `loop.cycle` however you can use this cycler outside of
-    loops or over multiple loops.
-
-    This is for example very useful if you want to show a list of folders and
-    files, with the folders on top, but both in the same list with alternating
-    row colors.
-
-    The following example shows how `cycler` can be used::
-
-        {% set row_class = cycler('odd', 'even') %}
-        <ul class="browser">
-        {% for folder in folders %}
-          <li class="folder {{ row_class.next() }}">{{ folder|e }}</li>
-        {% endfor %}
-        {% for filename in files %}
-          <li class="file {{ row_class.next() }}">{{ filename|e }}</li>
-        {% endfor %}
-        </ul>
-
-    A cycler has the following attributes and methods:
-
-    .. method:: reset()
-
-        Resets the cycle to the first item.
-
-    .. method:: next()
-
-        Goes one item a head and returns the then current item.
-
-    .. attribute:: current
-
-        Returns the current item.
-    
-    **new in Jinja 2.1**
-
-.. class:: joiner(sep=', ')
-
-    A tiny helper that can be use to "join" multiple sections.  A joiner is
-    passed a string and will return that string every time it's calld, except
-    the first time in which situation it returns an empty string.  You can
-    use this to join things::
-
-        {% set pipe = joiner("|") %}
-        {% if categories %} {{ pipe() }}
-            Categories: {{ categories|join(", ") }}
-        {% endif %}
-        {% if author %} {{ pipe() }}
-            Author: {{ author() }}
-        {% endif %}
-        {% if can_edit %} {{ pipe() }}
-            <a href="?action=edit">Edit</a>
-        {% endif %}
-
-    **new in Jinja 2.1**
-
-
-Extensions
-----------
-
-The following sections cover the built-in Jinja2 extensions that may be
-enabled by the application.  The application could also provide further
-extensions not covered by this documentation.  In that case there should
-be a separate document explaining the extensions.
-
-.. _i18n-in-templates:
-
-i18n
-~~~~
-
-If the i18n extension is enabled it's possible to mark parts in the template
-as translatable.  To mark a section as translatable you can use `trans`::
-
-    <p>{% trans %}Hello {{ user }}!{% endtrans %}</p>
-
-To translate a template expression --- say, using template filters or just
-accessing an attribute of an object --- you need to bind the expression to a
-name for use within the translation block::
-
-    <p>{% trans user=user.username %}Hello {{ user }}!{% endtrans %}</p>
-
-If you need to bind more than one expression inside a `trans` tag, separate
-the pieces with a comma (``,``)::
-
-    {% trans book_title=book.title, author=author.name %}
-    This is {{ book_title }} by {{ author }}
-    {% endtrans %}
-
-Inside trans tags no statements are allowed, only variable tags are.
-
-To pluralize, specify both the singular and plural forms with the `pluralize`
-tag, which appears between `trans` and `endtrans`::
-
-    {% trans count=list|length %}
-    There is {{ count }} {{ name }} object.
-    {% pluralize %}
-    There are {{ count }} {{ name }} objects.
-    {% endtrans %}
-
-Per default the first variable in a block is used to determine the correct
-singular or plural form.  If that doesn't work out you can specify the name
-which should be used for pluralizing by adding it as parameter to `pluralize`::
-
-    {% trans ..., user_count=users|length %}...
-    {% pluralize user_count %}...{% endtrans %}
-
-It's also possible to translate strings in expressions.  For that purpose
-three functions exist:
-
-_   `gettext`: translate a single string
--   `ngettext`: translate a pluralizable string
--   `_`: alias for `gettext`
-
-For example you can print a translated string easily this way::
-
-    {{ _('Hello World!') }}
-
-To use placeholders you can use the `format` filter::
-
-    {{ _('Hello %(user)s!')|format(user=user.username) }}
-        or
-    {{ _('Hello %s')|format(user.username) }}
-
-For multiple placeholders always use keyword arguments to `format` as other
-languages may not use the words in the same order.
-
-
-Expression Statement
-~~~~~~~~~~~~~~~~~~~~
-
-If the expression-statement extension is loaded a tag called `do` is available
-that works exactly like the regular variable expression (``{{ ... }}``) just
-that it doesn't print anything.  This can be used to modify lists::
-
-    {% do navigation.append('a string') %}
-
-
-Loop Controls
-~~~~~~~~~~~~~
-
-If the application enables the :ref:`loopcontrols-extension` it's possible to
-use `break` and `continue` in loops.  When `break` is reached, the loop is
-terminated, if `continue` is eached the processing is stopped and continues
-with the next iteration.
-
-Here a loop that skips every second item::
-
-    {% for user in users %}
-        {%- if loop.index is even %}{% continue %}{% endif %}
-        ...
-    {% endfor %}
-
-Likewise a look that stops processing after the 10th iteration::
-
-    {% for user in users %}
-        {%- if loop.index >= 10 %}{% break %}{% endif %}
-    {%- endfor %}
-
-
-With Statement
-~~~~~~~~~~~~~~
-
-.. versionadded:: 2.3
-
-If the application enables the :ref:`with-extension` it is possible to
-use the `with` keyword in templates.  This makes it possible to create
-a new inner scope.  Variables set within this scope are not visible
-outside of the scope.
-
-With in a nutshell::
-
-    {% with %}
-        {% set foo = 42 %}
-        {{ foo }}           foo is 42 here
-    {% endwith %}
-    foo is not visible here any longer
-
-Because it is common to set variables at the beginning of the scope
-you can do that within the with statement.  The following two examples
-are equivalent::
-
-    {% with foo = 42 %}
-        {{ foo }}
-    {% endwith %}
-
-    {% with %}
-        {% set foo = 42 %}
-        {{ foo }}
-    {% endwith %}
--- a/bundled/jinja2/docs/tricks.rst	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-Tips and Tricks
-===============
-
-.. highlight:: html+jinja
-
-This part of the documentation shows some tips and tricks for Jinja2
-templates.
-
-
-.. _null-master-fallback:
-
-Null-Master Fallback
---------------------
-
-Jinja2 supports dynamic inheritance and does not distinguish between parent
-and child template as long as no `extends` tag is visited.  While this leads
-to the surprising behavior that everything before the first `extends` tag
-including whitespace is printed out instead of being igored, it can be used
-for a neat trick.
-
-Usually child templates extend from one template that adds a basic HTML
-skeleton.  However it's possible put the `extends` tag into an `if` tag to
-only extend from the layout template if the `standalone` variable evaluates
-to false which it does per default if it's not defined.  Additionally a very
-basic skeleton is added to the file so that if it's indeed rendered with
-`standalone` set to `True` a very basic HTML skeleton is added::
-
-    {% if not standalone %}{% extends 'master.html' %}{% endif -%}
-    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-    <title>{% block title %}The Page Title{% endblock %}</title>
-    <link rel="stylesheet" href="style.css" type="text/css">
-    {% block body %}
-      <p>This is the page body.</p>
-    {% endblock %}
-
-
-Alternating Rows
-----------------
-
-If you want to have different styles for each row of a table or
-list you can use the `cycle` method on the `loop` object::
-
-    <ul>
-    {% for row in rows %}
-      <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
-    {% endfor %}
-    </ul>
-
-`cycle` can take an unlimited amount of strings.  Each time this
-tag is encountered the next item from the list is rendered.
-
-
-Highlighting Active Menu Items
-------------------------------
-
-Often you want to have a navigation bar with an active navigation
-item.  This is really simple to achieve.  Because assignments outside
-of `block`\s in child templates are global and executed before the layout
-template is evaluated it's possible to define the active menu item in the
-child template::
-
-    {% extends "layout.html" %}
-    {% set active_page = "index" %}
-
-The layout template can then access `active_page`.  Additionally it makes
-sense to defined a default for that variable::
-
-    {% set navigation_bar = [
-        ('/', 'index', 'Index'),
-        ('/downloads/', 'downloads', 'Downloads'),
-        ('/about/', 'about', 'About')
-    ] -%}
-    {% set active_page = active_page|default('index') -%}
-    ...
-    <ul id="navigation">
-    {% for href, id, caption in navigation_bar %}
-      <li{% if id == active_page %} class="active"{% endif
-      %}><a href="{{ href|e }}">{{ caption|e }}</a>/li>
-    {% endfor %}
-    </ul>
-    ...
-
-.. _accessing-the-parent-loop:
-
-Accessing the parent Loop
--------------------------
-
-The special `loop` variable always points to the innermost loop.  If it's
-desired to have access to an outer loop it's possible to alias it::
-
-    <table>
-    {% for row in table %}
-      <tr>
-      {% set rowloop = loop %}
-      {% for cell in row %}
-        <td id="cell-{{ rowloop.index }}-{{ loop.index }}>{{ cell }}</td>
-      {% endfor %}
-      </tr>
-    {% endfor %}
-    </table>
--- a/bundled/jinja2/examples/basic/cycle.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-from jinja2 import Environment
-
-
-env = Environment(line_statement_prefix="#", variable_start_string="${", variable_end_string="}")
-
-
-print env.from_string("""\
-<ul>
-# for item in range(10)
-    <li class="${loop.cycle('odd', 'even')}">${item}</li>
-# endfor
-</ul>\
-""").render()
--- a/bundled/jinja2/examples/basic/debugger.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-from jinja2 import Environment
-from jinja2.loaders import FileSystemLoader
-
-env = Environment(loader=FileSystemLoader('templates'))
-
-tmpl = env.get_template('broken.html')
-print tmpl.render(seq=[3, 2, 4, 5, 3, 2, 0, 2, 1])
--- a/bundled/jinja2/examples/basic/inheritance.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-from jinja2 import Environment
-from jinja2.loaders import DictLoader
-
-
-env = Environment(loader=DictLoader({
-'a': '''[A[{% block body %}{% endblock %}]]''',
-'b': '''{% extends 'a' %}{% block body %}[B]{% endblock %}''',
-'c': '''{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}'''
-}))
-
-
-print env.get_template('c').render()
--- a/bundled/jinja2/examples/basic/templates/broken.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-{% from 'subbroken.html' import may_break %}
-<ul>
-{% for item in seq %}
-  <li>{{ may_break(item) }}</li>
-{% endfor %}
-</ul>
--- a/bundled/jinja2/examples/basic/templates/subbroken.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-{% macro may_break(item) -%}
-  [{{ item / 0 }}]
-{%- endmacro %}
--- a/bundled/jinja2/examples/basic/test.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-from jinja2 import Environment
-from jinja2.loaders import DictLoader
-
-env = Environment(loader=DictLoader({
-'child.html': u'''\
-{% extends master_layout or 'master.html' %}
-{% include helpers = 'helpers.html' %}
-{% macro get_the_answer() %}42{% endmacro %}
-{% title = 'Hello World' %}
-{% block body %}
-    {{ get_the_answer() }}
-    {{ helpers.conspirate() }}
-{% endblock %}
-''',
-'master.html': u'''\
-<!doctype html>
-<title>{{ title }}</title>
-{% block body %}{% endblock %}
-''',
-'helpers.html': u'''\
-{% macro conspirate() %}23{% endmacro %}
-'''
-}))
-
-
-tmpl = env.get_template("child.html")
-print tmpl.render()
--- a/bundled/jinja2/examples/basic/test_filter_and_linestatements.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-from jinja2 import Environment
-
-
-env = Environment(line_statement_prefix='%', variable_start_string="${", variable_end_string="}")
-tmpl = env.from_string("""\
-% macro foo()
-    ${caller(42)}
-% endmacro
-<ul>
-% for item in seq
-    <li>${item}</li>
-% endfor
-</ul>
-% call(var) foo()
-    [${var}]
-% endcall
-% filter escape
-    <hello world>
-    % for item in [1, 2, 3]
-      -  ${item}
-    % endfor
-% endfilter
-""")
-
-print tmpl.render(seq=range(10))
--- a/bundled/jinja2/examples/basic/test_loop_filter.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-from jinja2 import Environment
-
-tmpl = Environment().from_string("""\
-<ul>
-{%- for item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] if item % 2 == 0 %}
-    <li>{{ loop.index }} / {{ loop.length }}: {{ item }}</li>
-{%- endfor %}
-</ul>
-if condition: {{ 1 if foo else 0 }}
-""")
-
-print tmpl.render(foo=True)
--- a/bundled/jinja2/examples/basic/translate.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-from jinja2 import Environment
-
-print Environment(extensions=['jinja2.i18n.TransExtension']).from_string("""\
-{% trans %}Hello {{ user }}!{% endtrans %}
-{% trans count=users|count %}{{ count }} user{% pluralize %}{{ count }} users{% endtrans %}
-""").render(user="someone")
--- a/bundled/jinja2/examples/bench.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,361 +0,0 @@
-"""\
-    This benchmark compares some python templating engines with Jinja 2 so
-    that we get a picture of how fast Jinja 2 is for a semi real world
-    template.  If a template engine is not installed the test is skipped.\
-"""
-import sys
-import cgi
-from timeit import Timer
-from jinja2 import Environment as JinjaEnvironment
-
-context = {
-    'page_title': 'mitsuhiko\'s benchmark',
-    'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)]
-}
-
-jinja_template = JinjaEnvironment(
-    line_statement_prefix='%',
-    variable_start_string="${",
-    variable_end_string="}"
-).from_string("""\
-<!doctype html>
-<html>
-  <head>
-    <title>${page_title|e}</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>${page_title|e}</h1>
-    </div>
-    <ul class="navigation">
-    % for href, caption in [
-        ('index.html', 'Index'),
-        ('downloads.html', 'Downloads'),
-        ('products.html', 'Products')
-      ]
-      <li><a href="${href|e}">${caption|e}</a></li>
-    % endfor
-    </ul>
-    <div class="table">
-      <table>
-      % for row in table
-        <tr>
-        % for cell in row
-          <td>${cell}</td>
-        % endfor
-        </tr>
-      % endfor
-      </table>
-    </div>
-  </body>
-</html>\
-""")
-
-def test_jinja():
-    jinja_template.render(context)
-
-try:
-    from tornado.template import Template
-except ImportError:
-    test_tornado = None
-else:
-    tornado_template = Template("""\
-<!doctype html>
-<html>
-  <head>
-    <title>{{ page_title }}</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>{{ page_title }}</h1>
-    </div>
-    <ul class="navigation">
-    {% for href, caption in [ \
-        ('index.html', 'Index'), \
-        ('downloads.html', 'Downloads'), \
-        ('products.html', 'Products') \
-      ] %}
-      <li><a href="{{ href }}">{{ caption }}</a></li>
-    {% end %}
-    </ul>
-    <div class="table">
-      <table>
-      {% for row in table %}
-        <tr>
-        {% for cell in row %}
-          <td>{{ cell }}</td>
-        {% end %}
-        </tr>
-      {% end %}
-      </table>
-    </div>
-  </body>
-</html>\
-""")
-
-    def test_tornado():
-        tornado_template.generate(**context)
-
-try:
-    from django.conf import settings
-    settings.configure()
-    from django.template import Template as DjangoTemplate, Context as DjangoContext
-except ImportError:
-    test_django = None
-else:
-    django_template = """\
-<!doctype html>
-<html>
-  <head>
-    <title>{{ page_title }}</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>{{ page_title }}</h1>
-    </div>
-    <ul class="navigation">
-    {% for href, caption in navigation %}
-      <li><a href="{{ href }}">{{ caption }}</a></li>
-    {% endfor %}
-    </ul>
-    <div class="table">
-      <table>
-      {% for row in table %}
-        <tr>
-        {% for cell in row %}
-          <td>{{ cell }}</td>
-        {% endfor %}
-        </tr>
-      {% endfor %}
-      </table>
-    </div>
-  </body>
-</html>\
-"""
-
-    def test_django():
-        c = DjangoContext(context)
-        c['navigation'] = [('index.html', 'Index'), ('downloads.html', 'Downloads'),
-                           ('products.html', 'Products')]
-        # recompile template each rendering because that's what django
-        # is doing in normal situations too.  Django is not thread safe
-        # so we can't cache it in regular apps either.
-        DjangoTemplate(django_template).render(c)
-
-try:
-    from mako.template import Template as MakoTemplate
-except ImportError:
-    test_mako = None
-else:
-    mako_template = MakoTemplate("""\
-<!doctype html>
-<html>
-  <head>
-    <title>${page_title|h}</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>${page_title|h}</h1>
-    </div>
-    <ul class="navigation">
-    % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
-      <li><a href="${href|h}">${caption|h}</a></li>
-    % endfor
-    </ul>
-    <div class="table">
-      <table>
-      % for row in table:
-        <tr>
-        % for cell in row:
-          <td>${cell}</td>
-        % endfor
-        </tr>
-      % endfor
-      </table>
-    </div>
-  </body>
-</html>\
-""")
-
-    def test_mako():
-        mako_template.render(**context)
-
-try:
-    from genshi.template import MarkupTemplate as GenshiTemplate
-except ImportError:
-    test_genshi = None
-else:
-    genshi_template = GenshiTemplate("""\
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
-  <head>
-    <title>${page_title}</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>${page_title}</h1>
-    </div>
-    <ul class="navigation">
-      <li py:for="href, caption in [
-        ('index.html', 'Index'),
-        ('downloads.html', 'Downloads'),
-        ('products.html', 'Products')]"><a href="${href}">${caption}</a></li>
-    </ul>
-    <div class="table">
-      <table>
-        <tr py:for="row in table">
-          <td py:for="cell in row">${cell}</td>
-        </tr>
-      </table>
-    </div>
-  </body>
-</html>\
-""")
-
-    def test_genshi():
-        genshi_template.generate(**context).render('html', strip_whitespace=False)
-
-try:
-    from Cheetah.Template import Template as CheetahTemplate
-except ImportError:
-    test_cheetah = None
-else:
-    cheetah_template = CheetahTemplate("""\
-#import cgi
-<!doctype html>
-<html>
-  <head>
-    <title>$cgi.escape($page_title)</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>$cgi.escape($page_title)</h1>
-    </div>
-    <ul class="navigation">
-    #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
-      <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
-    #end for
-    </ul>
-    <div class="table">
-      <table>
-      #for $row in $table:
-        <tr>
-        #for $cell in $row:
-          <td>$cell</td>
-        #end for
-        </tr>
-      #end for
-      </table>
-    </div>
-  </body>
-</html>\
-""", searchList=[dict(context)])
-
-    def test_cheetah():
-        unicode(cheetah_template)
-
-try:
-    import tenjin
-except ImportError:
-    test_tenjin = None
-else:
-    tenjin_template = tenjin.Template()
-    tenjin_template.convert("""\
-<!doctype html>
-<html>
-  <head>
-    <title>${page_title}</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>${page_title}</h1>
-    </div>
-    <ul class="navigation">
-<?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: ?>
-      <li><a href="${href}">${caption}</a></li>
-<?py #end ?>
-    </ul>
-    <div class="table">
-      <table>
-<?py for row in table: ?>
-        <tr>
-<?py     for cell in row: ?>
-          <td>#{cell}</td>
-<?py #end ?>
-        </tr>
-<?py #end ?>
-      </table>
-    </div>
-  </body>
-</html>\
-""")
-
-    def test_tenjin():
-        from tenjin.helpers import escape, to_str
-        tenjin_template.render(context, locals())
-
-try:
-    from spitfire.compiler import util as SpitfireTemplate
-    from spitfire.compiler.analyzer import o2_options as spitfire_optimizer
-except ImportError:
-    test_spitfire = None
-else:
-    spitfire_template = SpitfireTemplate.load_template("""\
-<!doctype html>
-<html>
-  <head>
-    <title>$cgi.escape($page_title)</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>$cgi.escape($page_title)</h1>
-    </div>
-    <ul class="navigation">
-    #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]
-      <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
-    #end for
-    </ul>
-    <div class="table">
-      <table>
-      #for $row in $table
-        <tr>
-        #for $cell in $row
-          <td>$cell</td>
-        #end for
-        </tr>
-      #end for
-      </table>
-    </div>
-  </body>
-</html>\
-""", 'spitfire_tmpl', spitfire_optimizer, {'enable_filters': False})
-    spitfire_context = dict(context, **{'cgi': cgi})
-
-    def test_spitfire():
-        spitfire_template(search_list=[spitfire_context]).main()
-
-sys.stdout.write('\r' + '\n'.join((
-    '=' * 80,
-    'Template Engine BigTable Benchmark'.center(80),
-    '=' * 80,
-    __doc__,
-    '-' * 80
-)) + '\n')
-
-
-for test in 'jinja', 'mako', 'tornado', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah':
-    if locals()['test_' + test] is None:
-        sys.stdout.write('    %-20s*not installed*\n' % test)
-        continue
-    t = Timer(setup='from __main__ import test_%s as bench' % test,
-              stmt='bench()')
-    sys.stdout.write(' >> %-20s<running>' % test)
-    sys.stdout.flush()
-    sys.stdout.write('\r    %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50))
-sys.stdout.write('-' * 80 + '\n')
-sys.stdout.write('''\
-    WARNING: The results of this benchmark are useless to compare the
-    performance of template engines and should not be taken seriously in any
-    way.  It's testing the performance of simple loops and has no real-world
-    usefulnes.  It only used to check if changes on the Jinja code affect
-    performance in a good or bad way and how it roughly compares to others.
-''' + '=' * 80 + '\n')
--- a/bundled/jinja2/examples/profile.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-try:
-    from cProfile import Profile
-except ImportError:
-    from profile import Profile
-from pstats import Stats
-from jinja2 import Environment as JinjaEnvironment
-
-context = {
-    'page_title': 'mitsuhiko\'s benchmark',
-    'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)]
-}
-
-source = """\
-<!doctype html>
-<html>
-  <head>
-    <title>${page_title|e}</title>
-  </head>
-  <body>
-    <div class="header">
-      <h1>${page_title|e}</h1>
-    </div>
-    <div class="table">
-      <table>
-      % for row in table
-        <tr>
-        % for cell in row
-          <td>${cell}</td>
-        % endfor
-        </tr>
-      % endfor
-      </table>
-    </div>
-  </body>
-</html>\
-"""
-jinja_template = JinjaEnvironment(
-    line_statement_prefix='%',
-    variable_start_string="${",
-    variable_end_string="}"
-).from_string(source)
-print jinja_template.environment.compile(source, raw=True)
-
-
-p = Profile()
-p.runcall(lambda: jinja_template.render(context))
-stats = Stats(p)
-stats.sort_stats('time', 'calls')
-stats.print_stats()
--- a/bundled/jinja2/examples/rwbench/django/_form.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-<form action="{{ action }}" method="{{ method }}">{{ body }}</form>
--- a/bundled/jinja2/examples/rwbench/django/_input_field.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-<input type="{{ type }}" value="{{ value }}" name="{{ name }}">
--- a/bundled/jinja2/examples/rwbench/django/_textarea.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-<textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ value }}</textarea>
--- a/bundled/jinja2/examples/rwbench/django/index.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-{% extends "layout.html" %}
-{% block page_title %}Index Page{% endblock %}
-{% block body %}
-  {% for article in articles %}
-  {% if article.published %}
-  <div class="article">
-    <h2><a href="{{ article.href }}">{{ article.title }}</a></h2>
-    <p class="meta">written by <a href="{{ article.user.href }}">{{ article.user.username }}</a> on {{ article.pub_date|dateformat }}</p>
-    <div class="text">{{ article.body|safe }}</div>
-  </div>
-  {% endif %}
-  {% endfor %}
-  {% form %}
-    <dl>
-      <dt>Name</dt>
-      <dd>{% input_field 'name' %}</dd>
-      <dt>E-Mail</dt>
-      <dd>{% input_field 'email' %}</dd>
-      <dt>URL</dt>
-      <dd>{% input_field 'url' %}</dd>
-      <dt>Comment</dt>
-      <dd>{% textarea 'comment' %}</dd>
-      <dt>Captcha</dt>
-      <dd>{% input_field 'captcha' %}</dd>
-    </dl>
-    {% input_field '' 'submit' 'Submit' %}
-    {% input_field 'cancel' 'submit' 'Cancel' %}
-  {% endform %}
-{% endblock %}
--- a/bundled/jinja2/examples/rwbench/django/layout.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-<head>
-  <title>{% block page_title %}{% endblock %} | RealWorld Benchmark</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-</head>
-<body>
-  <div class="contents">
-    <div class="header">
-      <h1>RealWorld Benchmark</h1>
-      <blockquote><p>
-        A less stupid benchmark for Mako and Jinja2 to get an impression how
-        code changes affect runtime performance.
-      </p></blockquote>
-    </div>
-    <ul class="navigation">
-    {% for href, caption in page_navigation %}
-      <li><a href="{{ href }}">{{ caption }}</a></li>
-    {% endfor %}
-    </ul>
-    <div class="body">
-      {% block body %}{% endblock %}
-    </div>
-    <div class="footer">
-      &copy; Copyright 2008 by I don't know who.
-    </div>
-  </div>
-</body>
-</html>
--- a/bundled/jinja2/examples/rwbench/djangoext.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +0,0 @@
-# -*- coding: utf-8 -*-
-from rwbench import ROOT
-from os.path import join
-from django.conf import settings
-settings.configure(TEMPLATE_DIRS=(join(ROOT, 'django'),))
-from django.template import loader as django_loader, Context as DjangoContext, \
-     Node, NodeList, Variable, TokenParser
-from django import template as django_template_module
-from django.template import Library
-
-
-# for django extensions.  We monkey patch our extensions in so that
-# we don't have to initialize a more complex django setup.
-django_extensions = django_template_module.Library()
-django_template_module.builtins.append(django_extensions)
-
-
-from rwbench import dateformat
-django_extensions.filter(dateformat)
-
-
-def var_or_none(x):
-    if x is not None:
-        return Variable(x)
-
-
-# and more django extensions
-@django_extensions.tag
-def input_field(parser, token):
-    p = TokenParser(token.contents)
-    args = [p.value()]
-    while p.more():
-        args.append(p.value())
-    return InputFieldNode(*args)
-
-
-@django_extensions.tag
-def textarea(parser, token):
-    p = TokenParser(token.contents)
-    args = [p.value()]
-    while p.more():
-        args.append(p.value())
-    return TextareaNode(*args)
-
-
-@django_extensions.tag
-def form(parser, token):
-    p = TokenParser(token.contents)
-    args = []
-    while p.more():
-        args.append(p.value())
-    body = parser.parse(('endform',))
-    parser.delete_first_token()
-    return FormNode(body, *args)
-
-
-class InputFieldNode(Node):
-
-    def __init__(self, name, type=None, value=None):
-        self.name = var_or_none(name)
-        self.type = var_or_none(type)
-        self.value = var_or_none(value)
-
-    def render(self, context):
-        name = self.name.resolve(context)
-        type = 'text'
-        value = ''
-        if self.type is not None:
-            type = self.type.resolve(context)
-        if self.value is not None:
-            value = self.value.resolve(context)
-        tmpl = django_loader.get_template('_input_field.html')
-        return tmpl.render(DjangoContext({
-            'name':     name,
-            'type':     type,
-            'value':    value
-        }))
-
-
-class TextareaNode(Node):
-
-    def __init__(self, name, rows=None, cols=None, value=None):
-        self.name = var_or_none(name)
-        self.rows = var_or_none(rows)
-        self.cols = var_or_none(cols)
-        self.value = var_or_none(value)
-
-    def render(self, context):
-        name = self.name.resolve(context)
-        rows = 10
-        cols = 40
-        value = ''
-        if self.rows is not None:
-            rows = int(self.rows.resolve(context))
-        if self.cols is not None:
-            cols = int(self.cols.resolve(context))
-        if self.value is not None:
-            value = self.value.resolve(context)
-        tmpl = django_loader.get_template('_textarea.html')
-        return tmpl.render(DjangoContext({
-            'name':     name,
-            'rows':     rows,
-            'cols':     cols,
-            'value':    value
-        }))
-
-
-class FormNode(Node):
-
-    def __init__(self, body, action=None, method=None):
-        self.body = body
-        self.action = action
-        self.method = method
-
-    def render(self, context):
-        body = self.body.render(context)
-        action = ''
-        method = 'post'
-        if self.action is not None:
-            action = self.action.resolve(context)
-        if self.method is not None:
-            method = self.method.resolve(context)
-        tmpl = django_loader.get_template('_form.html')
-        return tmpl.render(DjangoContext({
-            'body':     body,
-            'action':   action,
-            'method':   method
-        }))
--- a/bundled/jinja2/examples/rwbench/genshi/helpers.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-<div xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/"
-     py:strip="">
-
-  <py:def function="input_field(name='', value='', type='text')">
-    <input type="$type" value="$value" name="$name" />
-  </py:def>
-
-  <py:def function="textarea(name, value='', rows=10, cols=40)">
-    <textarea name="$name" rows="$rows" cols="cols">$value</textarea>
-  </py:def>
-
-</div>
--- a/bundled/jinja2/examples/rwbench/genshi/index.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-<?python
-  from rwbench import dateformat
-?>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/">
-  <xi:include href="layout.html" />
-  <xi:include href="helpers.html" />
-  <head><title>Index Page</title></head>
-  <body>
-    <div class="article" py:for="article in articles">
-      <py:if test="article.published">
-        <h2><a href="${article.href}">${article.title}</a></h2>
-        <p class="meta">written by <a href="${article.user.href}"
-          >${article.user.username}</a> on ${dateformat(article.pub_date)}</p>
-        <div class="text">${Markup(article.body)}</div>
-      </py:if>
-    </div>
-    <!--
-      For a fair and balanced comparison we would have to use a def here
-      that wraps the form data but I don't know what would be the best
-      Genshi equivalent for that.  Quite frankly I doubt that this makes
-      sense in Genshi anyways.
-    -->
-    <form action="" method="post">
-      <dl>
-        <dt>Name</dt>
-        <dd>${input_field('name')}</dd>
-        <dt>E-Mail</dt>
-        <dd>${input_field('email')}</dd>
-        <dt>URL</dt>
-        <dd>${input_field('url')}</dd>
-        <dt>Comment</dt>
-        <dd>${textarea('comment')}</dd>
-        <dt>Captcha</dt>
-        <dd>${input_field('captcha')}</dd>
-      </dl>
-      ${input_field(type='submit', value='Submit')}
-      ${input_field(name='cancel', type='submit', value='Cancel')}
-    </form>
-  </body>
-</html>
--- a/bundled/jinja2/examples/rwbench/genshi/layout.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" >
-  <py:match path="head" once="true">
-    <head>
-      <title>${select('title/text()')} | RealWorld Benchmark</title>
-      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    </head>
-  </py:match>
-  <py:match path="body" once="true">
-    <body>
-      <div class="contents">
-        <div class="header">
-          <h1>RealWorld Benchmark</h1>
-          <blockquote><p>
-            A less stupid benchmark for Mako and Jinja2 to get an impression how
-            code changes affect runtime performance.
-          </p></blockquote>
-        </div>
-        <ul class="navigation">
-          <li py:for="href, caption in page_navigation"><a href="$href">$caption</a></li>
-        </ul>
-        <div class="body">
-          ${select('*|text()')}
-        </div>
-        <div class="footer">
-          &copy; Copyright 2008 by I don't know who.
-        </div>
-      </div>
-    </body>
-  </py:match>
-</html>
--- a/bundled/jinja2/examples/rwbench/jinja/helpers.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-{% macro input_field(name, value='', type='text') -%}
-  <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
-{%- endmacro %}
-
-{% macro textarea(name, value='', rows=10, cols=40) -%}
-  <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{
-    value|e }}</textarea>
-{%- endmacro %}
-
-{% macro form(action='', method='post') -%}
-  <form action="{{ action|e }}" method="{{ method }}">{{ caller() }}</form>
-{%- endmacro %}
--- a/bundled/jinja2/examples/rwbench/jinja/index.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-{% extends "layout.html" %}
-{% from "helpers.html" import input_field, textarea, form %}
-{% block page_title %}Index Page{% endblock %}
-{% block body %}
-  {%- for article in articles if article.published %}
-  <div class="article">
-    <h2><a href="{{ article.href|e }}">{{ article.title|e }}</a></h2>
-    <p class="meta">written by <a href="{{ article.user.href|e
-      }}">{{ article.user.username|e }}</a> on {{ article.pub_date|dateformat }}</p>
-    <div class="text">{{ article.body }}</div>
-  </div>
-  {%- endfor %}
-  {%- call form() %}
-    <dl>
-      <dt>Name</dt>
-      <dd>{{ input_field('name') }}</dd>
-      <dt>E-Mail</dt>
-      <dd>{{ input_field('email') }}</dd>
-      <dt>URL</dt>
-      <dd>{{ input_field('url') }}</dd>
-      <dt>Comment</dt>
-      <dd>{{ textarea('comment') }}</dd>
-      <dt>Captcha</dt>
-      <dd>{{ input_field('captcha') }}</dd>
-    </dl>
-    {{ input_field(type='submit', value='Submit') }}
-    {{ input_field('cancel', type='submit', value='Cancel') }}
-  {%- endcall %}
-{% endblock %}
--- a/bundled/jinja2/examples/rwbench/jinja/layout.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-<head>
-  <title>{% block page_title %}{% endblock %} | RealWorld Benchmark</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-</head>
-<body>
-  <div class="contents">
-    <div class="header">
-      <h1>RealWorld Benchmark</h1>
-      <blockquote><p>
-        A less stupid benchmark for Mako and Jinja2 to get an impression how
-        code changes affect runtime performance.
-      </p></blockquote>
-    </div>
-    <ul class="navigation">
-    {%- for href, caption in page_navigation %}
-      <li><a href="{{ href|e }}">{{ caption }}</a></li>
-    {%- endfor %}
-    </ul>
-    <div class="body">
-      {% block body %}{% endblock %}
-    </div>
-    <div class="footer">
-      &copy; Copyright 2008 by I don't know who.
-    </div>
-  </div>
-</body>
-</html>
--- a/bundled/jinja2/examples/rwbench/mako/helpers.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-<%def name="input_field(name='', value='', type='text')">
-  <input type="${type}" value="${value|h}" name="${name}">
-</%def>
-
-<%def name="textarea(name, value='', rows=10, cols=40)">
-  <textarea name="${name}" rows="${rows}" cols="${cols}">${value|h}</textarea>
-</%def>
-
-<%def name="form(action='', method='post')">
-  <form action="${action|h}" method="${method}">${caller.body()}</form>
-</%def>
--- a/bundled/jinja2/examples/rwbench/mako/index.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-<%!
-  from rwbench import dateformat
-%>
-<%inherit file="layout.html" />
-<%namespace file="helpers.html" import="input_field, textarea, form" />
-<%def name="page_title()">Index Page</%def>
-% for article in articles:
-  <% if not article.published: continue %>
-<div class="article">
-  <h2><a href="${article.href|h}">${article.title|h}</a></h2>
-  <p class="meta">written by <a href="${article.user.href|h
-    }">${article.user.username|h}</a> on ${dateformat(article.pub_date)}</p>
-  <div class="text">${article.body}</div>
-</div>
-% endfor
-<%call expr="form()">
-  <dl>
-    <dt>Name</dt>
-    <dd>${input_field('name')}</dd>
-    <dt>E-Mail</dt>
-    <dd>${input_field('email')}</dd>
-    <dt>URL</dt>
-    <dd>${input_field('url')}</dd>
-    <dt>Comment</dt>
-    <dd>${textarea('comment')}</dd>
-    <dt>Captcha</dt>
-    <dd>${input_field('captcha')}</dd>
-  </dl>
-  ${input_field(type='submit', value='Submit')}
-  ${input_field(name='cancel', type='submit', value='Cancel')}
-</%call>
--- a/bundled/jinja2/examples/rwbench/mako/layout.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-<head>
-  <title>${self.page_title()} | RealWorld Benchmark</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-</head>
-<body>
-  <div class="contents">
-    <div class="header">
-      <h1>RealWorld Benchmark</h1>
-      <blockquote><p>
-        A less stupid benchmark for Mako and Jinja2 to get an impression how
-        code changes affect runtime performance.
-      </p></blockquote>
-    </div>
-    <ul class="navigation">
-    % for href, caption in page_navigation:
-      <li><a href="${href|h}">${caption}</a></li>
-    % endfor
-    </ul>
-    <div class="body">
-      ${self.body()}
-    </div>
-    <div class="footer">
-      &copy; Copyright 2008 by I don't know who.
-    </div>
-  </div>
-</body>
-</html>
-<%def name="page_title()"></%def>
--- a/bundled/jinja2/examples/rwbench/rwbench.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    RealWorldish Benchmark
-    ~~~~~~~~~~~~~~~~~~~~~~
-
-    A more real-world benchmark of Jinja2.  Like the other benchmark in the
-    Jinja2 repository this has no real-world usefulnes (despite the name).
-    Just go away and ignore it.  NOW!
-
-    :copyright: (c) 2009 by the Jinja Team.
-    :license: BSD.
-"""
-import sys
-from os.path import join, dirname, abspath
-ROOT = abspath(dirname(__file__))
-
-from random import choice, randrange
-from datetime import datetime
-from timeit import Timer
-from jinja2 import Environment, FileSystemLoader
-from jinja2.utils import generate_lorem_ipsum
-from mako.lookup import TemplateLookup
-from genshi.template import TemplateLoader as GenshiTemplateLoader
-
-
-def dateformat(x):
-    return x.strftime('%Y-%m-%d')
-
-
-jinja_env = Environment(loader=FileSystemLoader(join(ROOT, 'jinja')))
-jinja_env.filters['dateformat'] = dateformat
-mako_lookup = TemplateLookup(directories=[join(ROOT, 'mako')])
-genshi_loader = GenshiTemplateLoader([join(ROOT, 'genshi')])
-
-class Article(object):
-
-    def __init__(self, id):
-        self.id = id
-        self.href = '/article/%d' % self.id
-        self.title = generate_lorem_ipsum(1, False, 5, 10)
-        self.user = choice(users)
-        self.body = generate_lorem_ipsum()
-        self.pub_date = datetime.utcfromtimestamp(randrange(10 ** 9, 2 * 10 ** 9))
-        self.published = True
-
-
-class User(object):
-
-    def __init__(self, username):
-        self.href = '/user/%s' % username
-        self.username = username
-
-
-users = map(User, [u'John Doe', u'Jane Doe', u'Peter Somewhat'])
-articles = map(Article, range(20))
-navigation = [
-    ('index',           'Index'),
-    ('about',           'About'),
-    ('foo?bar=1',       'Foo with Bar'),
-    ('foo?bar=2&s=x',   'Foo with X'),
-    ('blah',            'Blub Blah'),
-    ('hehe',            'Haha'),
-] * 5
-
-context = dict(users=users, articles=articles, page_navigation=navigation)
-
-
-jinja_template = jinja_env.get_template('index.html')
-mako_template = mako_lookup.get_template('index.html')
-genshi_template = genshi_loader.load('index.html')
-
-
-def test_jinja():
-    jinja_template.render(context)
-
-def test_mako():
-    mako_template.render_unicode(**context)
-
-
-from djangoext import django_loader, DjangoContext
-def test_django():
-    # not cached because django is not thread safe and does
-    # not cache by itself so it would be unfair to cache it here.
-    django_template = django_loader.get_template('index.html')
-    django_template.render(DjangoContext(context))
-
-
-def test_genshi():
-    genshi_template.generate(**context).render('html', doctype='html')
-
-
-if __name__ == '__main__':
-    sys.stdout.write('Realworldish Benchmark:\n')
-    for test in 'jinja', 'mako', 'django', 'genshi':
-        t = Timer(setup='from __main__ import test_%s as bench' % test,
-                  stmt='bench()')
-        sys.stdout.write(' >> %-20s<running>' % test)
-        sys.stdout.flush()
-        sys.stdout.write('\r    %-20s%.4f seconds\n' % (test, t.timeit(number=200) / 200))
--- a/bundled/jinja2/jinja2/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/__init__.py	Fri Jun 11 21:25:17 2010 -0400
@@ -38,7 +38,8 @@
 
 # loaders
 from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
-     DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader
+     DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \
+     ModuleLoader
 
 # bytecode caches
 from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
@@ -53,9 +54,11 @@
      TemplateAssertionError
 
 # decorators and public utilities
-from jinja2.filters import environmentfilter, contextfilter
+from jinja2.filters import environmentfilter, contextfilter, \
+     evalcontextfilter
 from jinja2.utils import Markup, escape, clear_caches, \
-     environmentfunction, contextfunction, is_undefined
+     environmentfunction, evalcontextfunction, contextfunction, \
+     is_undefined
 
 __all__ = [
     'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
@@ -64,6 +67,7 @@
     'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined',
     'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound',
     'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
-    'environmentfilter', 'contextfilter', 'Markup', 'escape',
-    'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined'
+    'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
+    'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
+    'evalcontextfilter', 'evalcontextfunction'
 ]
--- a/bundled/jinja2/jinja2/compiler.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/compiler.py	Fri Jun 11 21:25:17 2010 -0400
@@ -12,6 +12,7 @@
 from itertools import chain
 from copy import deepcopy
 from jinja2 import nodes
+from jinja2.nodes import EvalContext
 from jinja2.visitor import NodeVisitor, NodeTransformer
 from jinja2.exceptions import TemplateAssertionError
 from jinja2.utils import Markup, concat, escape, is_python_keyword, next
@@ -53,11 +54,12 @@
 unoptimize_before_dead_code = bool(unoptimize_before_dead_code().func_closure)
 
 
-def generate(node, environment, name, filename, stream=None):
+def generate(node, environment, name, filename, stream=None,
+             defer_init=False):
     """Generate the python source for a node tree."""
     if not isinstance(node, nodes.Template):
         raise TypeError('Can\'t compile non template nodes')
-    generator = CodeGenerator(environment, name, filename, stream)
+    generator = CodeGenerator(environment, name, filename, stream, defer_init)
     generator.visit(node)
     if stream is None:
         return generator.stream.getvalue()
@@ -140,7 +142,8 @@
 class Frame(object):
     """Holds compile time information for us."""
 
-    def __init__(self, parent=None):
+    def __init__(self, eval_ctx, parent=None):
+        self.eval_ctx = eval_ctx
         self.identifiers = Identifiers()
 
         # a toplevel frame is the root + soft frames such as if conditions.
@@ -210,7 +213,7 @@
 
     def inner(self):
         """Return an inner frame."""
-        return Frame(self)
+        return Frame(self.eval_ctx, self)
 
     def soft(self):
         """Return a soft frame.  A soft frame may not be modified as
@@ -290,8 +293,7 @@
         self.visit(node.test)
         real_identifiers = self.identifiers
 
-        old_names = real_identifiers.declared | \
-                    real_identifiers.declared_locally | \
+        old_names = real_identifiers.declared_locally | \
                     real_identifiers.declared_parameter
 
         def inner_visit(nodes):
@@ -312,7 +314,8 @@
 
         # the differences between the two branches are also pulled as
         # undeclared variables
-        real_identifiers.undeclared.update(body.symmetric_difference(else_))
+        real_identifiers.undeclared.update(body.symmetric_difference(else_) -
+                                           real_identifiers.declared)
 
         # remember those that are declared.
         real_identifiers.declared_locally.update(body | else_)
@@ -365,7 +368,8 @@
 
 class CodeGenerator(NodeVisitor):
 
-    def __init__(self, environment, name, filename, stream=None):
+    def __init__(self, environment, name, filename, stream=None,
+                 defer_init=False):
         if stream is None:
             stream = StringIO()
         self.environment = environment
@@ -373,6 +377,7 @@
         self.filename = filename
         self.stream = stream
         self.created_block_context = False
+        self.defer_init = defer_init
 
         # aliases for imports
         self.import_aliases = {}
@@ -419,7 +424,7 @@
     # -- Various compilation helpers
 
     def fail(self, msg, lineno):
-        """Fail with a `TemplateAssertionError`."""
+        """Fail with a :exc:`TemplateAssertionError`."""
         raise TemplateAssertionError(msg, lineno, self.name, self.filename)
 
     def temporary_identifier(self):
@@ -434,7 +439,16 @@
 
     def return_buffer_contents(self, frame):
         """Return the buffer contents of the frame."""
-        if self.environment.autoescape:
+        if frame.eval_ctx.volatile:
+            self.writeline('if context.eval_ctx.autoescape:')
+            self.indent()
+            self.writeline('return Markup(concat(%s))' % frame.buffer)
+            self.outdent()
+            self.writeline('else:')
+            self.indent()
+            self.writeline('return concat(%s)' % frame.buffer)
+            self.outdent()
+        elif frame.eval_ctx.autoescape:
             self.writeline('return Markup(concat(%s))' % frame.buffer)
         else:
             self.writeline('return concat(%s)' % frame.buffer)
@@ -747,12 +761,18 @@
 
     def visit_Template(self, node, frame=None):
         assert frame is None, 'no root frame allowed'
+        eval_ctx = EvalContext(self.environment, self.name)
+
         from jinja2.runtime import __all__ as exported
         self.writeline('from __future__ import division')
         self.writeline('from jinja2.runtime import ' + ', '.join(exported))
         if not unoptimize_before_dead_code:
             self.writeline('dummy = lambda *x: None')
 
+        # if we want a deferred initialization we cannot move the
+        # environment into a local name
+        envenv = not self.defer_init and ', environment=environment' or ''
+
         # do we have an extends tag at all?  If not, we can save some
         # overhead by just not processing any inheritance code.
         have_extends = node.find(nodes.Extends) is not None
@@ -779,10 +799,10 @@
         self.writeline('name = %r' % self.name)
 
         # generate the root render function.
-        self.writeline('def root(context, environment=environment):', extra=1)
+        self.writeline('def root(context%s):' % envenv, extra=1)
 
         # process the root
-        frame = Frame()
+        frame = Frame(eval_ctx)
         frame.inspect(node.body)
         frame.toplevel = frame.rootlevel = True
         frame.require_output_check = have_extends and not self.has_known_extends
@@ -811,11 +831,11 @@
 
         # at this point we now have the blocks collected and can visit them too.
         for name, block in self.blocks.iteritems():
-            block_frame = Frame()
+            block_frame = Frame(eval_ctx)
             block_frame.inspect(block.body)
             block_frame.block = name
-            self.writeline('def block_%s(context, environment=environment):'
-                           % name, block, 1)
+            self.writeline('def block_%s(context%s):' % (name, envenv),
+                           block, 1)
             self.indent()
             undeclared = find_undeclared(block.body, ('self', 'super'))
             if 'self' in undeclared:
@@ -1217,12 +1237,15 @@
         body = []
         for child in node.nodes:
             try:
-                const = child.as_const()
+                const = child.as_const(frame.eval_ctx)
             except nodes.Impossible:
                 body.append(child)
                 continue
+            # the frame can't be volatile here, becaus otherwise the
+            # as_const() function would raise an Impossible exception
+            # at that point.
             try:
-                if self.environment.autoescape:
+                if frame.eval_ctx.autoescape:
                     if hasattr(const, '__html__'):
                         const = const.__html__()
                     else:
@@ -1260,7 +1283,10 @@
                     else:
                         self.newline(item)
                     close = 1
-                    if self.environment.autoescape:
+                    if frame.eval_ctx.volatile:
+                        self.write('(context.eval_ctx.autoescape and'
+                                   ' escape or to_string)(')
+                    elif frame.eval_ctx.autoescape:
                         self.write('escape(')
                     else:
                         self.write('to_string(')
@@ -1293,7 +1319,11 @@
             for argument in arguments:
                 self.newline(argument)
                 close = 0
-                if self.environment.autoescape:
+                if frame.eval_ctx.volatile:
+                    self.write('(context.eval_ctx.autoescape and'
+                               ' escape or to_string)(')
+                    close += 1
+                elif frame.eval_ctx.autoescape:
                     self.write('escape(')
                     close += 1
                 if self.environment.finalize is not None:
@@ -1360,7 +1390,11 @@
             self.write(repr(val))
 
     def visit_TemplateData(self, node, frame):
-        self.write(repr(node.as_const()))
+        try:
+            self.write(repr(node.as_const(frame.eval_ctx)))
+        except nodes.Impossible:
+            self.write('(context.eval_ctx.autoescape and Markup or identity)(%r)'
+                       % node.data)
 
     def visit_Tuple(self, node, frame):
         self.write('(')
@@ -1420,8 +1454,14 @@
     del binop, uaop
 
     def visit_Concat(self, node, frame):
-        self.write('%s((' % (self.environment.autoescape and
-                             'markup_join' or 'unicode_join'))
+        if frame.eval_ctx.volatile:
+            func_name = '(context.eval_ctx.volatile and' \
+                        ' markup_join or unicode_join)'
+        elif frame.eval_ctx.autoescape:
+            func_name = 'markup_join'
+        else:
+            func_name = 'unicode_join'
+        self.write('%s((' % func_name)
         for arg in node.nodes:
             self.visit(arg, frame)
             self.write(', ')
@@ -1472,6 +1512,8 @@
             self.fail('no filter named %r' % node.name, node.lineno)
         if getattr(func, 'contextfilter', False):
             self.write('context, ')
+        elif getattr(func, 'evalcontextfilter', False):
+            self.write('context.eval_ctx, ')
         elif getattr(func, 'environmentfilter', False):
             self.write('environment, ')
 
@@ -1479,7 +1521,11 @@
         # and want to write to the current buffer
         if node.node is not None:
             self.visit(node.node, frame)
-        elif self.environment.autoescape:
+        elif frame.eval_ctx.volatile:
+            self.write('(context.eval_ctx.autoescape and'
+                       ' Markup(concat(%s)) or concat(%s))' %
+                       (frame.buffer, frame.buffer))
+        elif frame.eval_ctx.autoescape:
             self.write('Markup(concat(%s))' % frame.buffer)
         else:
             self.write('concat(%s)' % frame.buffer)
@@ -1540,6 +1586,11 @@
         self.visit(node.expr, frame)
         self.write(')')
 
+    def visit_MarkSafeIfAutoescape(self, node, frame):
+        self.write('(context.eval_ctx.autoescape and Markup or identity)(')
+        self.visit(node.expr, frame)
+        self.write(')')
+
     def visit_EnvironmentAttribute(self, node, frame):
         self.write('environment.' + node.name)
 
@@ -1568,3 +1619,24 @@
         self.pull_locals(scope_frame)
         self.blockvisit(node.body, scope_frame)
         self.pop_scope(aliases, scope_frame)
+
+    def visit_EvalContextModifier(self, node, frame):
+        for keyword in node.options:
+            self.writeline('context.eval_ctx.%s = ' % keyword.key)
+            self.visit(keyword.value, frame)
+            try:
+                val = keyword.value.as_const(frame.eval_ctx)
+            except nodes.Impossible:
+                frame.eval_ctx.volatile = True
+            else:
+                setattr(frame.eval_ctx, keyword.key, val)
+
+    def visit_ScopedEvalContextModifier(self, node, frame):
+        old_ctx_name = self.temporary_identifier()
+        safed_ctx = frame.eval_ctx.save()
+        self.writeline('%s = context.eval_ctx.save()' % old_ctx_name)
+        self.visit_EvalContextModifier(node, frame)
+        for child in node.body:
+            self.visit(child, frame)
+        frame.eval_ctx.revert(safed_ctx)
+        self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name)
--- a/bundled/jinja2/jinja2/environment.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/environment.py	Fri Jun 11 21:25:17 2010 -0400
@@ -8,6 +8,7 @@
     :copyright: (c) 2010 by the Jinja Team.
     :license: BSD, see LICENSE for more details.
 """
+import os
 import sys
 from jinja2 import nodes
 from jinja2.defaults import *
@@ -157,9 +158,15 @@
             `None` implicitly into an empty string here.
 
         `autoescape`
-            If set to true the XML/HTML autoescaping feature is enabled.
-            For more details about auto escaping see
-            :class:`~jinja2.utils.Markup`.
+            If set to true the XML/HTML autoescaping feature is enabled by
+            default.  For more details about auto escaping see
+            :class:`~jinja2.utils.Markup`.  As of Jinja 2.4 this can also
+            be a callable that is passed the template name and has to
+            return `True` or `False` depending on autoescape should be
+            enabled by default.
+
+            .. versionchanged:: 2.4
+               `autoescape` can now be a function
 
         `loader`
             The template loader for this environment.
@@ -272,6 +279,13 @@
 
         _environment_sanity_check(self)
 
+    def add_extension(self, extension):
+        """Adds an extension after the environment was created.
+
+        .. versionadded:: 2.5
+        """
+        self.extensions.update(load_extensions(self, [extension]))
+
     def extend(self, **attributes):
         """Add the items to the instance of the environment if they do not exist
         yet.  This is used by :ref:`extensions <writing-extensions>` to register
@@ -327,6 +341,11 @@
 
     lexer = property(get_lexer, doc="The lexer for this environment.")
 
+    def iter_extensions(self):
+        """Iterates over the extensions by priority."""
+        return iter(sorted(self.extensions.values(),
+                           key=lambda x: x.priority))
+
     def getitem(self, obj, argument):
         """Get an item or attribute of an object but prefer the item."""
         try:
@@ -400,7 +419,7 @@
         because there you usually only want the actual source tokenized.
         """
         return reduce(lambda s, e: e.preprocess(s, name, filename),
-                      self.extensions.itervalues(), unicode(source))
+                      self.iter_extensions(), unicode(source))
 
     def _tokenize(self, source, name, filename=None, state=None):
         """Called by the parser to do the preprocessing and filtering
@@ -408,14 +427,31 @@
         """
         source = self.preprocess(source, name, filename)
         stream = self.lexer.tokenize(source, name, filename, state)
-        for ext in self.extensions.itervalues():
+        for ext in self.iter_extensions():
             stream = ext.filter_stream(stream)
             if not isinstance(stream, TokenStream):
                 stream = TokenStream(stream, name, filename)
         return stream
 
+    def _generate(self, source, name, filename, defer_init=False):
+        """Internal hook that can be overriden to hook a different generate
+        method in.
+
+        .. versionadded:: 2.5
+        """
+        return generate(source, self, name, filename, defer_init=defer_init)
+
+    def _compile(self, source, filename):
+        """Internal hook that can be overriden to hook a different compile
+        method in.
+
+        .. versionadded:: 2.5
+        """
+        return compile(source, filename, 'exec')
+
     @internalcode
-    def compile(self, source, name=None, filename=None, raw=False):
+    def compile(self, source, name=None, filename=None, raw=False,
+                defer_init=False):
         """Compile a node or template source code.  The `name` parameter is
         the load name of the template after it was joined using
         :meth:`join_path` if necessary, not the filename on the file system.
@@ -427,6 +463,13 @@
         parameter is `True` the return value will be a string with python
         code equivalent to the bytecode returned otherwise.  This method is
         mainly used internally.
+
+        `defer_init` is use internally to aid the module code generator.  This
+        causes the generated code to be able to import without the global
+        environment variable to be set.
+
+        .. versionadded:: 2.4
+           `defer_init` parameter added.
         """
         source_hint = None
         try:
@@ -435,14 +478,15 @@
                 source = self._parse(source, name, filename)
             if self.optimized:
                 source = optimize(source, self)
-            source = generate(source, self, name, filename)
+            source = self._generate(source, name, filename,
+                                    defer_init=defer_init)
             if raw:
                 return source
             if filename is None:
                 filename = '<template>'
             else:
                 filename = _encode_filename(filename)
-            return compile(source, filename, 'exec')
+            return self._compile(source, filename)
         except TemplateSyntaxError:
             exc_info = sys.exc_info()
         self.handle_exception(exc_info, source_hint=source)
@@ -483,6 +527,7 @@
                 raise TemplateSyntaxError('chunk after expression',
                                           parser.stream.current.lineno,
                                           None, None)
+            expr.set_environment(self)
         except TemplateSyntaxError:
             exc_info = sys.exc_info()
         if exc_info is not None:
@@ -491,6 +536,114 @@
         template = self.from_string(nodes.Template(body, lineno=1))
         return TemplateExpression(template, undefined_to_none)
 
+    def compile_templates(self, target, extensions=None, filter_func=None,
+                          zip='deflated', log_function=None,
+                          ignore_errors=True, py_compile=False):
+        """Compiles all the templates the loader can find, compiles them
+        and stores them in `target`.  If `zip` is `None`, instead of in a
+        zipfile, the templates will be will be stored in a directory.
+        By default a deflate zip algorithm is used, to switch to
+        the stored algorithm, `zip` can be set to ``'stored'``.
+
+        `extensions` and `filter_func` are passed to :meth:`list_templates`.
+        Each template returned will be compiled to the target folder or
+        zipfile.
+
+        By default template compilation errors are ignored.  In case a
+        log function is provided, errors are logged.  If you want template
+        syntax errors to abort the compilation you can set `ignore_errors`
+        to `False` and you will get an exception on syntax errors.
+
+        If `py_compile` is set to `True` .pyc files will be written to the
+        target instead of standard .py files.
+
+        .. versionadded:: 2.4
+        """
+        from jinja2.loaders import ModuleLoader
+
+        if log_function is None:
+            log_function = lambda x: None
+
+        if py_compile:
+            import imp, struct, marshal
+            py_header = imp.get_magic() + \
+                u'\xff\xff\xff\xff'.encode('iso-8859-15')
+
+        def write_file(filename, data, mode):
+            if zip:
+                info = ZipInfo(filename)
+                info.external_attr = 0755 << 16L
+                zip_file.writestr(info, data)
+            else:
+                f = open(os.path.join(target, filename), mode)
+                try:
+                    f.write(data)
+                finally:
+                    f.close()
+
+        if zip is not None:
+            from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
+            zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
+                                                 stored=ZIP_STORED)[zip])
+            log_function('Compiling into Zip archive "%s"' % target)
+        else:
+            if not os.path.isdir(target):
+                os.makedirs(target)
+            log_function('Compiling into folder "%s"' % target)
+
+        try:
+            for name in self.list_templates(extensions, filter_func):
+                source, filename, _ = self.loader.get_source(self, name)
+                try:
+                    code = self.compile(source, name, filename, True, True)
+                except TemplateSyntaxError, e:
+                    if not ignore_errors:
+                        raise
+                    log_function('Could not compile "%s": %s' % (name, e))
+                    continue
+
+                filename = ModuleLoader.get_module_filename(name)
+
+                if py_compile:
+                    c = self._compile(code, _encode_filename(filename))
+                    write_file(filename + 'c', py_header +
+                               marshal.dumps(c), 'wb')
+                    log_function('Byte-compiled "%s" as %s' %
+                                 (name, filename + 'c'))
+                else:
+                    write_file(filename, code, 'w')
+                    log_function('Compiled "%s" as %s' % (name, filename))
+        finally:
+            if zip:
+                zip_file.close()
+
+        log_function('Finished compiling templates')
+
+    def list_templates(self, extensions=None, filter_func=None):
+        """Returns a list of templates for this environment.  This requires
+        that the loader supports the loader's
+        :meth:`~BaseLoader.list_templates` method.
+
+        If there are other files in the template folder besides the
+        actual templates, the returned list can be filtered.  There are two
+        ways: either `extensions` is set to a list of file extensions for
+        templates, or a `filter_func` can be provided which is a callable that
+        is passed a template name and should return `True` if it should end up
+        in the result list.
+
+        If the loader does not support that, a :exc:`TypeError` is raised.
+        """
+        x = self.loader.list_templates()
+        if extensions is not None:
+            if filter_func is not None:
+                raise TypeError('either extensions or filter_func '
+                                'can be passed, but not both')
+            filter_func = lambda x: '.' in x and \
+                                    x.rsplit('.', 1)[1] in extensions
+        if filter_func is not None:
+            x = filter(filter_func, x)
+        return x
+
     def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
         """Exception handling helper.  This is used internally to either raise
         rewritten exceptions or return a rendered traceback for the template.
@@ -679,16 +832,31 @@
         """Creates a template object from compiled code and the globals.  This
         is used by the loaders and environment to create a template object.
         """
-        t = object.__new__(cls)
         namespace = {
-            'environment':          environment,
-            '__jinja_template__':   t
+            'environment':  environment,
+            '__file__':     code.co_filename
         }
         exec code in namespace
+        rv = cls._from_namespace(environment, namespace, globals)
+        rv._uptodate = uptodate
+        return rv
+
+    @classmethod
+    def from_module_dict(cls, environment, module_dict, globals):
+        """Creates a template object from a module.  This is used by the
+        module loader to create a template object.
+
+        .. versionadded:: 2.4
+        """
+        return cls._from_namespace(environment, module_dict, globals)
+
+    @classmethod
+    def _from_namespace(cls, environment, namespace, globals):
+        t = object.__new__(cls)
         t.environment = environment
         t.globals = globals
         t.name = namespace['name']
-        t.filename = code.co_filename
+        t.filename = namespace['__file__']
         t.blocks = namespace['blocks']
 
         # render function and module
@@ -697,7 +865,11 @@
 
         # debug and loader helpers
         t._debug_info = namespace['debug_info']
-        t._uptodate = uptodate
+        t._uptodate = None
+
+        # store the reference
+        namespace['environment'] = environment
+        namespace['__jinja_template__'] = t
 
         return t
 
--- a/bundled/jinja2/jinja2/ext.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/ext.py	Fri Jun 11 21:25:17 2010 -0400
@@ -57,6 +57,13 @@
     #: if this extension parses this is the list of tags it's listening to.
     tags = set()
 
+    #: the priority of that extension.  This is especially useful for
+    #: extensions that preprocess values.  A lower value means higher
+    #: priority.
+    #:
+    #: .. versionadded:: 2.4
+    priority = 100
+
     def __init__(self, environment):
         self.environment = environment
 
@@ -116,8 +123,29 @@
 
 
 @contextfunction
-def _gettext_alias(context, string):
-    return context.resolve('gettext')(string)
+def _gettext_alias(__context, *args, **kwargs):
+    return __context.call(__context.resolve('gettext'), *args, **kwargs)
+
+
+def _make_new_gettext(func):
+    @contextfunction
+    def gettext(__context, __string, **variables):
+        rv = __context.call(func, __string)
+        if __context.eval_ctx.autoescape:
+            rv = Markup(rv)
+        return rv % variables
+    return gettext
+
+
+def _make_new_ngettext(func):
+    @contextfunction
+    def ngettext(__context, __singular, __plural, __num, **variables):
+        variables.setdefault('num', __num)
+        rv = __context.call(func, __singular, __plural, __num)
+        if __context.eval_ctx.autoescape:
+            rv = Markup(rv)
+        return rv % variables
+    return ngettext
 
 
 class InternationalizationExtension(Extension):
@@ -137,23 +165,37 @@
         environment.extend(
             install_gettext_translations=self._install,
             install_null_translations=self._install_null,
+            install_gettext_callables=self._install_callables,
             uninstall_gettext_translations=self._uninstall,
-            extract_translations=self._extract
+            extract_translations=self._extract,
+            newstyle_gettext=False
         )
 
-    def _install(self, translations):
+    def _install(self, translations, newstyle=None):
         gettext = getattr(translations, 'ugettext', None)
         if gettext is None:
             gettext = translations.gettext
         ngettext = getattr(translations, 'ungettext', None)
         if ngettext is None:
             ngettext = translations.ngettext
-        self.environment.globals.update(gettext=gettext, ngettext=ngettext)
+        self._install_callables(gettext, ngettext, newstyle)
+
+    def _install_null(self, newstyle=None):
+        self._install_callables(
+            lambda x: x,
+            lambda s, p, n: (n != 1 and (p,) or (s,))[0],
+            newstyle
+        )
 
-    def _install_null(self):
+    def _install_callables(self, gettext, ngettext, newstyle=None):
+        if newstyle is not None:
+            self.environment.newstyle_gettext = newstyle
+        if self.environment.newstyle_gettext:
+            gettext = _make_new_gettext(gettext)
+            ngettext = _make_new_ngettext(ngettext)
         self.environment.globals.update(
-            gettext=lambda x: x,
-            ngettext=lambda s, p, n: (n != 1 and (p,) or (s,))[0]
+            gettext=gettext,
+            ngettext=ngettext
         )
 
     def _uninstall(self, translations):
@@ -168,6 +210,7 @@
     def parse(self, parser):
         """Parse a translatable tag."""
         lineno = next(parser.stream).lineno
+        num_called_num = False
 
         # find all the variables referenced.  Additionally a variable can be
         # defined in the body of the trans block too, but this is checked at
@@ -194,8 +237,10 @@
                 variables[name.value] = var = parser.parse_expression()
             else:
                 variables[name.value] = var = nodes.Name(name.value, 'load')
+
             if plural_expr is None:
                 plural_expr = var
+                num_called_num = name.value == 'num'
 
         parser.stream.expect('block_end')
 
@@ -209,6 +254,7 @@
             referenced.update(singular_names)
             if plural_expr is None:
                 plural_expr = nodes.Name(singular_names[0], 'load')
+                num_called_num = singular_names[0] == 'num'
 
         # if we have a pluralize block, we parse that too
         if parser.stream.current.test('name:pluralize'):
@@ -221,6 +267,7 @@
                                 name.value, name.lineno,
                                 exc=TemplateAssertionError)
                 plural_expr = variables[name.value]
+                num_called_num = name.value == 'num'
             parser.stream.expect('block_end')
             plural_names, plural = self._parse_block(parser, False)
             next(parser.stream)
@@ -233,24 +280,13 @@
             if var not in variables:
                 variables[var] = nodes.Name(var, 'load')
 
-        # no variables referenced?  no need to escape
-        if not referenced:
-            singular = singular.replace('%%', '%')
-            if plural:
-                plural = plural.replace('%%', '%')
-
         if not have_plural:
             plural_expr = None
         elif plural_expr is None:
             parser.fail('pluralize without variables', lineno)
 
-        if variables:
-            variables = nodes.Dict([nodes.Pair(nodes.Const(x, lineno=lineno), y)
-                                    for x, y in variables.items()])
-        else:
-            variables = None
-
-        node = self._make_node(singular, plural, variables, plural_expr)
+        node = self._make_node(singular, plural, variables, plural_expr,
+                               bool(referenced), num_called_num)
         node.set_lineno(lineno)
         return node
 
@@ -286,8 +322,16 @@
 
         return referenced, concat(buf)
 
-    def _make_node(self, singular, plural, variables, plural_expr):
+    def _make_node(self, singular, plural, variables, plural_expr,
+                   vars_referenced, num_called_num):
         """Generates a useful node from the data provided."""
+        # no variables referenced?  no need to escape for old style
+        # gettext invocations
+        if not vars_referenced and not self.environment.newstyle_gettext:
+            singular = singular.replace('%%', '%')
+            if plural:
+                plural = plural.replace('%%', '%')
+
         # singular only:
         if plural_expr is None:
             gettext = nodes.Name('gettext', 'load')
@@ -303,13 +347,27 @@
                 plural_expr
             ], [], None, None)
 
-        # mark the return value as safe if we are in an
-        # environment with autoescaping turned on
-        if self.environment.autoescape:
-            node = nodes.MarkSafe(node)
+        # in case newstyle gettext is used, the method is powerful
+        # enough to handle the variable expansion and autoescape
+        # handling itself
+        if self.environment.newstyle_gettext:
+            for key, value in variables.iteritems():
+                # the function adds that later anyways in case num was
+                # called num, so just skip it.
+                if num_called_num and key == 'num':
+                    continue
+                node.kwargs.append(nodes.Keyword(key, value))
 
-        if variables:
-            node = nodes.Mod(node, variables)
+        # otherwise do that here
+        else:
+            # mark the return value as safe if we are in an
+            # environment with autoescaping turned on
+            node = nodes.MarkSafeIfAutoescape(node)
+            if variables:
+                node = nodes.Mod(node, nodes.Dict([
+                    nodes.Pair(nodes.Const(key), value)
+                    for key, value in variables.items()
+                ]))
         return nodes.Output([node])
 
 
@@ -357,6 +415,20 @@
         return node
 
 
+class AutoEscapeExtension(Extension):
+    """Changes auto escape rules for a scope."""
+    tags = set(['autoescape'])
+
+    def parse(self, parser):
+        node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno)
+        node.options = [
+            nodes.Keyword('autoescape', parser.parse_expression())
+        ]
+        node.body = parser.parse_statements(('name:endautoescape',),
+                                            drop_needle=True)
+        return nodes.Scope([node])
+
+
 def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
                      babel_style=True):
     """Extract localizable strings from the given template node.  Per
@@ -529,3 +601,4 @@
 do = ExprStmtExtension
 loopcontrols = LoopControlExtension
 with_ = WithExtension
+autoescape = AutoEscapeExtension
--- a/bundled/jinja2/jinja2/filters.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/filters.py	Fri Jun 11 21:25:17 2010 -0400
@@ -25,18 +25,25 @@
     """Decorator for marking context dependent filters. The current
     :class:`Context` will be passed as first argument.
     """
-    if getattr(f, 'environmentfilter', False):
-        raise TypeError('filter already marked as environment filter')
     f.contextfilter = True
     return f
 
 
+def evalcontextfilter(f):
+    """Decorator for marking eval-context dependent filters.  An eval
+    context object is passed as first argument.  For more information
+    about the eval context, see :ref:`eval-context`.
+
+    .. versionadded:: 2.4
+    """
+    f.evalcontextfilter = True
+    return f
+
+
 def environmentfilter(f):
     """Decorator for marking evironment dependent filters.  The current
     :class:`Environment` is passed to the filter as first argument.
     """
-    if getattr(f, 'contextfilter', False):
-        raise TypeError('filter already marked as context filter')
     f.environmentfilter = True
     return f
 
@@ -48,8 +55,8 @@
     return escape(unicode(value))
 
 
-@environmentfilter
-def do_replace(environment, s, old, new, count=None):
+@evalcontextfilter
+def do_replace(eval_ctx, s, old, new, count=None):
     """Return a copy of the value with all occurrences of a substring
     replaced with a new one. The first argument is the substring
     that should be replaced, the second is the replacement string.
@@ -66,7 +73,7 @@
     """
     if count is None:
         count = -1
-    if not environment.autoescape:
+    if not eval_ctx.autoescape:
         return unicode(s).replace(unicode(old), unicode(new), count)
     if hasattr(old, '__html__') or hasattr(new, '__html__') and \
        not hasattr(s, '__html__'):
@@ -86,8 +93,8 @@
     return soft_unicode(s).lower()
 
 
-@environmentfilter
-def do_xmlattr(_environment, d, autospace=True):
+@evalcontextfilter
+def do_xmlattr(_eval_ctx, d, autospace=True):
     """Create an SGML/XML attribute string based on the items in a dict.
     All values that are neither `none` nor `undefined` are automatically
     escaped:
@@ -117,7 +124,7 @@
     )
     if autospace and rv:
         rv = u' ' + rv
-    if _environment.autoescape:
+    if _eval_ctx.autoescape:
         rv = Markup(rv)
     return rv
 
@@ -169,10 +176,13 @@
     return sorted(value.items(), key=sort_func)
 
 
-def do_sort(value, case_sensitive=False):
-    """Sort an iterable.  If the iterable is made of strings the second
-    parameter can be used to control the case sensitiveness of the
-    comparison which is disabled by default.
+def do_sort(value, reverse=False, case_sensitive=False):
+    """Sort an iterable.  Per default it sorts ascending, if you pass it
+    true as first argument it will reverse the sorting.
+
+    If the iterable is made of strings the third parameter can be used to
+    control the case sensitiveness of the comparison which is disabled by
+    default.
 
     .. sourcecode:: jinja
 
@@ -187,7 +197,7 @@
             return item
     else:
         sort_func = None
-    return sorted(seq, key=sort_func)
+    return sorted(value, key=sort_func, reverse=reverse)
 
 
 def do_default(value, default_value=u'', boolean=False):
@@ -212,8 +222,8 @@
     return value
 
 
-@environmentfilter
-def do_join(environment, value, d=u''):
+@evalcontextfilter
+def do_join(eval_ctx, value, d=u''):
     """Return a string which is the concatenation of the strings in the
     sequence. The separator between elements is an empty string per
     default, you can define it with the optional parameter:
@@ -227,7 +237,7 @@
             -> 123
     """
     # no automatic escaping?  joining is a lot eaiser then
-    if not environment.autoescape:
+    if not eval_ctx.autoescape:
         return unicode(d).join(imap(unicode, value))
 
     # if the delimiter doesn't have an html representation we check
@@ -309,8 +319,8 @@
     return pformat(value, verbose=verbose)
 
 
-@environmentfilter
-def do_urlize(environment, value, trim_url_limit=None, nofollow=False):
+@evalcontextfilter
+def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
     """Converts URLs in plain text into clickable links.
 
     If you pass the filter an additional integer it will shorten the urls
@@ -323,7 +333,7 @@
             links are shortened to 40 chars and defined with rel="nofollow"
     """
     rv = urlize(value, trim_url_limit, nofollow)
-    if environment.autoescape:
+    if eval_ctx.autoescape:
         rv = Markup(rv)
     return rv
 
@@ -557,13 +567,6 @@
         return func(value)
 
 
-def do_sort(value, reverse=False):
-    """Sort a sequence. Per default it sorts ascending, if you pass it
-    true as first argument it will reverse the sorting.
-    """
-    return sorted(value, reverse=reverse)
-
-
 @environmentfilter
 def do_groupby(environment, value, attribute):
     """Group a sequence of objects by a common attribute.
@@ -716,7 +719,6 @@
     'sum':                  sum,
     'abs':                  abs,
     'round':                do_round,
-    'sort':                 do_sort,
     'groupby':              do_groupby,
     'safe':                 do_mark_safe,
     'xmlattr':              do_xmlattr
--- a/bundled/jinja2/jinja2/lexer.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/lexer.py	Fri Jun 11 21:25:17 2010 -0400
@@ -432,9 +432,10 @@
             'root': [
                 # directives
                 (c('(.*?)(?:%s)' % '|'.join(
-                    [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*%s)' % (
+                    [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
                         e(environment.block_start_string),
                         e(environment.block_start_string),
+                        e(environment.block_end_string),
                         e(environment.block_end_string)
                     )] + [
                         r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, r)
--- a/bundled/jinja2/jinja2/loaders.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/loaders.py	Fri Jun 11 21:25:17 2010 -0400
@@ -8,6 +8,10 @@
     :copyright: (c) 2010 by the Jinja Team.
     :license: BSD, see LICENSE for more details.
 """
+import os
+import sys
+import weakref
+from types import ModuleType
 from os import path
 try:
     from hashlib import sha1
@@ -59,6 +63,12 @@
                 return source, path, lambda: mtime == getmtime(path)
     """
 
+    #: if set to `False` it indicates that the loader cannot provide access
+    #: to the source of templates.
+    #:
+    #: .. versionadded:: 2.4
+    has_source_access = True
+
     def get_source(self, environment, template):
         """Get the template source, filename and reload helper for a template.
         It's passed the environment and template name and has to return a
@@ -77,8 +87,17 @@
         old state somewhere (for example in a closure).  If it returns `False`
         the template will be reloaded.
         """
+        if not self.has_source_access:
+            raise RuntimeError('%s cannot provide access to the source' %
+                               self.__class__.__name__)
         raise TemplateNotFound(template)
 
+    def list_templates(self):
+        """Iterates over all templates.  If the loader does not support that
+        it should raise a :exc:`TypeError` which is the default behavior.
+        """
+        raise TypeError('this loader cannot iterate over all templates')
+
     @internalcode
     def load(self, environment, name, globals=None):
         """Loads a template.  This method looks up the template in the cache
@@ -160,6 +179,20 @@
             return contents, filename, uptodate
         raise TemplateNotFound(template)
 
+    def list_templates(self):
+        found = set()
+        for searchpath in self.searchpath:
+            for dirpath, dirnames, filenames in os.walk(searchpath):
+                for filename in filenames:
+                    template = os.path.join(dirpath, filename) \
+                        [len(searchpath):].strip(os.path.sep) \
+                                          .replace(os.path.sep, '/')
+                    if template[:2] == './':
+                        template = template[2:]
+                    if template not in found:
+                        found.add(template)
+        return sorted(found)
+
 
 class PackageLoader(BaseLoader):
     """Load templates from python eggs or packages.  It is constructed with
@@ -206,6 +239,26 @@
         source = self.provider.get_resource_string(self.manager, p)
         return source.decode(self.encoding), filename, uptodate
 
+    def list_templates(self):
+        path = self.package_path
+        if path[:2] == './':
+            path = path[2:]
+        elif path == '.':
+            path = ''
+        offset = len(path)
+        results = []
+        def _walk(path):
+            for filename in self.provider.resource_listdir(path):
+                fullname = path + '/' + filename
+                if self.provider.resource_isdir(fullname):
+                    for item in _walk(fullname):
+                        results.append(item)
+                else:
+                    results.append(fullname[offset:].lstrip('/'))
+        _walk(path)
+        results.sort()
+        return results
+
 
 class DictLoader(BaseLoader):
     """Loads a template from a python dict.  It's passed a dict of unicode
@@ -225,6 +278,9 @@
             return source, None, lambda: source != self.mapping.get(template)
         raise TemplateNotFound(template)
 
+    def list_templates(self):
+        return sorted(self.mapping)
+
 
 class FunctionLoader(BaseLoader):
     """A loader that is passed a function which does the loading.  The
@@ -288,6 +344,13 @@
             # (the one that includes the prefix)
             raise TemplateNotFound(template)
 
+    def list_templates(self):
+        result = []
+        for prefix, loader in self.mapping.iteritems():
+            for template in loader.list_templates():
+                result.append(prefix + self.delimiter + template)
+        return result
+
 
 class ChoiceLoader(BaseLoader):
     """This loader works like the `PrefixLoader` just that no prefix is
@@ -313,3 +376,74 @@
             except TemplateNotFound:
                 pass
         raise TemplateNotFound(template)
+
+    def list_templates(self):
+        found = set()
+        for loader in self.loaders:
+            found.update(loader.list_templates())
+        return sorted(found)
+
+
+class _TemplateModule(ModuleType):
+    """Like a normal module but with support for weak references"""
+
+
+class ModuleLoader(BaseLoader):
+    """This loader loads templates from precompiled templates.
+
+    Example usage:
+
+    >>> loader = ChoiceLoader([
+    ...     ModuleLoader('/path/to/compiled/templates'),
+    ...     FileSystemLoader('/path/to/templates')
+    ... ])
+    """
+
+    has_source_access = False
+
+    def __init__(self, path):
+        package_name = '_jinja2_module_templates_%x' % id(self)
+
+        # create a fake module that looks for the templates in the
+        # path given.
+        mod = _TemplateModule(package_name)
+        if isinstance(path, basestring):
+            path = [path]
+        else:
+            path = list(path)
+        mod.__path__ = path
+
+        sys.modules[package_name] = weakref.proxy(mod,
+            lambda x: sys.modules.pop(package_name, None))
+
+        # the only strong reference, the sys.modules entry is weak
+        # so that the garbage collector can remove it once the
+        # loader that created it goes out of business.
+        self.module = mod
+        self.package_name = package_name
+
+    @staticmethod
+    def get_template_key(name):
+        return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest()
+
+    @staticmethod
+    def get_module_filename(name):
+        return ModuleLoader.get_template_key(name) + '.py'
+
+    @internalcode
+    def load(self, environment, name, globals=None):
+        key = self.get_template_key(name)
+        module = '%s.%s' % (self.package_name, key)
+        mod = getattr(self.module, module, None)
+        if mod is None:
+            try:
+                mod = __import__(module, None, None, ['root'])
+            except ImportError:
+                raise TemplateNotFound(name)
+
+            # remove the entry from sys.modules, we only want the attribute
+            # on the module object we have stored on the loader.
+            sys.modules.pop(module, None)
+
+        return environment.template_class.from_module_dict(
+            environment, mod.__dict__, globals)
--- a/bundled/jinja2/jinja2/nodes.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/nodes.py	Fri Jun 11 21:25:17 2010 -0400
@@ -15,7 +15,11 @@
 import operator
 from itertools import chain, izip
 from collections import deque
-from jinja2.utils import Markup
+from jinja2.utils import Markup, MethodType, FunctionType
+
+
+#: the types we support for context functions
+_context_function_types = (FunctionType, MethodType)
 
 
 _binop_to_func = {
@@ -67,6 +71,36 @@
         return type.__new__(cls, name, bases, d)
 
 
+class EvalContext(object):
+    """Holds evaluation time information.  Custom attributes can be attached
+    to it in extensions.
+    """
+
+    def __init__(self, environment, template_name=None):
+        if callable(environment.autoescape):
+            self.autoescape = environment.autoescape(template_name)
+        else:
+            self.autoescape = environment.autoescape
+        self.volatile = False
+
+    def save(self):
+        return self.__dict__.copy()
+
+    def revert(self, old):
+        self.__dict__.clear()
+        self.__dict__.update(old)
+
+
+def get_eval_context(node, ctx):
+    if ctx is None:
+        if node.environment is None:
+            raise RuntimeError('if no eval context is passed, the '
+                               'node must have an attached '
+                               'environment.')
+        return EvalContext(node.environment)
+    return ctx
+
+
 class Node(object):
     """Baseclass for all Jinja2 nodes.  There are a number of nodes available
     of different types.  There are three major types:
@@ -312,19 +346,16 @@
     """Baseclass for all expressions."""
     abstract = True
 
-    def as_const(self):
+    def as_const(self, eval_ctx=None):
         """Return the value of the expression as constant or raise
-        :exc:`Impossible` if this was not possible:
+        :exc:`Impossible` if this was not possible.
 
-        >>> Add(Const(23), Const(42)).as_const()
-        65
-        >>> Add(Const(23), Name('var', 'load')).as_const()
-        Traceback (most recent call last):
-          ...
-        Impossible
+        An :class:`EvalContext` can be provided, if none is given
+        a default context is created which requires the nodes to have
+        an attached environment.
 
-        This requires the `environment` attribute of all nodes to be
-        set to the environment that created the nodes.
+        .. versionchanged:: 2.4
+           the `eval_ctx` parameter was added.
         """
         raise Impossible()
 
@@ -339,10 +370,11 @@
     operator = None
     abstract = True
 
-    def as_const(self):
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
         f = _binop_to_func[self.operator]
         try:
-            return f(self.left.as_const(), self.right.as_const())
+            return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
         except:
             raise Impossible()
 
@@ -353,10 +385,11 @@
     operator = None
     abstract = True
 
-    def as_const(self):
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
         f = _uaop_to_func[self.operator]
         try:
-            return f(self.node.as_const())
+            return f(self.node.as_const(eval_ctx))
         except:
             raise Impossible()
 
@@ -389,7 +422,7 @@
     """
     fields = ('value',)
 
-    def as_const(self):
+    def as_const(self, eval_ctx=None):
         return self.value
 
     @classmethod
@@ -408,8 +441,11 @@
     """A constant template string."""
     fields = ('data',)
 
-    def as_const(self):
-        if self.environment.autoescape:
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        if eval_ctx.volatile:
+            raise Impossible()
+        if eval_ctx.autoescape:
             return Markup(self.data)
         return self.data
 
@@ -421,8 +457,9 @@
     """
     fields = ('items', 'ctx')
 
-    def as_const(self):
-        return tuple(x.as_const() for x in self.items)
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return tuple(x.as_const(eval_ctx) for x in self.items)
 
     def can_assign(self):
         for item in self.items:
@@ -435,8 +472,9 @@
     """Any list literal such as ``[1, 2, 3]``"""
     fields = ('items',)
 
-    def as_const(self):
-        return [x.as_const() for x in self.items]
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return [x.as_const(eval_ctx) for x in self.items]
 
 
 class Dict(Literal):
@@ -445,24 +483,27 @@
     """
     fields = ('items',)
 
-    def as_const(self):
-        return dict(x.as_const() for x in self.items)
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return dict(x.as_const(eval_ctx) for x in self.items)
 
 
 class Pair(Helper):
     """A key, value pair for dicts."""
     fields = ('key', 'value')
 
-    def as_const(self):
-        return self.key.as_const(), self.value.as_const()
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
 
 
 class Keyword(Helper):
     """A key, value pair for keyword arguments where key is a string."""
     fields = ('key', 'value')
 
-    def as_const(self):
-        return self.key, self.value.as_const()
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return self.key, self.value.as_const(eval_ctx)
 
 
 class CondExpr(Expr):
@@ -471,15 +512,16 @@
     """
     fields = ('test', 'expr1', 'expr2')
 
-    def as_const(self):
-        if self.test.as_const():
-            return self.expr1.as_const()
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        if self.test.as_const(eval_ctx):
+            return self.expr1.as_const(eval_ctx)
 
         # if we evaluate to an undefined object, we better do that at runtime
         if self.expr2 is None:
             raise Impossible()
 
-        return self.expr2.as_const()
+        return self.expr2.as_const(eval_ctx)
 
 
 class Filter(Expr):
@@ -491,8 +533,9 @@
     """
     fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
 
-    def as_const(self, obj=None):
-        if self.node is obj is None:
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        if eval_ctx.volatile or self.node is None:
             raise Impossible()
         # we have to be careful here because we call filter_ below.
         # if this variable would be called filter, 2to3 would wrap the
@@ -502,20 +545,21 @@
         filter_ = self.environment.filters.get(self.name)
         if filter_ is None or getattr(filter_, 'contextfilter', False):
             raise Impossible()
-        if obj is None:
-            obj = self.node.as_const()
-        args = [x.as_const() for x in self.args]
-        if getattr(filter_, 'environmentfilter', False):
+        obj = self.node.as_const(eval_ctx)
+        args = [x.as_const(eval_ctx) for x in self.args]
+        if getattr(filter_, 'evalcontextfilter', False):
+            args.insert(0, eval_ctx)
+        elif getattr(filter_, 'environmentfilter', False):
             args.insert(0, self.environment)
-        kwargs = dict(x.as_const() for x in self.kwargs)
+        kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
         if self.dyn_args is not None:
             try:
-                args.extend(self.dyn_args.as_const())
+                args.extend(self.dyn_args.as_const(eval_ctx))
             except:
                 raise Impossible()
         if self.dyn_kwargs is not None:
             try:
-                kwargs.update(self.dyn_kwargs.as_const())
+                kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
             except:
                 raise Impossible()
         try:
@@ -540,25 +584,31 @@
     """
     fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
 
-    def as_const(self):
-        obj = self.node.as_const()
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        if eval_ctx.volatile:
+            raise Impossible()
+        obj = self.node.as_const(eval_ctx)
 
         # don't evaluate context functions
-        args = [x.as_const() for x in self.args]
-        if getattr(obj, 'contextfunction', False):
-            raise Impossible()
-        elif getattr(obj, 'environmentfunction', False):
-            args.insert(0, self.environment)
+        args = [x.as_const(eval_ctx) for x in self.args]
+        if isinstance(obj, _context_function_types):
+            if getattr(obj, 'contextfunction', False):
+                raise Impossible()
+            elif getattr(obj, 'evalcontextfunction', False):
+                args.insert(0, eval_ctx)
+            elif getattr(obj, 'environmentfunction', False):
+                args.insert(0, self.environment)
 
-        kwargs = dict(x.as_const() for x in self.kwargs)
+        kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
         if self.dyn_args is not None:
             try:
-                args.extend(self.dyn_args.as_const())
+                args.extend(self.dyn_args.as_const(eval_ctx))
             except:
                 raise Impossible()
         if self.dyn_kwargs is not None:
             try:
-                kwargs.update(self.dyn_kwargs.as_const())
+                kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
             except:
                 raise Impossible()
         try:
@@ -571,12 +621,13 @@
     """Get an attribute or item from an expression and prefer the item."""
     fields = ('node', 'arg', 'ctx')
 
-    def as_const(self):
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
         if self.ctx != 'load':
             raise Impossible()
         try:
-            return self.environment.getitem(self.node.as_const(),
-                                            self.arg.as_const())
+            return self.environment.getitem(self.node.as_const(eval_ctx),
+                                            self.arg.as_const(eval_ctx))
         except:
             raise Impossible()
 
@@ -590,11 +641,13 @@
     """
     fields = ('node', 'attr', 'ctx')
 
-    def as_const(self):
+    def as_const(self, eval_ctx=None):
         if self.ctx != 'load':
             raise Impossible()
         try:
-            return self.environment.getattr(self.node.as_const(), arg)
+            eval_ctx = get_eval_context(self, eval_ctx)
+            return self.environment.getattr(self.node.as_const(eval_ctx),
+                                            self.attr)
         except:
             raise Impossible()
 
@@ -608,11 +661,12 @@
     """
     fields = ('start', 'stop', 'step')
 
-    def as_const(self):
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
         def const(obj):
             if obj is None:
-                return obj
-            return obj.as_const()
+                return None
+            return obj.as_const(eval_ctx)
         return slice(const(self.start), const(self.stop), const(self.step))
 
 
@@ -622,8 +676,9 @@
     """
     fields = ('nodes',)
 
-    def as_const(self):
-        return ''.join(unicode(x.as_const()) for x in self.nodes)
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return ''.join(unicode(x.as_const(eval_ctx)) for x in self.nodes)
 
 
 class Compare(Expr):
@@ -632,11 +687,12 @@
     """
     fields = ('expr', 'ops')
 
-    def as_const(self):
-        result = value = self.expr.as_const()
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        result = value = self.expr.as_const(eval_ctx)
         try:
             for op in self.ops:
-                new_value = op.expr.as_const()
+                new_value = op.expr.as_const(eval_ctx)
                 result = _cmpop_to_func[op.op](value, new_value)
                 value = new_value
         except:
@@ -695,16 +751,18 @@
     """Short circuited AND."""
     operator = 'and'
 
-    def as_const(self):
-        return self.left.as_const() and self.right.as_const()
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx)
 
 
 class Or(BinExpr):
     """Short circuited OR."""
     operator = 'or'
 
-    def as_const(self):
-        return self.left.as_const() or self.right.as_const()
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx)
 
 
 class Not(UnaryExpr):
@@ -769,12 +827,40 @@
     """Mark the wrapped expression as safe (wrap it as `Markup`)."""
     fields = ('expr',)
 
-    def as_const(self):
-        return Markup(self.expr.as_const())
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        return Markup(self.expr.as_const(eval_ctx))
+
+
+class MarkSafeIfAutoescape(Expr):
+    """Mark the wrapped expression as safe (wrap it as `Markup`) but
+    only if autoescaping is active.
+
+    .. versionadded:: 2.5
+    """
+    fields = ('expr',)
+
+    def as_const(self, eval_ctx=None):
+        eval_ctx = get_eval_context(self, eval_ctx)
+        if eval_ctx.volatile:
+            raise Impossible()
+        expr = self.expr.as_const(eval_ctx)
+        if eval_ctx.autoescape:
+            return Markup(expr)
+        return expr
 
 
 class ContextReference(Expr):
-    """Returns the current template context."""
+    """Returns the current template context.  It can be used like a
+    :class:`Name` node, with a ``'load'`` ctx and will return the
+    current :class:`~jinja2.runtime.Context` object.
+
+    Here an example that assigns the current template name to a
+    variable named `foo`::
+
+        Assign(Name('foo', ctx='store'),
+               Getattr(ContextReference(), 'name'))
+    """
 
 
 class Continue(Stmt):
@@ -790,6 +876,25 @@
     fields = ('body',)
 
 
+class EvalContextModifier(Stmt):
+    """Modifies the eval context.  For each option that should be modified,
+    a :class:`Keyword` has to be added to the :attr:`options` list.
+
+    Example to change the `autoescape` setting::
+
+        EvalContextModifier(options=[Keyword('autoescape', Const(True))])
+    """
+    fields = ('options',)
+
+
+class ScopedEvalContextModifier(EvalContextModifier):
+    """Modifies the eval context and reverts it later.  Works exactly like
+    :class:`EvalContextModifier` but will only modify the
+    :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
+    """
+    fields = ('body',)
+
+
 # make sure nobody creates custom nodes
 def _failing_new(*args, **kwargs):
     raise TypeError('can\'t create custom node types')
--- a/bundled/jinja2/jinja2/parser.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/parser.py	Fri Jun 11 21:25:17 2010 -0400
@@ -34,7 +34,7 @@
         self.filename = filename
         self.closed = False
         self.extensions = {}
-        for extension in environment.extensions.itervalues():
+        for extension in environment.iter_extensions():
             for tag in extension.tags:
                 self.extensions[tag] = extension.parse
         self._last_identifier = 0
--- a/bundled/jinja2/jinja2/runtime.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/runtime.py	Fri Jun 11 21:25:17 2010 -0400
@@ -10,8 +10,9 @@
 """
 import sys
 from itertools import chain, imap
+from jinja2.nodes import EvalContext, _context_function_types
 from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
-     concat, MethodType, FunctionType, internalcode, next
+     concat, internalcode, next, object_type_repr
 from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
      TemplateNotFound
 
@@ -19,18 +20,17 @@
 # these variables are exported to the template runtime
 __all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
            'TemplateRuntimeError', 'missing', 'concat', 'escape',
-           'markup_join', 'unicode_join', 'to_string',
+           'markup_join', 'unicode_join', 'to_string', 'identity',
            'TemplateNotFound']
 
-
-#: the types we support for context functions
-_context_function_types = (FunctionType, MethodType)
-
 #: the name of the function that is used to convert something into
 #: a string.  2to3 will adopt that automatically and the generated
 #: code can take advantage of it.
 to_string = unicode
 
+#: the identity function.  Useful for certain things in the environment
+identity = lambda x: x
+
 
 def markup_join(seq):
     """Concatenation that escapes if necessary and converts to unicode."""
@@ -76,7 +76,7 @@
 
     def __getitem__(self, name):
         blocks = self.__context.blocks[name]
-        wrap = self.__context.environment.autoescape and \
+        wrap = self.__context.eval_ctx.autoescape and \
                Markup or (lambda x: x)
         return BlockReference(name, self.__context, blocks, 0)
 
@@ -106,13 +106,14 @@
     method that doesn't fail with a `KeyError` but returns an
     :class:`Undefined` object for missing variables.
     """
-    __slots__ = ('parent', 'vars', 'environment', 'exported_vars', 'name',
-                 'blocks', '__weakref__')
+    __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars',
+                 'name', 'blocks', '__weakref__')
 
     def __init__(self, environment, parent, name, blocks):
         self.parent = parent
         self.vars = {}
         self.environment = environment
+        self.eval_ctx = EvalContext(self.environment, name)
         self.exported_vars = set()
         self.name = name
 
@@ -174,14 +175,22 @@
         if isinstance(__obj, _context_function_types):
             if getattr(__obj, 'contextfunction', 0):
                 args = (__self,) + args
+            elif getattr(__obj, 'evalcontextfunction', 0):
+                args = (__self.eval_ctx,) + args
             elif getattr(__obj, 'environmentfunction', 0):
                 args = (__self.environment,) + args
-        return __obj(*args, **kwargs)
+        try:
+            return __obj(*args, **kwargs)
+        except StopIteration:
+            return __self.environment.undefined('value was undefined because '
+                                                'a callable raised a '
+                                                'StopIteration exception')
 
     def derived(self, locals=None):
         """Internal helper function to create a derived context."""
         context = new_context(self.environment, self.name, {},
                               self.parent, True, None, locals)
+        context.eval_ctx = self.eval_ctx
         context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
         return context
 
@@ -252,7 +261,7 @@
     @internalcode
     def __call__(self):
         rv = concat(self._stack[self._depth](self._context))
-        if self._context.environment.autoescape:
+        if self._context.eval_ctx.autoescape:
             rv = Markup(rv)
         return rv
 
@@ -341,7 +350,7 @@
 
 
 class Macro(object):
-    """Wraps a macro."""
+    """Wraps a macro function."""
 
     def __init__(self, environment, func, name, arguments, defaults,
                  catch_kwargs, catch_varargs, caller):
@@ -357,20 +366,24 @@
 
     @internalcode
     def __call__(self, *args, **kwargs):
-        arguments = []
-        for idx, name in enumerate(self.arguments):
-            try:
-                value = args[idx]
-            except:
+        # try to consume the positional arguments
+        arguments = list(args[:self._argument_count])
+        off = len(arguments)
+
+        # if the number of arguments consumed is not the number of
+        # arguments expected we start filling in keyword arguments
+        # and defaults.
+        if off != self._argument_count:
+            for idx, name in enumerate(self.arguments[len(arguments):]):
                 try:
                     value = kwargs.pop(name)
-                except:
+                except KeyError:
                     try:
-                        value = self.defaults[idx - self._argument_count]
-                    except:
+                        value = self.defaults[idx - self._argument_count + off]
+                    except IndexError:
                         value = self._environment.undefined(
                             'parameter %r was not provided' % name, name=name)
-            arguments.append(value)
+                arguments.append(value)
 
         # it's important that the order of these arguments does not change
         # if not also changed in the compiler's `function_scoping` method.
@@ -417,7 +430,7 @@
     __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
                  '_undefined_exception')
 
-    def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
+    def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError):
         self._undefined_hint = hint
         self._undefined_obj = obj
         self._undefined_name = name
@@ -429,16 +442,16 @@
         `UndefinedError` on call.
         """
         if self._undefined_hint is None:
-            if self._undefined_obj is None:
+            if self._undefined_obj is missing:
                 hint = '%r is undefined' % self._undefined_name
             elif not isinstance(self._undefined_name, basestring):
-                hint = '%r object has no element %r' % (
-                    self._undefined_obj.__class__.__name__,
+                hint = '%s has no element %r' % (
+                    object_type_repr(self._undefined_obj),
                     self._undefined_name
                 )
             else:
-                hint = '%r object has no attribute %r' % (
-                    self._undefined_obj.__class__.__name__,
+                hint = '%r has no attribute %r' % (
+                    object_type_repr(self._undefined_obj),
                     self._undefined_name
                 )
         else:
@@ -493,10 +506,10 @@
 
     def __unicode__(self):
         if self._undefined_hint is None:
-            if self._undefined_obj is None:
+            if self._undefined_obj is missing:
                 return u'{{ %s }}' % self._undefined_name
             return '{{ no such element: %s[%r] }}' % (
-                self._undefined_obj.__class__.__name__,
+                object_type_repr(self._undefined_obj),
                 self._undefined_name
             )
         return u'{{ undefined value printed: %s }}' % self._undefined_hint
@@ -523,7 +536,7 @@
     """
     __slots__ = ()
     __iter__ = __unicode__ = __str__ = __len__ = __nonzero__ = __eq__ = \
-        __ne__ = Undefined._fail_with_undefined_error
+        __ne__ = __bool__ = Undefined._fail_with_undefined_error
 
 
 # remove remaining slots attributes, after the metaclass did the magic they
--- a/bundled/jinja2/jinja2/testsuite/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/__init__.py	Fri Jun 11 21:25:17 2010 -0400
@@ -38,6 +38,18 @@
     ### use only these methods for testing.  If you need standard
     ### unittest method, wrap them!
 
+    def setup(self):
+        pass
+
+    def teardown(self):
+        pass
+
+    def setUp(self):
+        self.setup()
+
+    def tearDown(self):
+        self.teardown()
+
     def assert_equal(self, a, b):
         return self.assertEqual(a, b)
 
--- a/bundled/jinja2/jinja2/testsuite/api.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/api.py	Fri Jun 11 21:25:17 2010 -0400
@@ -17,7 +17,7 @@
 
 from jinja2 import Environment, Undefined, DebugUndefined, \
      StrictUndefined, UndefinedError, Template, meta, \
-     is_undefined, Template
+     is_undefined, Template, DictLoader
 from jinja2.utils import Cycler
 
 env = Environment()
@@ -76,6 +76,23 @@
         assert env.get_or_select_template([t]) is t
         assert env.get_or_select_template(t) is t
 
+    def test_autoescape_autoselect(self):
+        def select_autoescape(name):
+            if name is None or '.' not in name:
+                return False
+            return name.endswith('.html')
+        env = Environment(autoescape=select_autoescape,
+                          loader=DictLoader({
+            'test.txt':     '{{ foo }}',
+            'test.html':    '{{ foo }}'
+        }))
+        t = env.get_template('test.txt')
+        assert t.render(foo='<foo>') == '<foo>'
+        t = env.get_template('test.html')
+        assert t.render(foo='<foo>') == '&lt;foo&gt;'
+        t = env.from_string('{{ foo }}')
+        assert t.render(foo='<foo>') == '<foo>'
+
 
 class MetaTestCase(JinjaTestCase):
 
@@ -155,29 +172,36 @@
 
 class UndefinedTestCase(JinjaTestCase):
 
+    def test_stopiteration_is_undefined(self):
+        def test():
+            raise StopIteration()
+        t = Template('A{{ test() }}B')
+        assert t.render(test=test) == 'AB'
+        t = Template('A{{ test().missingattribute }}B')
+        self.assert_raises(UndefinedError, t.render, test=test)
+
     def test_default_undefined(self):
         env = Environment(undefined=Undefined)
         self.assert_equal(env.from_string('{{ missing }}').render(), u'')
         self.assert_raises(UndefinedError,
                            env.from_string('{{ missing.attribute }}').render)
-        self.assert_equal(env.from_string('{{ missing|list }}').render, '[]')
-        self.assert_equal(env.from_string('{{ missing is not defined }}').render, 'True')
+        self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
+        self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
         self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), '')
         self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
 
-    def test_debug_undefined():
+    def test_debug_undefined(self):
         env = Environment(undefined=DebugUndefined)
         self.assert_equal(env.from_string('{{ missing }}').render(), '{{ missing }}')
         self.assert_raises(UndefinedError,
-                           env.from_string('{{ missing.attribute }}').render())
+                           env.from_string('{{ missing.attribute }}').render)
         self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
-        u'[]'
-        self.assert_equal(env.from_string('{{ missing is not defined }}').render, 'True')
+        self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
         self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42),
-                          u"{{ no such element: int['missing'] }}")
+                          u"{{ no such element: int object['missing'] }}")
         self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
 
-    def test_strict_undefined():
+    def test_strict_undefined(self):
         env = Environment(undefined=StrictUndefined)
         self.assert_raises(UndefinedError, env.from_string('{{ missing }}').render)
         self.assert_raises(UndefinedError, env.from_string('{{ missing.attribute }}').render)
@@ -188,8 +212,23 @@
 
     def test_indexing_gives_undefined(self):
         t = Template("{{ var[42].foo }}")
-        assert_raises(UndefinedError, t.render, var=0)
+        self.assert_raises(UndefinedError, t.render, var=0)
 
+    def test_none_gives_proper_error(self):
+        try:
+            Environment().getattr(None, 'split')()
+        except UndefinedError, e:
+            assert e.message == "'None' has no attribute 'split'"
+        else:
+            assert False, 'expected exception'
+
+    def test_object_repr(self):
+        try:
+            Undefined(obj=42, name='upper')()
+        except UndefinedError, e:
+            assert e.message == "'int object' has no attribute 'upper'"
+        else:
+            assert False, 'expected exception'
 
 
 def suite():
@@ -197,4 +236,5 @@
     suite.addTest(unittest.makeSuite(ExtendedAPITestCase))
     suite.addTest(unittest.makeSuite(MetaTestCase))
     suite.addTest(unittest.makeSuite(StreamingTestCase))
+    suite.addTest(unittest.makeSuite(UndefinedTestCase))
     return suite
--- a/bundled/jinja2/jinja2/testsuite/core_tags.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/core_tags.py	Fri Jun 11 21:25:17 2010 -0400
@@ -271,6 +271,12 @@
         assert tmpl.module.bar.catch_varargs
         assert tmpl.module.baz.caller
 
+    def test_callself(self):
+        tmpl = self.env.from_string('{% macro foo(x) %}{{ x }}{% if x > 1 %}|'
+                                    '{{ foo(x - 1) }}{% endif %}{% endmacro %}'
+                                    '{{ foo(5) }}')
+        assert tmpl.render() == '5|4|3|2|1'
+
 
 def suite():
     suite = unittest.TestSuite()
--- a/bundled/jinja2/jinja2/testsuite/ext.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/ext.py	Fri Jun 11 21:25:17 2010 -0400
@@ -31,24 +31,39 @@
 _gettext_re = re.compile(r'_\((.*?)\)(?s)')
 
 
-templates = {
+i18n_templates = {
+    'master.html': '<title>{{ page_title|default(_("missing")) }}</title>'
+                   '{% block body %}{% endblock %}',
+    'child.html': '{% extends "master.html" %}{% block body %}'
+                  '{% trans %}watch out{% endtrans %}{% endblock %}',
+    'plural.html': '{% trans user_count %}One user online{% pluralize %}'
+                   '{{ user_count }} users online{% endtrans %}',
+    'stringformat.html': '{{ _("User: %(num)s")|format(num=user_count) }}'
+}
+
+newstyle_i18n_templates = {
     'master.html': '<title>{{ page_title|default(_("missing")) }}</title>'
                    '{% block body %}{% endblock %}',
     'child.html': '{% extends "master.html" %}{% block body %}'
                   '{% trans %}watch out{% endtrans %}{% endblock %}',
     'plural.html': '{% trans user_count %}One user online{% pluralize %}'
                    '{{ user_count }} users online{% endtrans %}',
-    'stringformat.html': '{{ _("User: %d")|format(user_count) }}'
+    'stringformat.html': '{{ _("User: %(num)s", num=user_count) }}',
+    'ngettext.html': '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}',
+    'ngettext_long.html': '{% trans num=apples %}{{ num }} apple{% pluralize %}'
+                          '{{ num }} apples{% endtrans %}'
 }
 
 
 languages = {
     'de': {
-        'missing':                      'fehlend',
-        'watch out':                    'pass auf',
-        'One user online':              'Ein Benutzer online',
-        '%(user_count)s users online':  '%(user_count)s Benutzer online',
-        'User: %d':                     'Benutzer: %d'
+        'missing':                      u'fehlend',
+        'watch out':                    u'pass auf',
+        'One user online':              u'Ein Benutzer online',
+        '%(user_count)s users online':  u'%(user_count)s Benutzer online',
+        'User: %(num)s':                u'Benutzer: %(num)s',
+        '%(num)s apple':                u'%(num)s Apfel',
+        '%(num)s apples':               u'%(num)s Äpfel'
     }
 }
 
@@ -68,7 +83,7 @@
 
 
 i18n_env = Environment(
-    loader=DictLoader(templates),
+    loader=DictLoader(i18n_templates),
     extensions=['jinja2.ext.i18n']
 )
 i18n_env.globals.update({
@@ -77,6 +92,11 @@
     'ngettext':     ngettext
 })
 
+newstyle_i18n_env = Environment(
+    loader=DictLoader(newstyle_i18n_templates),
+    extensions=['jinja2.ext.i18n']
+)
+newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True)
 
 class TestExtension(Extension):
     tags = set(['test'])
@@ -140,6 +160,12 @@
 
 class ExtensionsTestCase(JinjaTestCase):
 
+    def test_extend_late(self):
+        env = Environment()
+        env.add_extension('jinja2.ext.autoescape')
+        t = env.from_string('{% autoescape true %}{{ "<test>" }}{% endautoescape %}')
+        assert t.render() == '&lt;test&gt;'
+
     def test_loop_controls(self):
         env = Environment(extensions=['jinja2.ext.loopcontrols'])
 
@@ -204,6 +230,16 @@
         out = tmpl.render()
         assert out == 'Foo BAR Baz'
 
+    def test_extension_ordering(self):
+        class T1(Extension):
+            priority = 1
+        class T2(Extension):
+            priority = 2
+        env = Environment(extensions=[T1, T2])
+        ext = list(env.iter_extensions())
+        assert ext[0].__class__ is T1
+        assert ext[1].__class__ is T2
+
 
 class InternationalizationTestCase(JinjaTestCase):
 
@@ -256,8 +292,141 @@
         ]
 
 
+class NewstyleInternationalizationTestCase(JinjaTestCase):
+
+    def test_trans(self):
+        tmpl = newstyle_i18n_env.get_template('child.html')
+        assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf'
+
+    def test_trans_plural(self):
+        tmpl = newstyle_i18n_env.get_template('plural.html')
+        assert tmpl.render(LANGUAGE='de', user_count=1) == 'Ein Benutzer online'
+        assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online'
+
+    def test_complex_plural(self):
+        tmpl = newstyle_i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% '
+                                    'pluralize count %}{{ count }} items{% endtrans %}')
+        assert tmpl.render() == '2 items'
+        self.assert_raises(TemplateAssertionError, i18n_env.from_string,
+                           '{% trans foo %}...{% pluralize bar %}...{% endtrans %}')
+
+    def test_trans_stringformatting(self):
+        tmpl = newstyle_i18n_env.get_template('stringformat.html')
+        assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5'
+
+    def test_newstyle_plural(self):
+        tmpl = newstyle_i18n_env.get_template('ngettext.html')
+        assert tmpl.render(LANGUAGE='de', apples=1) == '1 Apfel'
+        assert tmpl.render(LANGUAGE='de', apples=5) == u'5 Äpfel'
+
+    def test_autoescape_support(self):
+        env = Environment(extensions=['jinja2.ext.autoescape',
+                                      'jinja2.ext.i18n'])
+        env.install_gettext_callables(lambda x: u'<strong>Wert: %(name)s</strong>',
+                                      lambda s, p, n: s, newstyle=True)
+        t = env.from_string('{% autoescape ae %}{{ gettext("foo", name='
+                            '"<test>") }}{% endautoescape %}')
+        assert t.render(ae=True) == '<strong>Wert: &lt;test&gt;</strong>'
+        assert t.render(ae=False) == '<strong>Wert: <test></strong>'
+
+    def test_num_used_twice(self):
+        tmpl = newstyle_i18n_env.get_template('ngettext_long.html')
+        assert tmpl.render(apples=5, LANGUAGE='de') == u'5 Äpfel'
+
+    def test_num_called_num(self):
+        source = newstyle_i18n_env.compile('''
+            {% trans num=3 %}{{ num }} apple{% pluralize
+            %}{{ num }} apples{% endtrans %}
+        ''', raw=True)
+        # quite hacky, but the only way to properly test that.  The idea is
+        # that the generated code does not pass num twice (although that
+        # would work) for better performance.  This only works on the
+        # newstyle gettext of course
+        assert re.search(r"l_ngettext, u?'\%\(num\)s apple', u?'\%\(num\)s "
+                         r"apples', 3", source) is not None
+
+
+class AutoEscapeTestCase(JinjaTestCase):
+
+    def test_scoped_setting(self):
+        env = Environment(extensions=['jinja2.ext.autoescape'],
+                          autoescape=True)
+        tmpl = env.from_string('''
+            {{ "<HelloWorld>" }}
+            {% autoescape false %}
+                {{ "<HelloWorld>" }}
+            {% endautoescape %}
+            {{ "<HelloWorld>" }}
+        ''')
+        assert tmpl.render().split() == \
+            [u'&lt;HelloWorld&gt;', u'<HelloWorld>', u'&lt;HelloWorld&gt;']
+
+        env = Environment(extensions=['jinja2.ext.autoescape'],
+                          autoescape=False)
+        tmpl = env.from_string('''
+            {{ "<HelloWorld>" }}
+            {% autoescape true %}
+                {{ "<HelloWorld>" }}
+            {% endautoescape %}
+            {{ "<HelloWorld>" }}
+        ''')
+        assert tmpl.render().split() == \
+            [u'<HelloWorld>', u'&lt;HelloWorld&gt;', u'<HelloWorld>']
+
+    def test_nonvolatile(self):
+        env = Environment(extensions=['jinja2.ext.autoescape'],
+                          autoescape=True)
+        tmpl = env.from_string('{{ {"foo": "<test>"}|xmlattr|escape }}')
+        assert tmpl.render() == ' foo="&lt;test&gt;"'
+        tmpl = env.from_string('{% autoescape false %}{{ {"foo": "<test>"}'
+                               '|xmlattr|escape }}{% endautoescape %}')
+        assert tmpl.render() == ' foo=&#34;&amp;lt;test&amp;gt;&#34;'
+
+    def test_volatile(self):
+        env = Environment(extensions=['jinja2.ext.autoescape'],
+                          autoescape=True)
+        tmpl = env.from_string('{% autoescape foo %}{{ {"foo": "<test>"}'
+                               '|xmlattr|escape }}{% endautoescape %}')
+        assert tmpl.render(foo=False) == ' foo=&#34;&amp;lt;test&amp;gt;&#34;'
+        assert tmpl.render(foo=True) == ' foo="&lt;test&gt;"'
+
+    def test_scoping(self):
+        env = Environment(extensions=['jinja2.ext.autoescape'])
+        tmpl = env.from_string('{% autoescape true %}{% set x = "<x>" %}{{ x }}'
+                               '{% endautoescape %}{{ x }}{{ "<y>" }}')
+        assert tmpl.render(x=1) == '&lt;x&gt;1<y>'
+
+    def test_volatile_scoping(self):
+        env = Environment(extensions=['jinja2.ext.autoescape'])
+        tmplsource = '''
+        {% autoescape val %}
+            {% macro foo(x) %}
+                [{{ x }}]
+            {% endmacro %}
+            {{ foo().__class__.__name__ }}
+        {% endautoescape %}
+        {{ '<testing>' }}
+        '''
+        tmpl = env.from_string(tmplsource)
+        assert tmpl.render(val=True).split()[0] == 'Markup'
+        assert tmpl.render(val=False).split()[0] == unicode.__name__
+
+        # looking at the source we should see <testing> there in raw
+        # (and then escaped as well)
+        env = Environment(extensions=['jinja2.ext.autoescape'])
+        pysource = env.compile(tmplsource, raw=True)
+        assert '<testing>\\n' in pysource
+
+        env = Environment(extensions=['jinja2.ext.autoescape'],
+                          autoescape=True)
+        pysource = env.compile(tmplsource, raw=True)
+        assert '&lt;testing&gt;\\n' in pysource
+
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(ExtensionsTestCase))
     suite.addTest(unittest.makeSuite(InternationalizationTestCase))
+    suite.addTest(unittest.makeSuite(NewstyleInternationalizationTestCase))
+    suite.addTest(unittest.makeSuite(AutoEscapeTestCase))
     return suite
--- a/bundled/jinja2/jinja2/testsuite/filters.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/filters.py	Fri Jun 11 21:25:17 2010 -0400
@@ -227,6 +227,10 @@
         tmpl = env.from_string('{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}')
         assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
 
+    def test_sort2(self):
+        tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort(false, true)) }}')
+        assert tmpl.render() == 'AbcD'
+
     def test_groupby(self):
         tmpl = env.from_string('''
         {%- for grouper, list in [{'foo': 1, 'bar': 2},
--- a/bundled/jinja2/jinja2/testsuite/lexnparse.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/lexnparse.py	Fri Jun 11 21:25:17 2010 -0400
@@ -36,6 +36,10 @@
                                '{%raw%}{{ bar }}|{% baz %}{%       endraw    %}')
         assert tmpl.render() == 'foo|{{ bar }}|{% baz %}'
 
+    def test_raw2(self):
+        tmpl = env.from_string('1  {%- raw -%}   2   {%- endraw -%}   3')
+        assert tmpl.render() == '123'
+
     def test_balancing(self):
         env = Environment('{%', '%}', '${', '}')
         tmpl = env.from_string('''{% for item in seq
--- a/bundled/jinja2/jinja2/testsuite/loader.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/loader.py	Fri Jun 11 21:25:17 2010 -0400
@@ -9,8 +9,10 @@
     :license: BSD, see LICENSE for more details.
 """
 import os
+import sys
 import time
 import tempfile
+import shutil
 import unittest
 
 from jinja2.testsuite import JinjaTestCase, dict_loader, \
@@ -98,7 +100,92 @@
         self.assert_raises(TemplateNotFound, split_template_path, '../foo')
 
 
+class ModuleLoaderTestCase(JinjaTestCase):
+    archive = None
+
+    def compile_down(self, zip='deflated', py_compile=False):
+        super(ModuleLoaderTestCase, self).setup()
+        log = []
+        self.reg_env = Environment(loader=prefix_loader)
+        if zip is not None:
+            self.archive = tempfile.mkstemp(suffix='.zip')[1]
+        else:
+            self.archive = tempfile.mkdtemp()
+        self.reg_env.compile_templates(self.archive, zip=zip,
+                                       log_function=log.append,
+                                       py_compile=py_compile)
+        self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive))
+        return ''.join(log)
+
+    def teardown(self):
+        super(ModuleLoaderTestCase, self).teardown()
+        if hasattr(self, 'mod_env'):
+            if os.path.isfile(self.archive):
+                os.remove(self.archive)
+            else:
+                shutil.rmtree(self.archive)
+            self.archive = None
+
+    def test_log(self):
+        log = self.compile_down()
+        assert 'Compiled "a/foo/test.html" as ' \
+               'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log
+        assert 'Finished compiling templates' in log
+        assert 'Could not compile "a/syntaxerror.html": ' \
+               'Encountered unknown tag \'endif\'' in log
+
+    def _test_common(self):
+        tmpl1 = self.reg_env.get_template('a/test.html')
+        tmpl2 = self.mod_env.get_template('a/test.html')
+        assert tmpl1.render() == tmpl2.render()
+
+        tmpl1 = self.reg_env.get_template('b/justdict.html')
+        tmpl2 = self.mod_env.get_template('b/justdict.html')
+        assert tmpl1.render() == tmpl2.render()
+
+    def test_deflated_zip_compile(self):
+        self.compile_down(zip='deflated')
+        self._test_common()
+
+    def test_stored_zip_compile(self):
+        self.compile_down(zip='stored')
+        self._test_common()
+
+    def test_filesystem_compile(self):
+        self.compile_down(zip=None)
+        self._test_common()
+
+    def test_weak_references(self):
+        self.compile_down()
+        tmpl = self.mod_env.get_template('a/test.html')
+        key = loaders.ModuleLoader.get_template_key('a/test.html')
+        name = self.mod_env.loader.module.__name__
+
+        assert hasattr(self.mod_env.loader.module, key)
+        assert name in sys.modules
+
+        # unset all, ensure the module is gone from sys.modules
+        self.mod_env = tmpl = None
+
+        try:
+            import gc
+            gc.collect()
+        except:
+            pass
+
+        assert name not in sys.modules
+
+    def test_byte_compilation(self):
+        log = self.compile_down(py_compile=True)
+        assert 'Byte-compiled "a/test.html"' in log
+        tmpl1 = self.mod_env.get_template('a/test.html')
+        mod = self.mod_env.loader.module. \
+            tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490
+        assert mod.__file__.endswith('.pyc')
+
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(LoaderTestCase))
+    suite.addTest(unittest.makeSuite(ModuleLoaderTestCase))
     return suite
--- a/bundled/jinja2/jinja2/testsuite/regression.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/regression.py	Fri Jun 11 21:25:17 2010 -0400
@@ -162,6 +162,19 @@
     ''')
         assert t.render(a=0, b=False, c=42, d=42.0) == '1111C'
 
+    def test_stacked_locals_scoping_bug_twoframe(self):
+        t = Template('''
+            {% set x = 1 %}
+            {% for item in foo %}
+                {% if item == 1 %}
+                    {% set x = 2 %}
+                {% endif %}
+            {% endfor %}
+            {{ x }}
+        ''')
+        rv = t.render(foo=[1]).strip()
+        assert rv == u'1'
+
     def test_call_with_args(self):
         t = Template("""{% macro dump_users(users) -%}
         <ul>
--- a/bundled/jinja2/jinja2/testsuite/utils.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/testsuite/utils.py	Fri Jun 11 21:25:17 2010 -0400
@@ -18,7 +18,7 @@
 
 from jinja2 import Environment, Undefined, DebugUndefined, \
      StrictUndefined, UndefinedError, Template, meta
-from jinja2.utils import LRUCache, escape
+from jinja2.utils import LRUCache, escape, object_type_repr
 
 
 class LRUCacheTestCase(JinjaTestCase):
@@ -46,6 +46,19 @@
             assert copy._queue == cache._queue
 
 
+class HelpersTestCase(JinjaTestCase):
+
+    def test_object_type_repr(self):
+        class X(object):
+            pass
+        self.assert_equal(object_type_repr(42), 'int object')
+        self.assert_equal(object_type_repr([]), 'list object')
+        self.assert_equal(object_type_repr(X()),
+                         'jinja2.testsuite.utils.X object')
+        self.assert_equal(object_type_repr(None), 'None')
+        self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis')
+
+
 class MarkupLeakTestCase(JinjaTestCase):
 
     def test_markup_leaks(self):
@@ -63,6 +76,7 @@
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(LRUCacheTestCase))
+    suite.addTest(unittest.makeSuite(HelpersTestCase))
 
     # this test only tests the c extension
     if not hasattr(escape, 'func_code'):
--- a/bundled/jinja2/jinja2/utils.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/jinja2/utils.py	Fri Jun 11 21:25:17 2010 -0400
@@ -127,6 +127,19 @@
     return f
 
 
+def evalcontextfunction(f):
+    """This decoraotr can be used to mark a function or method as an eval
+    context callable.  This is similar to the :func:`contextfunction`
+    but instead of passing the context, an evaluation context object is
+    passed.  For more information about the eval context, see
+    :ref:`eval-context`.
+
+    .. versionadded:: 2.4
+    """
+    f.evalcontextfunction = True
+    return f
+
+
 def environmentfunction(f):
     """This decorator can be used to mark a function or method as environment
     callable.  This decorator works exactly like the :func:`contextfunction`
@@ -214,6 +227,23 @@
             raise
 
 
+def object_type_repr(obj):
+    """Returns the name of the object's type.  For some recognized
+    singletons the name of the object is returned instead. (For
+    example for `None` and `Ellipsis`).
+    """
+    if obj is None:
+        return 'None'
+    elif obj is Ellipsis:
+        return 'Ellipsis'
+    # __builtin__ in 2.x, builtins in 3.x
+    if obj.__class__.__module__ in ('__builtin__', 'builtins'):
+        name = obj.__class__.__name__
+    else:
+        name = obj.__class__.__module__ + '.' + obj.__class__.__name__
+    return '%s object' % name
+
+
 def pformat(obj, verbose=False):
     """Prettyprint an object.  Either use the `pretty` library or the
     builtin `pprint`.
--- a/bundled/jinja2/setup.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/bundled/jinja2/setup.py	Fri Jun 11 21:25:17 2010 -0400
@@ -55,7 +55,7 @@
 
 setup(
     name='Jinja2',
-    version='2.4',
+    version='2.6',
     url='http://jinja.pocoo.org/',
     license='BSD',
     author='Armin Ronacher',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/CHANGES.txt	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,183 @@
+Version 2.1.1 released 2010-03-31
+
+* Change how setup.py imports ez_setup.py to try and workaround old versions
+  of setuptools.
+  http://code.google.com/p/simplejson/issues/detail?id=75
+* Fix compilation on Windows platform (and other platforms with very
+  picky compilers)
+* Corrected simplejson.__version__ and other minor doc changes.
+* Do not fail speedups tests if speedups could not be built.
+  http://code.google.com/p/simplejson/issues/detail?id=73
+
+Version 2.1.0 released 2010-03-10
+
+* Decimal serialization officially supported for encoding with
+  use_decimal=True. For encoding this encodes Decimal objects and
+  for decoding it implies parse_float=Decimal
+* Python 2.4 no longer supported (may still work, but no longer tested)
+* Decoding performance and memory utilization enhancements
+  http://bugs.python.org/issue7451
+* JSONEncoderForHTML class for escaping &, <, >
+  http://code.google.com/p/simplejson/issues/detail?id=66
+* Memoization of object keys during encoding (when using speedups)
+* Encoder changed to use PyIter_Next for list iteration to avoid
+  potential threading issues
+* Encoder changed to use iteritems rather than PyDict_Next in order to
+  support dict subclasses that have a well defined ordering
+  http://bugs.python.org/issue6105
+* indent encoding parameter changed to be a string rather than an integer
+  (integer use still supported for backwards compatibility)
+  http://code.google.com/p/simplejson/issues/detail?id=56
+* Test suite (python setup.py test) now automatically runs with and without
+  speedups
+  http://code.google.com/p/simplejson/issues/detail?id=55
+* Fixed support for older versions of easy_install (e.g. stock Mac OS X config)
+  http://code.google.com/p/simplejson/issues/detail?id=54
+* Fixed str/unicode mismatches when using ensure_ascii=False
+  http://code.google.com/p/simplejson/issues/detail?id=48
+* Fixed error message when parsing an array with trailing comma with speedups
+  http://code.google.com/p/simplejson/issues/detail?id=46
+* Refactor decoder errors to raise JSONDecodeError instead of ValueError
+  http://code.google.com/p/simplejson/issues/detail?id=45
+* New ordered_pairs_hook feature in decoder which makes it possible to
+  preserve key order. http://bugs.python.org/issue5381
+* Fixed containerless unicode float decoding (same bug as 2.0.4, oops!)
+  http://code.google.com/p/simplejson/issues/detail?id=43
+* Share PosInf definition between encoder and decoder
+* Minor reformatting to make it easier to backport simplejson changes
+  to Python 2.7/3.1 json module
+
+Version 2.0.9 released 2009-02-18
+
+* Adds cyclic GC to the Encoder and Scanner speedups, which could've
+  caused uncollectible cycles in some cases when using custom parser
+  or encoder functions
+
+Version 2.0.8 released 2009-02-15
+
+* Documentation fixes
+* Fixes encoding True and False as keys
+* Fixes checking for True and False by identity for several parameters
+
+Version 2.0.7 released 2009-01-04
+
+* Documentation fixes
+* C extension now always returns unicode strings when the input string is
+  unicode, even for empty strings
+
+Version 2.0.6 released 2008-12-19
+
+* Windows build fixes
+
+Version 2.0.5 released 2008-11-23
+
+* Fixes a segfault in the C extension when using check_circular=False and
+  encoding an invalid document
+
+Version 2.0.4 released 2008-10-24
+
+* Fixes a parsing error in the C extension when the JSON document is (only)
+  a floating point number. It would consume one too few characters in that
+  case, and claim the document invalid.
+
+Version 2.0.3 released 2008-10-11
+
+* Fixes reference leaks in the encoding speedups (sorry about that!)
+* Fixes doctest suite for Python 2.6
+* More optimizations for the decoder
+
+Version 2.0.2 released 2008-10-06
+
+* Fixes MSVC2003 build regression
+* Fixes Python 2.4 compatibility in _speedups.c
+
+Version 2.0.1 released 2008-09-29
+
+* Fixes long encoding regression introduced in 2.0.0
+* Fixes MinGW build regression introduced in 2.0.0
+
+Version 2.0.0 released 2008-09-27
+
+* optimized Python encoding path
+* optimized Python decoding path
+* optimized C encoding path
+* optimized C decoding path
+* switched to sphinx docs (nearly the same as the json module in python 2.6)
+
+Version 1.9.3 released 2008-09-23
+
+* Decoding is significantly faster (for our internal benchmarks)
+* Pretty-printing tool changed from simplejson to simplejson.tool for better
+  Python 2.6 comaptibility
+* Misc. bug fixes
+
+Version 1.9 released 2008-05-03
+
+* Rewrote test suite with unittest and doctest (no more nosetest dependency)
+* Better PEP 7 and PEP 8 source compliance
+* Removed simplejson.jsonfilter demo module
+* simplejson.jsonfilter is no longer included
+
+Version 1.8.1 released 2008-03-24
+
+* Optional C extension for accelerating the decoding of JSON strings
+* Command line interface for pretty-printing JSON (via python -msimplejson)
+* Decoding of integers and floats is now extensible (e.g. to use Decimal) via
+  parse_int, parse_float options.
+* Subversion and issue tracker moved to google code:
+  http://code.google.com/p/simplejson/
+* "/" is no longer escaped, so if you're embedding JSON directly in HTML
+  you'll want to use .replace("/", "\\/") to prevent a close-tag attack.
+
+Version 1.7 released 2007-03-18
+
+* Improves encoding performance with an optional C extension to speed up
+  str/unicode encoding (by 10-150x or so), which yields an overall speed
+  boost of 2x+ (JSON is string-heavy).
+* Support for encoding unicode code points outside the BMP to UTF-16
+  surrogate code pairs (specified by the Strings section of RFC 4627).
+
+Version 1.6 released 2007-03-03
+
+* Improved str support for encoding. Previous versions of simplejson
+  integrated strings directly into the output stream, this version ensures
+  they're of a particular encoding (default is UTF-8) so that the output
+  stream is valid.
+
+Version 1.5 released 2007-01-18
+
+* Better Python 2.5 compatibility
+* Better Windows compatibility
+* indent encoding parameter for pretty printing
+* separators encoding parameter for generating optimally compact JSON
+
+Version 1.3 released 2006-04-01
+
+* The optional object_hook function is called upon decoding of any JSON
+  object literal, and its return value is used instead of the dict that
+  would normally be used. This can be used to efficiently implement
+  features such as JSON-RPC class hinting, or other custom decodings of
+  JSON. See the documentation for more information.
+
+Version 1.1 released 2005-12-31
+
+* Renamed from simple_json to simplejson to comply with PEP 8 module naming
+  guidelines
+* Full set of documentation
+* More tests
+* The encoder and decoder have been extended to understand NaN, Infinity, and
+  -Infinity (but this can be turned off via allow_nan=False for strict JSON
+  compliance)
+* The decoder's scanner has been fixed so that it no longer accepts invalid
+  JSON documents
+* The decoder now reports line and column information as well as character
+  numbers for easier debugging
+* The encoder now has a circular reference checker, which can be optionally
+  disabled with check_circular=False
+* dump, dumps, load, loads now accept an optional cls kwarg to use an
+  alternate JSONEncoder or JSONDecoder class for convenience.
+* The read/write compatibility shim for json-py now have deprecation warnings
+ 
+Version 1.0 released 2005-12-25
+
+ * Initial release
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/LICENSE.txt	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,19 @@
+Copyright (c) 2006 Bob Ippolito
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/PKG-INFO	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,30 @@
+Metadata-Version: 1.0
+Name: simplejson
+Version: 2.1.1
+Summary: Simple, fast, extensible JSON encoder/decoder for Python
+Home-page: http://undefined.org/python/#simplejson
+Author: Bob Ippolito
+Author-email: bob@redivi.com
+License: MIT License
+Description: 
+        simplejson is a simple, fast, complete, correct and extensible
+        JSON <http://json.org> encoder and decoder for Python 2.5+.  It is
+        pure Python code with no dependencies, but includes an optional C
+        extension for a serious speed boost.
+        
+        simplejson is the externally maintained development version of the
+        json library included with Python 2.6 and Python 3.0, but maintains
+        backwards compatibility with Python 2.5.
+        
+        The encoder may be subclassed to provide serialization in any kind of
+        situation, without any special support by the objects to be serialized
+        (somewhat like pickle).
+        
+        The decoder can handle incoming JSON strings of any specified encoding
+        (UTF-8 by default).
+        
+Platform: any
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/conf.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+#
+# simplejson documentation build configuration file, created by
+# sphinx-quickstart on Fri Sep 26 18:58:30 2008.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('some/directory'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = 'simplejson'
+copyright = '2008, Bob Ippolito'
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = '2.1'
+# The full version, including alpha/beta/rc tags.
+release = '2.1.1'
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directories, that shouldn't be searched
+# for source files.
+#exclude_dirs = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+html_style = 'default.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+html_use_modindex = False
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+html_file_suffix = '.html'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'simplejsondoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('index', 'simplejson.tex', 'simplejson Documentation',
+   'Bob Ippolito', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/ez_setup.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,284 @@
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c11"
+DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
+    'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
+    'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
+    'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
+    'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
+    'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
+    'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
+    'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>="+version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+        else:
+            del pkg_resources, sys.modules['pkg_resources']    # reload ok
+            return do_download()
+    except pkg_resources.DistributionNotFound:
+        return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/index.rst	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,503 @@
+:mod:`simplejson` --- JSON encoder and decoder
+==============================================
+
+.. module:: simplejson
+   :synopsis: Encode and decode the JSON format.
+.. moduleauthor:: Bob Ippolito <bob@redivi.com>
+.. sectionauthor:: Bob Ippolito <bob@redivi.com>
+
+JSON (JavaScript Object Notation) <http://json.org> is a subset of JavaScript
+syntax (ECMA-262 3rd edition) used as a lightweight data interchange format.
+
+:mod:`simplejson` exposes an API familiar to users of the standard library
+:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
+version of the :mod:`json` library contained in Python 2.6, but maintains
+compatibility with Python 2.5 and (currently) has
+significant performance advantages, even without using the optional C
+extension for speedups.
+
+Encoding basic Python object hierarchies::
+
+    >>> import simplejson as json
+    >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
+    '["foo", {"bar": ["baz", null, 1.0, 2]}]'
+    >>> print json.dumps("\"foo\bar")
+    "\"foo\bar"
+    >>> print json.dumps(u'\u1234')
+    "\u1234"
+    >>> print json.dumps('\\')
+    "\\"
+    >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
+    {"a": 0, "b": 0, "c": 0}
+    >>> from StringIO import StringIO
+    >>> io = StringIO()
+    >>> json.dump(['streaming API'], io)
+    >>> io.getvalue()
+    '["streaming API"]'
+
+Compact encoding::
+
+    >>> import simplejson as json
+    >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
+    '[1,2,3,{"4":5,"6":7}]'
+
+Pretty printing::
+
+    >>> import simplejson as json
+    >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4 * ' ')
+    >>> print '\n'.join([l.rstrip() for l in  s.splitlines()])
+    {
+        "4": 5,
+        "6": 7
+    }
+
+Decoding JSON::
+
+    >>> import simplejson as json
+    >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
+    >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
+    True
+    >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
+    True
+    >>> from StringIO import StringIO
+    >>> io = StringIO('["streaming API"]')
+    >>> json.load(io)[0] == 'streaming API'
+    True
+
+Using Decimal instead of float::
+
+    >>> import simplejson as json
+    >>> from decimal import Decimal
+    >>> json.loads('1.1', use_decimal=True) == Decimal('1.1')
+    True
+    >>> json.dumps(Decimal('1.1'), use_decimal=True) == '1.1'
+    True
+
+Specializing JSON object decoding::
+
+    >>> import simplejson as json
+    >>> def as_complex(dct):
+    ...     if '__complex__' in dct:
+    ...         return complex(dct['real'], dct['imag'])
+    ...     return dct
+    ...
+    >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
+    ...     object_hook=as_complex)
+    (1+2j)
+    >>> import decimal
+    >>> json.loads('1.1', parse_float=decimal.Decimal) == decimal.Decimal('1.1')
+    True
+
+Specializing JSON object encoding::
+
+    >>> import simplejson as json
+    >>> def encode_complex(obj):
+    ...     if isinstance(obj, complex):
+    ...         return [obj.real, obj.imag]
+    ...     raise TypeError(repr(o) + " is not JSON serializable")
+    ...
+    >>> json.dumps(2 + 1j, default=encode_complex)
+    '[2.0, 1.0]'
+    >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
+    '[2.0, 1.0]'
+    >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
+    '[2.0, 1.0]'
+
+
+.. highlight:: none
+
+Using :mod:`simplejson.tool` from the shell to validate and pretty-print::
+
+    $ echo '{"json":"obj"}' | python -m simplejson.tool
+    {
+        "json": "obj"
+    }
+    $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+    Expecting property name: line 1 column 2 (char 2)
+
+.. highlight:: python
+
+.. note::
+
+   The JSON produced by this module's default settings is a subset of
+   YAML, so it may be used as a serializer for that as well.
+
+
+Basic Usage
+-----------
+
+.. function:: dump(obj, fp[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, **kw]]]]]]]]]]])
+
+   Serialize *obj* as a JSON formatted stream to *fp* (a ``.write()``-supporting
+   file-like object).
+
+   If *skipkeys* is true (default: ``False``), then dict keys that are not
+   of a basic type (:class:`str`, :class:`unicode`, :class:`int`, :class:`long`,
+   :class:`float`, :class:`bool`, ``None``) will be skipped instead of raising a
+   :exc:`TypeError`.
+
+   If *ensure_ascii* is false (default: ``True``), then some chunks written
+   to *fp* may be :class:`unicode` instances, subject to normal Python
+   :class:`str` to :class:`unicode` coercion rules.  Unless ``fp.write()``
+   explicitly understands :class:`unicode` (as in :func:`codecs.getwriter`) this
+   is likely to cause an error. It's best to leave the default settings, because
+   they are safe and it is highly optimized.
+
+   If *check_circular* is false (default: ``True``), then the circular
+   reference check for container types will be skipped and a circular reference
+   will result in an :exc:`OverflowError` (or worse).
+
+   If *allow_nan* is false (default: ``True``), then it will be a
+   :exc:`ValueError` to serialize out of range :class:`float` values (``nan``,
+   ``inf``, ``-inf``) in strict compliance of the JSON specification.
+   If *allow_nan* is true, their JavaScript equivalents will be used
+   (``NaN``, ``Infinity``, ``-Infinity``).
+
+   If *indent* is a string, then JSON array elements and object members
+   will be pretty-printed with a newline followed by that string repeated
+   for each level of nesting. ``None`` (the default) selects the most compact
+   representation without any newlines. For backwards compatibility with
+   versions of simplejson earlier than 2.1.0, an integer is also accepted
+   and is converted to a string with that many spaces.
+
+   .. versionchanged:: 2.1.0
+      Changed *indent* from an integer number of spaces to a string.
+
+   If specified, *separators* should be an ``(item_separator, dict_separator)``
+   tuple.  By default, ``(', ', ': ')`` are used.  To get the most compact JSON
+   representation, you should specify ``(',', ':')`` to eliminate whitespace.
+
+   *encoding* is the character encoding for str instances, default is
+   ``'utf-8'``.
+
+   *default(obj)* is a function that should return a serializable version of
+   *obj* or raise :exc:`TypeError`.  The default simply raises :exc:`TypeError`.
+
+   To use a custom :class:`JSONEncoder` subclass (e.g. one that overrides the
+   :meth:`default` method to serialize additional types), specify it with the
+   *cls* kwarg.
+   
+   If *use_decimal* is true (default: ``False``) then :class:`decimal.Decimal`
+   will be natively serialized to JSON with full precision.
+   
+   .. versionchanged:: 2.1.0
+      *use_decimal* is new in 2.1.0.
+
+    .. note::
+
+        JSON is not a framed protocol so unlike :mod:`pickle` or :mod:`marshal` it
+        does not make sense to serialize more than one JSON document without some
+        container protocol to delimit them.
+
+
+.. function:: dumps(obj[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, **kw]]]]]]]]]]])
+
+   Serialize *obj* to a JSON formatted :class:`str`.
+
+   If *ensure_ascii* is false, then the return value will be a
+   :class:`unicode` instance.  The other arguments have the same meaning as in
+   :func:`dump`. Note that the default *ensure_ascii* setting has much
+   better performance.
+
+
+.. function:: load(fp[, encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, use_decimal[, **kw]]]]]]]]])
+
+   Deserialize *fp* (a ``.read()``-supporting file-like object containing a JSON
+   document) to a Python object.
+
+   If the contents of *fp* are encoded with an ASCII based encoding other than
+   UTF-8 (e.g. latin-1), then an appropriate *encoding* name must be specified.
+   Encodings that are not ASCII based (such as UCS-2) are not allowed, and
+   should be wrapped with ``codecs.getreader(fp)(encoding)``, or simply decoded
+   to a :class:`unicode` object and passed to :func:`loads`. The default
+   setting of ``'utf-8'`` is fastest and should be using whenever possible.
+
+   If *fp.read()* returns :class:`str` then decoded JSON strings that contain
+   only ASCII characters may be parsed as :class:`str` for performance and
+   memory reasons. If your code expects only :class:`unicode` the appropriate
+   solution is to wrap fp with a reader as demonstrated above.
+
+   *object_hook* is an optional function that will be called with the result of
+   any object literal decode (a :class:`dict`).  The return value of
+   *object_hook* will be used instead of the :class:`dict`.  This feature can be used
+   to implement custom decoders (e.g. JSON-RPC class hinting).
+
+   *object_pairs_hook* is an optional function that will be called with the
+   result of any object literal decode with an ordered list of pairs.  The
+   return value of *object_pairs_hook* will be used instead of the
+   :class:`dict`.  This feature can be used to implement custom decoders that
+   rely on the order that the key and value pairs are decoded (for example,
+   :class:`collections.OrderedDict` will remember the order of insertion). If
+   *object_hook* is also defined, the *object_pairs_hook* takes priority.
+
+   .. versionchanged:: 2.1.0
+      Added support for *object_pairs_hook*.
+
+   *parse_float*, if specified, will be called with the string of every JSON
+   float to be decoded.  By default, this is equivalent to ``float(num_str)``.
+   This can be used to use another datatype or parser for JSON floats
+   (e.g. :class:`decimal.Decimal`).
+
+   *parse_int*, if specified, will be called with the string of every JSON int
+   to be decoded.  By default, this is equivalent to ``int(num_str)``.  This can
+   be used to use another datatype or parser for JSON integers
+   (e.g. :class:`float`).
+
+   *parse_constant*, if specified, will be called with one of the following
+   strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``.  This can be used to
+   raise an exception if invalid JSON numbers are encountered.
+
+   If *use_decimal* is true (default: ``False``) then *parse_float* is set to
+   :class:`decimal.Decimal`. This is a convenience for parity with the
+   :func:`dump` parameter.
+   
+   .. versionchanged:: 2.1.0
+      *use_decimal* is new in 2.1.0.
+
+   To use a custom :class:`JSONDecoder` subclass, specify it with the ``cls``
+   kwarg.  Additional keyword arguments will be passed to the constructor of the
+   class.
+
+    .. note::
+
+        :func:`load` will read the rest of the file-like object as a string and
+        then call :func:`loads`. It does not stop at the end of the first valid
+        JSON document it finds and it will raise an error if there is anything
+        other than whitespace after the document. Except for files containing
+        only one JSON document, it is recommended to use :func:`loads`.
+
+
+.. function:: loads(s[, encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, use_decimal[, **kw]]]]]]]]])
+
+   Deserialize *s* (a :class:`str` or :class:`unicode` instance containing a JSON
+   document) to a Python object.
+
+   If *s* is a :class:`str` instance and is encoded with an ASCII based encoding
+   other than UTF-8 (e.g. latin-1), then an appropriate *encoding* name must be
+   specified.  Encodings that are not ASCII based (such as UCS-2) are not
+   allowed and should be decoded to :class:`unicode` first.
+
+   If *s* is a :class:`str` then decoded JSON strings that contain
+   only ASCII characters may be parsed as :class:`str` for performance and
+   memory reasons. If your code expects only :class:`unicode` the appropriate
+   solution is decode *s* to :class:`unicode` prior to calling loads.
+
+   The other arguments have the same meaning as in :func:`load`.
+
+
+Encoders and decoders
+---------------------
+
+.. class:: JSONDecoder([encoding[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, strict]]]]]]])
+
+   Simple JSON decoder.
+
+   Performs the following translations in decoding by default:
+
+   +---------------+-------------------+
+   | JSON          | Python            |
+   +===============+===================+
+   | object        | dict              |
+   +---------------+-------------------+
+   | array         | list              |
+   +---------------+-------------------+
+   | string        | unicode           |
+   +---------------+-------------------+
+   | number (int)  | int, long         |
+   +---------------+-------------------+
+   | number (real) | float             |
+   +---------------+-------------------+
+   | true          | True              |
+   +---------------+-------------------+
+   | false         | False             |
+   +---------------+-------------------+
+   | null          | None              |
+   +---------------+-------------------+
+
+   It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as their
+   corresponding ``float`` values, which is outside the JSON spec.
+
+   *encoding* determines the encoding used to interpret any :class:`str` objects
+   decoded by this instance (``'utf-8'`` by default).  It has no effect when decoding
+   :class:`unicode` objects.
+
+   Note that currently only encodings that are a superset of ASCII work, strings
+   of other encodings should be passed in as :class:`unicode`.
+
+   *object_hook* is an optional function that will be called with the result of
+   every JSON object decoded and its return value will be used in place of the
+   given :class:`dict`.  This can be used to provide custom deserializations
+   (e.g. to support JSON-RPC class hinting).
+
+   *object_pairs_hook* is an optional function that will be called with the
+   result of any object literal decode with an ordered list of pairs.  The
+   return value of *object_pairs_hook* will be used instead of the
+   :class:`dict`.  This feature can be used to implement custom decoders that
+   rely on the order that the key and value pairs are decoded (for example,
+   :class:`collections.OrderedDict` will remember the order of insertion). If
+   *object_hook* is also defined, the *object_pairs_hook* takes priority.
+
+   .. versionchanged:: 2.1.0
+      Added support for *object_pairs_hook*.
+
+   *parse_float*, if specified, will be called with the string of every JSON
+   float to be decoded.  By default, this is equivalent to ``float(num_str)``.
+   This can be used to use another datatype or parser for JSON floats
+   (e.g. :class:`decimal.Decimal`).
+
+   *parse_int*, if specified, will be called with the string of every JSON int
+   to be decoded.  By default, this is equivalent to ``int(num_str)``.  This can
+   be used to use another datatype or parser for JSON integers
+   (e.g. :class:`float`).
+
+   *parse_constant*, if specified, will be called with one of the following
+   strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``.  This can be used to
+   raise an exception if invalid JSON numbers are encountered.
+
+   *strict* controls the parser's behavior when it encounters an invalid
+   control character in a string. The default setting of ``True`` means that
+   unescaped control characters are parse errors, if ``False`` then control
+   characters will be allowed in strings.
+
+   .. method:: decode(s)
+
+      Return the Python representation of *s* (a :class:`str` or
+      :class:`unicode` instance containing a JSON document)
+
+      If *s* is a :class:`str` then decoded JSON strings that contain
+      only ASCII characters may be parsed as :class:`str` for performance and
+      memory reasons. If your code expects only :class:`unicode` the
+      appropriate solution is decode *s* to :class:`unicode` prior to calling
+      decode.
+
+   .. method:: raw_decode(s)
+
+      Decode a JSON document from *s* (a :class:`str` or :class:`unicode`
+      beginning with a JSON document) and return a 2-tuple of the Python
+      representation and the index in *s* where the document ended.
+
+      This can be used to decode a JSON document from a string that may have
+      extraneous data at the end.
+
+
+.. class:: JSONEncoder([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default]]]]]]]]])
+
+   Extensible JSON encoder for Python data structures.
+
+   Supports the following objects and types by default:
+
+   +-------------------+---------------+
+   | Python            | JSON          |
+   +===================+===============+
+   | dict              | object        |
+   +-------------------+---------------+
+   | list, tuple       | array         |
+   +-------------------+---------------+
+   | str, unicode      | string        |
+   +-------------------+---------------+
+   | int, long, float  | number        |
+   +-------------------+---------------+
+   | True              | true          |
+   +-------------------+---------------+
+   | False             | false         |
+   +-------------------+---------------+
+   | None              | null          |
+   +-------------------+---------------+
+
+   To extend this to recognize other objects, subclass and implement a
+   :meth:`default` method with another method that returns a serializable object
+   for ``o`` if possible, otherwise it should call the superclass implementation
+   (to raise :exc:`TypeError`).
+
+   If *skipkeys* is false (the default), then it is a :exc:`TypeError` to
+   attempt encoding of keys that are not str, int, long, float or None.  If
+   *skipkeys* is true, such items are simply skipped.
+
+   If *ensure_ascii* is true (the default), the output is guaranteed to be
+   :class:`str` objects with all incoming unicode characters escaped.  If
+   *ensure_ascii* is false, the output will be a unicode object.
+
+   If *check_circular* is false (the default), then lists, dicts, and custom
+   encoded objects will be checked for circular references during encoding to
+   prevent an infinite recursion (which would cause an :exc:`OverflowError`).
+   Otherwise, no such check takes place.
+
+   If *allow_nan* is true (the default), then ``NaN``, ``Infinity``, and
+   ``-Infinity`` will be encoded as such.  This behavior is not JSON
+   specification compliant, but is consistent with most JavaScript based
+   encoders and decoders.  Otherwise, it will be a :exc:`ValueError` to encode
+   such floats.
+
+   If *sort_keys* is true (not the default), then the output of dictionaries
+   will be sorted by key; this is useful for regression tests to ensure that
+   JSON serializations can be compared on a day-to-day basis.
+
+   If *indent* is a string, then JSON array elements and object members
+   will be pretty-printed with a newline followed by that string repeated
+   for each level of nesting. ``None`` (the default) selects the most compact
+   representation without any newlines. For backwards compatibility with
+   versions of simplejson earlier than 2.1.0, an integer is also accepted
+   and is converted to a string with that many spaces.
+
+   .. versionchanged:: 2.1.0
+      Changed *indent* from an integer number of spaces to a string.
+
+   If specified, *separators* should be an ``(item_separator, key_separator)``
+   tuple.  By default, ``(', ', ': ')`` are used.  To get the most compact JSON
+   representation, you should specify ``(',', ':')`` to eliminate whitespace.
+
+   If specified, *default* should be a function that gets called for objects
+   that can't otherwise be serialized.  It should return a JSON encodable
+   version of the object or raise a :exc:`TypeError`.
+
+   If *encoding* is not ``None``, then all input strings will be transformed
+   into unicode using that encoding prior to JSON-encoding.  The default is
+   ``'utf-8'``.
+
+
+   .. method:: default(o)
+
+      Implement this method in a subclass such that it returns a serializable
+      object for *o*, or calls the base implementation (to raise a
+      :exc:`TypeError`).
+
+      For example, to support arbitrary iterators, you could implement default
+      like this::
+
+         def default(self, o):
+            try:
+                iterable = iter(o)
+            except TypeError:
+                pass
+            else:
+                return list(iterable)
+            return JSONEncoder.default(self, o)
+
+
+   .. method:: encode(o)
+
+      Return a JSON string representation of a Python data structure, *o*.  For
+      example::
+
+        >>> import simplejson as json
+        >>> json.JSONEncoder().encode({"foo": ["bar", "baz"]})
+        '{"foo": ["bar", "baz"]}'
+
+
+   .. method:: iterencode(o)
+
+      Encode the given object, *o*, and yield each string representation as
+      available.  For example::
+
+            for chunk in JSONEncoder().iterencode(bigobject):
+                mysocket.write(chunk)
+
+      Note that :meth:`encode` has much better performance than
+      :meth:`iterencode`.
+
+.. class:: JSONEncoderForHTML([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default]]]]]]]]])
+
+   Subclass of :class:`JSONEncoder` that escapes &, <, and > for embedding in HTML.
+
+   .. versionchanged:: 2.1.0
+      New in 2.1.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/setup.cfg	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/setup.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+
+import sys
+try:
+    import setuptools
+except ImportError:
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+from setuptools import setup, find_packages, Extension, Feature
+from distutils.command.build_ext import build_ext
+from distutils.errors import CCompilerError, DistutilsExecError, \
+    DistutilsPlatformError
+
+VERSION = '2.1.1'
+DESCRIPTION = "Simple, fast, extensible JSON encoder/decoder for Python"
+LONG_DESCRIPTION = """
+simplejson is a simple, fast, complete, correct and extensible
+JSON <http://json.org> encoder and decoder for Python 2.5+.  It is
+pure Python code with no dependencies, but includes an optional C
+extension for a serious speed boost.
+
+simplejson is the externally maintained development version of the
+json library included with Python 2.6 and Python 3.0, but maintains
+backwards compatibility with Python 2.5.
+
+The encoder may be subclassed to provide serialization in any kind of
+situation, without any special support by the objects to be serialized
+(somewhat like pickle).
+
+The decoder can handle incoming JSON strings of any specified encoding
+(UTF-8 by default).
+"""
+
+CLASSIFIERS = filter(None, map(str.strip,
+"""
+Intended Audience :: Developers
+License :: OSI Approved :: MIT License
+Programming Language :: Python
+Topic :: Software Development :: Libraries :: Python Modules
+""".splitlines()))
+
+
+speedups = Feature(
+    "optional C speed-enhancement module",
+    standard=True,
+    ext_modules = [
+        Extension("simplejson._speedups", ["simplejson/_speedups.c"]),
+    ],
+)
+
+if sys.platform == 'win32' and sys.version_info > (2, 6):
+   # 2.6's distutils.msvc9compiler can raise an IOError when failing to
+   # find the compiler
+   ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError,
+                 IOError)
+else:
+   ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
+
+class BuildFailed(Exception):
+    pass
+
+class ve_build_ext(build_ext):
+    # This class allows C extension building to fail.
+
+    def run(self):
+        try:
+            build_ext.run(self)
+        except DistutilsPlatformError, x:
+            raise BuildFailed()
+
+    def build_extension(self, ext):
+        try:
+            build_ext.build_extension(self, ext)
+        except ext_errors, x:
+            raise BuildFailed()
+
+def run_setup(with_binary):
+    if with_binary:
+        features = {'speedups': speedups}
+    else:
+        features = {}
+
+    setup(
+        name="simplejson",
+        version=VERSION,
+        description=DESCRIPTION,
+        long_description=LONG_DESCRIPTION,
+        classifiers=CLASSIFIERS,
+        author="Bob Ippolito",
+        author_email="bob@redivi.com",
+        url="http://undefined.org/python/#simplejson",
+        license="MIT License",
+        packages=find_packages(exclude=['ez_setup']),
+        platforms=['any'],
+        test_suite="simplejson.tests.all_tests_suite",
+        zip_safe=True,
+        features=features,
+        cmdclass={'build_ext': ve_build_ext},
+    )
+
+try:
+    run_setup(True)
+except BuildFailed:
+    BUILD_EXT_WARNING = "WARNING: The C extension could not be compiled, speedups are not enabled."
+    print '*' * 75
+    print BUILD_EXT_WARNING
+    print "Failure information, if any, is above."
+    print "I'm retrying the build without the C extension now."
+    print '*' * 75
+
+    run_setup(False)
+
+    print '*' * 75
+    print BUILD_EXT_WARNING
+    print "Plain-Python installation succeeded."
+    print '*' * 75
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/simplejson/__init__.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,437 @@
+r"""JSON (JavaScript Object Notation) <http://json.org> is a subset of
+JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
+interchange format.
+
+:mod:`simplejson` exposes an API familiar to users of the standard library
+:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
+version of the :mod:`json` library contained in Python 2.6, but maintains
+compatibility with Python 2.4 and Python 2.5 and (currently) has
+significant performance advantages, even without using the optional C
+extension for speedups.
+
+Encoding basic Python object hierarchies::
+
+    >>> import simplejson as json
+    >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
+    '["foo", {"bar": ["baz", null, 1.0, 2]}]'
+    >>> print json.dumps("\"foo\bar")
+    "\"foo\bar"
+    >>> print json.dumps(u'\u1234')
+    "\u1234"
+    >>> print json.dumps('\\')
+    "\\"
+    >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
+    {"a": 0, "b": 0, "c": 0}
+    >>> from StringIO import StringIO
+    >>> io = StringIO()
+    >>> json.dump(['streaming API'], io)
+    >>> io.getvalue()
+    '["streaming API"]'
+
+Compact encoding::
+
+    >>> import simplejson as json
+    >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
+    '[1,2,3,{"4":5,"6":7}]'
+
+Pretty printing::
+
+    >>> import simplejson as json
+    >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent='    ')
+    >>> print '\n'.join([l.rstrip() for l in  s.splitlines()])
+    {
+        "4": 5,
+        "6": 7
+    }
+
+Decoding JSON::
+
+    >>> import simplejson as json
+    >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
+    >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
+    True
+    >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
+    True
+    >>> from StringIO import StringIO
+    >>> io = StringIO('["streaming API"]')
+    >>> json.load(io)[0] == 'streaming API'
+    True
+
+Specializing JSON object decoding::
+
+    >>> import simplejson as json
+    >>> def as_complex(dct):
+    ...     if '__complex__' in dct:
+    ...         return complex(dct['real'], dct['imag'])
+    ...     return dct
+    ...
+    >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
+    ...     object_hook=as_complex)
+    (1+2j)
+    >>> from decimal import Decimal
+    >>> json.loads('1.1', parse_float=Decimal) == Decimal('1.1')
+    True
+
+Specializing JSON object encoding::
+
+    >>> import simplejson as json
+    >>> def encode_complex(obj):
+    ...     if isinstance(obj, complex):
+    ...         return [obj.real, obj.imag]
+    ...     raise TypeError(repr(o) + " is not JSON serializable")
+    ...
+    >>> json.dumps(2 + 1j, default=encode_complex)
+    '[2.0, 1.0]'
+    >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
+    '[2.0, 1.0]'
+    >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
+    '[2.0, 1.0]'
+
+
+Using simplejson.tool from the shell to validate and pretty-print::
+
+    $ echo '{"json":"obj"}' | python -m simplejson.tool
+    {
+        "json": "obj"
+    }
+    $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+    Expecting property name: line 1 column 2 (char 2)
+"""
+__version__ = '2.1.1'
+__all__ = [
+    'dump', 'dumps', 'load', 'loads',
+    'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
+    'OrderedDict',
+]
+
+__author__ = 'Bob Ippolito <bob@redivi.com>'
+
+from decimal import Decimal
+
+from decoder import JSONDecoder, JSONDecodeError
+from encoder import JSONEncoder
+def _import_OrderedDict():
+    import collections
+    try:
+        return collections.OrderedDict
+    except AttributeError:
+        import ordered_dict
+        return ordered_dict.OrderedDict
+OrderedDict = _import_OrderedDict()
+
+def _import_c_make_encoder():
+    try:
+        from simplejson._speedups import make_encoder
+        return make_encoder
+    except ImportError:
+        return None
+
+_default_encoder = JSONEncoder(
+    skipkeys=False,
+    ensure_ascii=True,
+    check_circular=True,
+    allow_nan=True,
+    indent=None,
+    separators=None,
+    encoding='utf-8',
+    default=None,
+    use_decimal=False,
+)
+
+def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
+        allow_nan=True, cls=None, indent=None, separators=None,
+        encoding='utf-8', default=None, use_decimal=False, **kw):
+    """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
+    ``.write()``-supporting file-like object).
+
+    If ``skipkeys`` is true then ``dict`` keys that are not basic types
+    (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+    will be skipped instead of raising a ``TypeError``.
+
+    If ``ensure_ascii`` is false, then the some chunks written to ``fp``
+    may be ``unicode`` instances, subject to normal Python ``str`` to
+    ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
+    understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
+    to cause an error.
+
+    If ``check_circular`` is false, then the circular reference check
+    for container types will be skipped and a circular reference will
+    result in an ``OverflowError`` (or worse).
+
+    If ``allow_nan`` is false, then it will be a ``ValueError`` to
+    serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
+    in strict compliance of the JSON specification, instead of using the
+    JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+    If *indent* is a string, then JSON array elements and object members
+    will be pretty-printed with a newline followed by that string repeated
+    for each level of nesting. ``None`` (the default) selects the most compact
+    representation without any newlines. For backwards compatibility with
+    versions of simplejson earlier than 2.1.0, an integer is also accepted
+    and is converted to a string with that many spaces.
+
+    If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+    then it will be used instead of the default ``(', ', ': ')`` separators.
+    ``(',', ':')`` is the most compact JSON representation.
+
+    ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+    ``default(obj)`` is a function that should return a serializable version
+    of obj or raise TypeError. The default simply raises TypeError.
+
+    If *use_decimal* is true (default: ``False``) then decimal.Decimal
+    will be natively serialized to JSON with full precision.
+
+    To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+    ``.default()`` method to serialize additional types), specify it with
+    the ``cls`` kwarg.
+
+    """
+    # cached encoder
+    if (not skipkeys and ensure_ascii and
+        check_circular and allow_nan and
+        cls is None and indent is None and separators is None and
+        encoding == 'utf-8' and default is None and not kw):
+        iterable = _default_encoder.iterencode(obj)
+    else:
+        if cls is None:
+            cls = JSONEncoder
+        iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+            check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+            separators=separators, encoding=encoding,
+            default=default, use_decimal=use_decimal, **kw).iterencode(obj)
+    # could accelerate with writelines in some versions of Python, at
+    # a debuggability cost
+    for chunk in iterable:
+        fp.write(chunk)
+
+
+def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
+        allow_nan=True, cls=None, indent=None, separators=None,
+        encoding='utf-8', default=None, use_decimal=False, **kw):
+    """Serialize ``obj`` to a JSON formatted ``str``.
+
+    If ``skipkeys`` is false then ``dict`` keys that are not basic types
+    (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+    will be skipped instead of raising a ``TypeError``.
+
+    If ``ensure_ascii`` is false, then the return value will be a
+    ``unicode`` instance subject to normal Python ``str`` to ``unicode``
+    coercion rules instead of being escaped to an ASCII ``str``.
+
+    If ``check_circular`` is false, then the circular reference check
+    for container types will be skipped and a circular reference will
+    result in an ``OverflowError`` (or worse).
+
+    If ``allow_nan`` is false, then it will be a ``ValueError`` to
+    serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
+    strict compliance of the JSON specification, instead of using the
+    JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+    If ``indent`` is a string, then JSON array elements and object members
+    will be pretty-printed with a newline followed by that string repeated
+    for each level of nesting. ``None`` (the default) selects the most compact
+    representation without any newlines. For backwards compatibility with
+    versions of simplejson earlier than 2.1.0, an integer is also accepted
+    and is converted to a string with that many spaces.
+
+    If ``separators`` is an ``(item_separator, dict_separator)`` tuple
+    then it will be used instead of the default ``(', ', ': ')`` separators.
+    ``(',', ':')`` is the most compact JSON representation.
+
+    ``encoding`` is the character encoding for str instances, default is UTF-8.
+
+    ``default(obj)`` is a function that should return a serializable version
+    of obj or raise TypeError. The default simply raises TypeError.
+
+    If *use_decimal* is true (default: ``False``) then decimal.Decimal
+    will be natively serialized to JSON with full precision.
+
+    To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+    ``.default()`` method to serialize additional types), specify it with
+    the ``cls`` kwarg.
+
+    """
+    # cached encoder
+    if (not skipkeys and ensure_ascii and
+        check_circular and allow_nan and
+        cls is None and indent is None and separators is None and
+        encoding == 'utf-8' and default is None and not use_decimal
+        and not kw):
+        return _default_encoder.encode(obj)
+    if cls is None:
+        cls = JSONEncoder
+    return cls(
+        skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
+        separators=separators, encoding=encoding, default=default,
+        use_decimal=use_decimal, **kw).encode(obj)
+
+
+_default_decoder = JSONDecoder(encoding=None, object_hook=None,
+                               object_pairs_hook=None)
+
+
+def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
+        parse_int=None, parse_constant=None, object_pairs_hook=None,
+        use_decimal=False, **kw):
+    """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
+    a JSON document) to a Python object.
+
+    *encoding* determines the encoding used to interpret any
+    :class:`str` objects decoded by this instance (``'utf-8'`` by
+    default).  It has no effect when decoding :class:`unicode` objects.
+
+    Note that currently only encodings that are a superset of ASCII work,
+    strings of other encodings should be passed in as :class:`unicode`.
+
+    *object_hook*, if specified, will be called with the result of every
+    JSON object decoded and its return value will be used in place of the
+    given :class:`dict`.  This can be used to provide custom
+    deserializations (e.g. to support JSON-RPC class hinting).
+
+    *object_pairs_hook* is an optional function that will be called with
+    the result of any object literal decode with an ordered list of pairs.
+    The return value of *object_pairs_hook* will be used instead of the
+    :class:`dict`.  This feature can be used to implement custom decoders
+    that rely on the order that the key and value pairs are decoded (for
+    example, :func:`collections.OrderedDict` will remember the order of
+    insertion). If *object_hook* is also defined, the *object_pairs_hook*
+    takes priority.
+
+    *parse_float*, if specified, will be called with the string of every
+    JSON float to be decoded.  By default, this is equivalent to
+    ``float(num_str)``. This can be used to use another datatype or parser
+    for JSON floats (e.g. :class:`decimal.Decimal`).
+
+    *parse_int*, if specified, will be called with the string of every
+    JSON int to be decoded.  By default, this is equivalent to
+    ``int(num_str)``.  This can be used to use another datatype or parser
+    for JSON integers (e.g. :class:`float`).
+
+    *parse_constant*, if specified, will be called with one of the
+    following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``.  This
+    can be used to raise an exception if invalid JSON numbers are
+    encountered.
+
+    If *use_decimal* is true (default: ``False``) then it implies
+    parse_float=decimal.Decimal for parity with ``dump``.
+
+    To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+    kwarg.
+
+    """
+    return loads(fp.read(),
+        encoding=encoding, cls=cls, object_hook=object_hook,
+        parse_float=parse_float, parse_int=parse_int,
+        parse_constant=parse_constant, object_pairs_hook=object_pairs_hook,
+        use_decimal=use_decimal, **kw)
+
+
+def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
+        parse_int=None, parse_constant=None, object_pairs_hook=None,
+        use_decimal=False, **kw):
+    """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
+    document) to a Python object.
+
+    *encoding* determines the encoding used to interpret any
+    :class:`str` objects decoded by this instance (``'utf-8'`` by
+    default).  It has no effect when decoding :class:`unicode` objects.
+
+    Note that currently only encodings that are a superset of ASCII work,
+    strings of other encodings should be passed in as :class:`unicode`.
+
+    *object_hook*, if specified, will be called with the result of every
+    JSON object decoded and its return value will be used in place of the
+    given :class:`dict`.  This can be used to provide custom
+    deserializations (e.g. to support JSON-RPC class hinting).
+
+    *object_pairs_hook* is an optional function that will be called with
+    the result of any object literal decode with an ordered list of pairs.
+    The return value of *object_pairs_hook* will be used instead of the
+    :class:`dict`.  This feature can be used to implement custom decoders
+    that rely on the order that the key and value pairs are decoded (for
+    example, :func:`collections.OrderedDict` will remember the order of
+    insertion). If *object_hook* is also defined, the *object_pairs_hook*
+    takes priority.
+
+    *parse_float*, if specified, will be called with the string of every
+    JSON float to be decoded.  By default, this is equivalent to
+    ``float(num_str)``. This can be used to use another datatype or parser
+    for JSON floats (e.g. :class:`decimal.Decimal`).
+
+    *parse_int*, if specified, will be called with the string of every
+    JSON int to be decoded.  By default, this is equivalent to
+    ``int(num_str)``.  This can be used to use another datatype or parser
+    for JSON integers (e.g. :class:`float`).
+
+    *parse_constant*, if specified, will be called with one of the
+    following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``.  This
+    can be used to raise an exception if invalid JSON numbers are
+    encountered.
+
+    If *use_decimal* is true (default: ``False``) then it implies
+    parse_float=decimal.Decimal for parity with ``dump``.
+
+    To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+    kwarg.
+
+    """
+    if (cls is None and encoding is None and object_hook is None and
+            parse_int is None and parse_float is None and
+            parse_constant is None and object_pairs_hook is None
+            and not use_decimal and not kw):
+        return _default_decoder.decode(s)
+    if cls is None:
+        cls = JSONDecoder
+    if object_hook is not None:
+        kw['object_hook'] = object_hook
+    if object_pairs_hook is not None:
+        kw['object_pairs_hook'] = object_pairs_hook
+    if parse_float is not None:
+        kw['parse_float'] = parse_float
+    if parse_int is not None:
+        kw['parse_int'] = parse_int
+    if parse_constant is not None:
+        kw['parse_constant'] = parse_constant
+    if use_decimal:
+        if parse_float is not None:
+            raise TypeError("use_decimal=True implies parse_float=Decimal")
+        kw['parse_float'] = Decimal
+    return cls(encoding=encoding, **kw).decode(s)
+
+
+def _toggle_speedups(enabled):
+    import simplejson.decoder as dec
+    import simplejson.encoder as enc
+    import simplejson.scanner as scan
+    c_make_encoder = _import_c_make_encoder()
+    if enabled:
+        dec.scanstring = dec.c_scanstring or dec.py_scanstring
+        enc.c_make_encoder = c_make_encoder
+        enc.encode_basestring_ascii = (enc.c_encode_basestring_ascii or 
+            enc.py_encode_basestring_ascii)
+        scan.make_scanner = scan.c_make_scanner or scan.py_make_scanner
+    else:
+        dec.scanstring = dec.py_scanstring
+        enc.c_make_encoder = None
+        enc.encode_basestring_ascii = enc.py_encode_basestring_ascii
+        scan.make_scanner = scan.py_make_scanner
+    dec.make_scanner = scan.make_scanner
+    global _default_decoder
+    _default_decoder = JSONDecoder(
+        encoding=None,
+        object_hook=None,
+        object_pairs_hook=None,
+    )
+    global _default_encoder
+    _default_encoder = JSONEncoder(
+       skipkeys=False,
+       ensure_ascii=True,
+       check_circular=True,
+       allow_nan=True,
+       indent=None,
+       separators=None,
+       encoding='utf-8',
+       default=None,
+   )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/simplejson/_speedups.c	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,2561 @@
+#include "Python.h"
+#include "structmember.h"
+#if PY_VERSION_HEX < 0x02070000 && !defined(PyOS_string_to_double)
+#define PyOS_string_to_double json_PyOS_string_to_double
+static double
+json_PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception);
+static double
+json_PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception) {
+    double x;
+    assert(endptr == NULL);
+    assert(overflow_exception == NULL);
+    PyFPE_START_PROTECT("json_PyOS_string_to_double", return -1.0;)
+    x = PyOS_ascii_atof(s);
+    PyFPE_END_PROTECT(x)
+    return x;
+}
+#endif
+#if PY_VERSION_HEX < 0x02060000 && !defined(Py_TYPE)
+#define Py_TYPE(ob)     (((PyObject*)(ob))->ob_type)
+#endif
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#define PyInt_FromSsize_t PyInt_FromLong
+#define PyInt_AsSsize_t PyInt_AsLong
+#endif
+#ifndef Py_IS_FINITE
+#define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X))
+#endif
+
+#ifdef __GNUC__
+#define UNUSED __attribute__((__unused__))
+#else
+#define UNUSED
+#endif
+
+#define DEFAULT_ENCODING "utf-8"
+
+#define PyScanner_Check(op) PyObject_TypeCheck(op, &PyScannerType)
+#define PyScanner_CheckExact(op) (Py_TYPE(op) == &PyScannerType)
+#define PyEncoder_Check(op) PyObject_TypeCheck(op, &PyEncoderType)
+#define PyEncoder_CheckExact(op) (Py_TYPE(op) == &PyEncoderType)
+#define Decimal_Check(op) (PyObject_TypeCheck(op, DecimalTypePtr))
+
+static PyTypeObject PyScannerType;
+static PyTypeObject PyEncoderType;
+static PyTypeObject *DecimalTypePtr;
+
+typedef struct _PyScannerObject {
+    PyObject_HEAD
+    PyObject *encoding;
+    PyObject *strict;
+    PyObject *object_hook;
+    PyObject *pairs_hook;
+    PyObject *parse_float;
+    PyObject *parse_int;
+    PyObject *parse_constant;
+    PyObject *memo;
+} PyScannerObject;
+
+static PyMemberDef scanner_members[] = {
+    {"encoding", T_OBJECT, offsetof(PyScannerObject, encoding), READONLY, "encoding"},
+    {"strict", T_OBJECT, offsetof(PyScannerObject, strict), READONLY, "strict"},
+    {"object_hook", T_OBJECT, offsetof(PyScannerObject, object_hook), READONLY, "object_hook"},
+    {"object_pairs_hook", T_OBJECT, offsetof(PyScannerObject, pairs_hook), READONLY, "object_pairs_hook"},
+    {"parse_float", T_OBJECT, offsetof(PyScannerObject, parse_float), READONLY, "parse_float"},
+    {"parse_int", T_OBJECT, offsetof(PyScannerObject, parse_int), READONLY, "parse_int"},
+    {"parse_constant", T_OBJECT, offsetof(PyScannerObject, parse_constant), READONLY, "parse_constant"},
+    {NULL}
+};
+
+typedef struct _PyEncoderObject {
+    PyObject_HEAD
+    PyObject *markers;
+    PyObject *defaultfn;
+    PyObject *encoder;
+    PyObject *indent;
+    PyObject *key_separator;
+    PyObject *item_separator;
+    PyObject *sort_keys;
+    PyObject *skipkeys;
+    PyObject *key_memo;
+    int fast_encode;
+    int allow_nan;
+    int use_decimal;
+} PyEncoderObject;
+
+static PyMemberDef encoder_members[] = {
+    {"markers", T_OBJECT, offsetof(PyEncoderObject, markers), READONLY, "markers"},
+    {"default", T_OBJECT, offsetof(PyEncoderObject, defaultfn), READONLY, "default"},
+    {"encoder", T_OBJECT, offsetof(PyEncoderObject, encoder), READONLY, "encoder"},
+    {"indent", T_OBJECT, offsetof(PyEncoderObject, indent), READONLY, "indent"},
+    {"key_separator", T_OBJECT, offsetof(PyEncoderObject, key_separator), READONLY, "key_separator"},
+    {"item_separator", T_OBJECT, offsetof(PyEncoderObject, item_separator), READONLY, "item_separator"},
+    {"sort_keys", T_OBJECT, offsetof(PyEncoderObject, sort_keys), READONLY, "sort_keys"},
+    {"skipkeys", T_OBJECT, offsetof(PyEncoderObject, skipkeys), READONLY, "skipkeys"},
+    {"key_memo", T_OBJECT, offsetof(PyEncoderObject, key_memo), READONLY, "key_memo"},
+    {NULL}
+};
+
+static Py_ssize_t
+ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars);
+static PyObject *
+ascii_escape_unicode(PyObject *pystr);
+static PyObject *
+ascii_escape_str(PyObject *pystr);
+static PyObject *
+py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr);
+void init_speedups(void);
+static PyObject *
+scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr);
+static PyObject *
+scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr);
+static PyObject *
+_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx);
+static PyObject *
+scanner_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static int
+scanner_init(PyObject *self, PyObject *args, PyObject *kwds);
+static void
+scanner_dealloc(PyObject *self);
+static int
+scanner_clear(PyObject *self);
+static PyObject *
+encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static int
+encoder_init(PyObject *self, PyObject *args, PyObject *kwds);
+static void
+encoder_dealloc(PyObject *self);
+static int
+encoder_clear(PyObject *self);
+static int
+encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level);
+static int
+encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level);
+static int
+encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level);
+static PyObject *
+_encoded_const(PyObject *obj);
+static void
+raise_errmsg(char *msg, PyObject *s, Py_ssize_t end);
+static PyObject *
+encoder_encode_string(PyEncoderObject *s, PyObject *obj);
+static int
+_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr);
+static PyObject *
+_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr);
+static PyObject *
+encoder_encode_float(PyEncoderObject *s, PyObject *obj);
+
+#define S_CHAR(c) (c >= ' ' && c <= '~' && c != '\\' && c != '"')
+#define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
+
+#define MIN_EXPANSION 6
+#ifdef Py_UNICODE_WIDE
+#define MAX_EXPANSION (2 * MIN_EXPANSION)
+#else
+#define MAX_EXPANSION MIN_EXPANSION
+#endif
+
+static int
+_convertPyInt_AsSsize_t(PyObject *o, Py_ssize_t *size_ptr)
+{
+    /* PyObject to Py_ssize_t converter */
+    *size_ptr = PyInt_AsSsize_t(o);
+    if (*size_ptr == -1 && PyErr_Occurred())
+        return 0;
+    return 1;
+}
+
+static PyObject *
+_convertPyInt_FromSsize_t(Py_ssize_t *size_ptr)
+{
+    /* Py_ssize_t to PyObject converter */
+    return PyInt_FromSsize_t(*size_ptr);
+}
+
+static Py_ssize_t
+ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars)
+{
+    /* Escape unicode code point c to ASCII escape sequences
+    in char *output. output must have at least 12 bytes unused to
+    accommodate an escaped surrogate pair "\uXXXX\uXXXX" */
+    output[chars++] = '\\';
+    switch (c) {
+        case '\\': output[chars++] = (char)c; break;
+        case '"': output[chars++] = (char)c; break;
+        case '\b': output[chars++] = 'b'; break;
+        case '\f': output[chars++] = 'f'; break;
+        case '\n': output[chars++] = 'n'; break;
+        case '\r': output[chars++] = 'r'; break;
+        case '\t': output[chars++] = 't'; break;
+        default:
+#ifdef Py_UNICODE_WIDE
+            if (c >= 0x10000) {
+                /* UTF-16 surrogate pair */
+                Py_UNICODE v = c - 0x10000;
+                c = 0xd800 | ((v >> 10) & 0x3ff);
+                output[chars++] = 'u';
+                output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf];
+                output[chars++] = "0123456789abcdef"[(c >>  8) & 0xf];
+                output[chars++] = "0123456789abcdef"[(c >>  4) & 0xf];
+                output[chars++] = "0123456789abcdef"[(c      ) & 0xf];
+                c = 0xdc00 | (v & 0x3ff);
+                output[chars++] = '\\';
+            }
+#endif
+            output[chars++] = 'u';
+            output[chars++] = "0123456789abcdef"[(c >> 12) & 0xf];
+            output[chars++] = "0123456789abcdef"[(c >>  8) & 0xf];
+            output[chars++] = "0123456789abcdef"[(c >>  4) & 0xf];
+            output[chars++] = "0123456789abcdef"[(c      ) & 0xf];
+    }
+    return chars;
+}
+
+static PyObject *
+ascii_escape_unicode(PyObject *pystr)
+{
+    /* Take a PyUnicode pystr and return a new ASCII-only escaped PyString */
+    Py_ssize_t i;
+    Py_ssize_t input_chars;
+    Py_ssize_t output_size;
+    Py_ssize_t max_output_size;
+    Py_ssize_t chars;
+    PyObject *rval;
+    char *output;
+    Py_UNICODE *input_unicode;
+
+    input_chars = PyUnicode_GET_SIZE(pystr);
+    input_unicode = PyUnicode_AS_UNICODE(pystr);
+
+    /* One char input can be up to 6 chars output, estimate 4 of these */
+    output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
+    max_output_size = 2 + (input_chars * MAX_EXPANSION);
+    rval = PyString_FromStringAndSize(NULL, output_size);
+    if (rval == NULL) {
+        return NULL;
+    }
+    output = PyString_AS_STRING(rval);
+    chars = 0;
+    output[chars++] = '"';
+    for (i = 0; i < input_chars; i++) {
+        Py_UNICODE c = input_unicode[i];
+        if (S_CHAR(c)) {
+            output[chars++] = (char)c;
+        }
+        else {
+            chars = ascii_escape_char(c, output, chars);
+        }
+        if (output_size - chars < (1 + MAX_EXPANSION)) {
+            /* There's more than four, so let's resize by a lot */
+            Py_ssize_t new_output_size = output_size * 2;
+            /* This is an upper bound */
+            if (new_output_size > max_output_size) {
+                new_output_size = max_output_size;
+            }
+            /* Make sure that the output size changed before resizing */
+            if (new_output_size != output_size) {
+                output_size = new_output_size;
+                if (_PyString_Resize(&rval, output_size) == -1) {
+                    return NULL;
+                }
+                output = PyString_AS_STRING(rval);
+            }
+        }
+    }
+    output[chars++] = '"';
+    if (_PyString_Resize(&rval, chars) == -1) {
+        return NULL;
+    }
+    return rval;
+}
+
+static PyObject *
+ascii_escape_str(PyObject *pystr)
+{
+    /* Take a PyString pystr and return a new ASCII-only escaped PyString */
+    Py_ssize_t i;
+    Py_ssize_t input_chars;
+    Py_ssize_t output_size;
+    Py_ssize_t chars;
+    PyObject *rval;
+    char *output;
+    char *input_str;
+
+    input_chars = PyString_GET_SIZE(pystr);
+    input_str = PyString_AS_STRING(pystr);
+
+    /* Fast path for a string that's already ASCII */
+    for (i = 0; i < input_chars; i++) {
+        Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i];
+        if (!S_CHAR(c)) {
+            /* If we have to escape something, scan the string for unicode */
+            Py_ssize_t j;
+            for (j = i; j < input_chars; j++) {
+                c = (Py_UNICODE)(unsigned char)input_str[j];
+                if (c > 0x7f) {
+                    /* We hit a non-ASCII character, bail to unicode mode */
+                    PyObject *uni;
+                    uni = PyUnicode_DecodeUTF8(input_str, input_chars, "strict");
+                    if (uni == NULL) {
+                        return NULL;
+                    }
+                    rval = ascii_escape_unicode(uni);
+                    Py_DECREF(uni);
+                    return rval;
+                }
+            }
+            break;
+        }
+    }
+
+    if (i == input_chars) {
+        /* Input is already ASCII */
+        output_size = 2 + input_chars;
+    }
+    else {
+        /* One char input can be up to 6 chars output, estimate 4 of these */
+        output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
+    }
+    rval = PyString_FromStringAndSize(NULL, output_size);
+    if (rval == NULL) {
+        return NULL;
+    }
+    output = PyString_AS_STRING(rval);
+    output[0] = '"';
+
+    /* We know that everything up to i is ASCII already */
+    chars = i + 1;
+    memcpy(&output[1], input_str, i);
+
+    for (; i < input_chars; i++) {
+        Py_UNICODE c = (Py_UNICODE)(unsigned char)input_str[i];
+        if (S_CHAR(c)) {
+            output[chars++] = (char)c;
+        }
+        else {
+            chars = ascii_escape_char(c, output, chars);
+        }
+        /* An ASCII char can't possibly expand to a surrogate! */
+        if (output_size - chars < (1 + MIN_EXPANSION)) {
+            /* There's more than four, so let's resize by a lot */
+            output_size *= 2;
+            if (output_size > 2 + (input_chars * MIN_EXPANSION)) {
+                output_size = 2 + (input_chars * MIN_EXPANSION);
+            }
+            if (_PyString_Resize(&rval, output_size) == -1) {
+                return NULL;
+            }
+            output = PyString_AS_STRING(rval);
+        }
+    }
+    output[chars++] = '"';
+    if (_PyString_Resize(&rval, chars) == -1) {
+        return NULL;
+    }
+    return rval;
+}
+
+static void
+raise_errmsg(char *msg, PyObject *s, Py_ssize_t end)
+{
+    /* Use the Python function simplejson.decoder.errmsg to raise a nice
+    looking ValueError exception */
+    static PyObject *JSONDecodeError = NULL;
+    PyObject *exc;
+    if (JSONDecodeError == NULL) {
+        PyObject *decoder = PyImport_ImportModule("simplejson.decoder");
+        if (decoder == NULL)
+            return;
+        JSONDecodeError = PyObject_GetAttrString(decoder, "JSONDecodeError");
+        Py_DECREF(decoder);
+        if (JSONDecodeError == NULL)
+            return;
+    }
+    exc = PyObject_CallFunction(JSONDecodeError, "(zOO&)", msg, s, _convertPyInt_FromSsize_t, &end);
+    if (exc) {
+        PyErr_SetObject(JSONDecodeError, exc);
+        Py_DECREF(exc);
+    }
+}
+
+static PyObject *
+join_list_unicode(PyObject *lst)
+{
+    /* return u''.join(lst) */
+    static PyObject *joinfn = NULL;
+    if (joinfn == NULL) {
+        PyObject *ustr = PyUnicode_FromUnicode(NULL, 0);
+        if (ustr == NULL)
+            return NULL;
+
+        joinfn = PyObject_GetAttrString(ustr, "join");
+        Py_DECREF(ustr);
+        if (joinfn == NULL)
+            return NULL;
+    }
+    return PyObject_CallFunctionObjArgs(joinfn, lst, NULL);
+}
+
+static PyObject *
+join_list_string(PyObject *lst)
+{
+    /* return ''.join(lst) */
+    static PyObject *joinfn = NULL;
+    if (joinfn == NULL) {
+        PyObject *ustr = PyString_FromStringAndSize(NULL, 0);
+        if (ustr == NULL)
+            return NULL;
+
+        joinfn = PyObject_GetAttrString(ustr, "join");
+        Py_DECREF(ustr);
+        if (joinfn == NULL)
+            return NULL;
+    }
+    return PyObject_CallFunctionObjArgs(joinfn, lst, NULL);
+}
+
+static PyObject *
+_build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) {
+    /* return (rval, idx) tuple, stealing reference to rval */
+    PyObject *tpl;
+    PyObject *pyidx;
+    /*
+    steal a reference to rval, returns (rval, idx)
+    */
+    if (rval == NULL) {
+        return NULL;
+    }
+    pyidx = PyInt_FromSsize_t(idx);
+    if (pyidx == NULL) {
+        Py_DECREF(rval);
+        return NULL;
+    }
+    tpl = PyTuple_New(2);
+    if (tpl == NULL) {
+        Py_DECREF(pyidx);
+        Py_DECREF(rval);
+        return NULL;
+    }
+    PyTuple_SET_ITEM(tpl, 0, rval);
+    PyTuple_SET_ITEM(tpl, 1, pyidx);
+    return tpl;
+}
+
+#define APPEND_OLD_CHUNK \
+    if (chunk != NULL) { \
+        if (chunks == NULL) { \
+            chunks = PyList_New(0); \
+            if (chunks == NULL) { \
+                goto bail; \
+            } \
+        } \
+        if (PyList_Append(chunks, chunk)) { \
+            goto bail; \
+        } \
+        Py_CLEAR(chunk); \
+    }
+
+static PyObject *
+scanstring_str(PyObject *pystr, Py_ssize_t end, char *encoding, int strict, Py_ssize_t *next_end_ptr)
+{
+    /* Read the JSON string from PyString pystr.
+    end is the index of the first character after the quote.
+    encoding is the encoding of pystr (must be an ASCII superset)
+    if strict is zero then literal control characters are allowed
+    *next_end_ptr is a return-by-reference index of the character
+        after the end quote
+
+    Return value is a new PyString (if ASCII-only) or PyUnicode
+    */
+    PyObject *rval;
+    Py_ssize_t len = PyString_GET_SIZE(pystr);
+    Py_ssize_t begin = end - 1;
+    Py_ssize_t next = begin;
+    int has_unicode = 0;
+    char *buf = PyString_AS_STRING(pystr);
+    PyObject *chunks = NULL;
+    PyObject *chunk = NULL;
+
+    if (end < 0 || len <= end) {
+        PyErr_SetString(PyExc_ValueError, "end is out of bounds");
+        goto bail;
+    }
+    while (1) {
+        /* Find the end of the string or the next escape */
+        Py_UNICODE c = 0;
+        for (next = end; next < len; next++) {
+            c = (unsigned char)buf[next];
+            if (c == '"' || c == '\\') {
+                break;
+            }
+            else if (strict && c <= 0x1f) {
+                raise_errmsg("Invalid control character at", pystr, next);
+                goto bail;
+            }
+            else if (c > 0x7f) {
+                has_unicode = 1;
+            }
+        }
+        if (!(c == '"' || c == '\\')) {
+            raise_errmsg("Unterminated string starting at", pystr, begin);
+            goto bail;
+        }
+        /* Pick up this chunk if it's not zero length */
+        if (next != end) {
+            PyObject *strchunk;
+            APPEND_OLD_CHUNK
+            strchunk = PyString_FromStringAndSize(&buf[end], next - end);
+            if (strchunk == NULL) {
+                goto bail;
+            }
+            if (has_unicode) {
+                chunk = PyUnicode_FromEncodedObject(strchunk, encoding, NULL);
+                Py_DECREF(strchunk);
+                if (chunk == NULL) {
+                    goto bail;
+                }
+            }
+            else {
+                chunk = strchunk;
+            }
+        }
+        next++;
+        if (c == '"') {
+            end = next;
+            break;
+        }
+        if (next == len) {
+            raise_errmsg("Unterminated string starting at", pystr, begin);
+            goto bail;
+        }
+        c = buf[next];
+        if (c != 'u') {
+            /* Non-unicode backslash escapes */
+            end = next + 1;
+            switch (c) {
+                case '"': break;
+                case '\\': break;
+                case '/': break;
+                case 'b': c = '\b'; break;
+                case 'f': c = '\f'; break;
+                case 'n': c = '\n'; break;
+                case 'r': c = '\r'; break;
+                case 't': c = '\t'; break;
+                default: c = 0;
+            }
+            if (c == 0) {
+                raise_errmsg("Invalid \\escape", pystr, end - 2);
+                goto bail;
+            }
+        }
+        else {
+            c = 0;
+            next++;
+            end = next + 4;
+            if (end >= len) {
+                raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1);
+                goto bail;
+            }
+            /* Decode 4 hex digits */
+            for (; next < end; next++) {
+                Py_UNICODE digit = buf[next];
+                c <<= 4;
+                switch (digit) {
+                    case '0': case '1': case '2': case '3': case '4':
+                    case '5': case '6': case '7': case '8': case '9':
+                        c |= (digit - '0'); break;
+                    case 'a': case 'b': case 'c': case 'd': case 'e':
+                    case 'f':
+                        c |= (digit - 'a' + 10); break;
+                    case 'A': case 'B': case 'C': case 'D': case 'E':
+                    case 'F':
+                        c |= (digit - 'A' + 10); break;
+                    default:
+                        raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+                        goto bail;
+                }
+            }
+#ifdef Py_UNICODE_WIDE
+            /* Surrogate pair */
+            if ((c & 0xfc00) == 0xd800) {
+                Py_UNICODE c2 = 0;
+                if (end + 6 >= len) {
+                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+                    goto bail;
+                }
+                if (buf[next++] != '\\' || buf[next++] != 'u') {
+                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+                    goto bail;
+                }
+                end += 6;
+                /* Decode 4 hex digits */
+                for (; next < end; next++) {
+                    c2 <<= 4;
+                    Py_UNICODE digit = buf[next];
+                    switch (digit) {
+                        case '0': case '1': case '2': case '3': case '4':
+                        case '5': case '6': case '7': case '8': case '9':
+                            c2 |= (digit - '0'); break;
+                        case 'a': case 'b': case 'c': case 'd': case 'e':
+                        case 'f':
+                            c2 |= (digit - 'a' + 10); break;
+                        case 'A': case 'B': case 'C': case 'D': case 'E':
+                        case 'F':
+                            c2 |= (digit - 'A' + 10); break;
+                        default:
+                            raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+                            goto bail;
+                    }
+                }
+                if ((c2 & 0xfc00) != 0xdc00) {
+                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+                    goto bail;
+                }
+                c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
+            }
+            else if ((c & 0xfc00) == 0xdc00) {
+                raise_errmsg("Unpaired low surrogate", pystr, end - 5);
+                goto bail;
+            }
+#endif
+        }
+        if (c > 0x7f) {
+            has_unicode = 1;
+        }
+        APPEND_OLD_CHUNK
+        if (has_unicode) {
+            chunk = PyUnicode_FromUnicode(&c, 1);
+            if (chunk == NULL) {
+                goto bail;
+            }
+        }
+        else {
+            char c_char = Py_CHARMASK(c);
+            chunk = PyString_FromStringAndSize(&c_char, 1);
+            if (chunk == NULL) {
+                goto bail;
+            }
+        }
+    }
+
+    if (chunks == NULL) {
+        if (chunk != NULL)
+            rval = chunk;
+        else
+            rval = PyString_FromStringAndSize("", 0);
+    }
+    else {
+        APPEND_OLD_CHUNK
+        rval = join_list_string(chunks);
+        if (rval == NULL) {
+            goto bail;
+        }
+        Py_CLEAR(chunks);
+    }
+
+    *next_end_ptr = end;
+    return rval;
+bail:
+    *next_end_ptr = -1;
+    Py_XDECREF(chunk);
+    Py_XDECREF(chunks);
+    return NULL;
+}
+
+
+static PyObject *
+scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr)
+{
+    /* Read the JSON string from PyUnicode pystr.
+    end is the index of the first character after the quote.
+    if strict is zero then literal control characters are allowed
+    *next_end_ptr is a return-by-reference index of the character
+        after the end quote
+
+    Return value is a new PyUnicode
+    */
+    PyObject *rval;
+    Py_ssize_t len = PyUnicode_GET_SIZE(pystr);
+    Py_ssize_t begin = end - 1;
+    Py_ssize_t next = begin;
+    const Py_UNICODE *buf = PyUnicode_AS_UNICODE(pystr);
+    PyObject *chunks = NULL;
+    PyObject *chunk = NULL;
+
+    if (end < 0 || len <= end) {
+        PyErr_SetString(PyExc_ValueError, "end is out of bounds");
+        goto bail;
+    }
+    while (1) {
+        /* Find the end of the string or the next escape */
+        Py_UNICODE c = 0;
+        for (next = end; next < len; next++) {
+            c = buf[next];
+            if (c == '"' || c == '\\') {
+                break;
+            }
+            else if (strict && c <= 0x1f) {
+                raise_errmsg("Invalid control character at", pystr, next);
+                goto bail;
+            }
+        }
+        if (!(c == '"' || c == '\\')) {
+            raise_errmsg("Unterminated string starting at", pystr, begin);
+            goto bail;
+        }
+        /* Pick up this chunk if it's not zero length */
+        if (next != end) {
+            APPEND_OLD_CHUNK
+            chunk = PyUnicode_FromUnicode(&buf[end], next - end);
+            if (chunk == NULL) {
+                goto bail;
+            }
+        }
+        next++;
+        if (c == '"') {
+            end = next;
+            break;
+        }
+        if (next == len) {
+            raise_errmsg("Unterminated string starting at", pystr, begin);
+            goto bail;
+        }
+        c = buf[next];
+        if (c != 'u') {
+            /* Non-unicode backslash escapes */
+            end = next + 1;
+            switch (c) {
+                case '"': break;
+                case '\\': break;
+                case '/': break;
+                case 'b': c = '\b'; break;
+                case 'f': c = '\f'; break;
+                case 'n': c = '\n'; break;
+                case 'r': c = '\r'; break;
+                case 't': c = '\t'; break;
+                default: c = 0;
+            }
+            if (c == 0) {
+                raise_errmsg("Invalid \\escape", pystr, end - 2);
+                goto bail;
+            }
+        }
+        else {
+            c = 0;
+            next++;
+            end = next + 4;
+            if (end >= len) {
+                raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1);
+                goto bail;
+            }
+            /* Decode 4 hex digits */
+            for (; next < end; next++) {
+                Py_UNICODE digit = buf[next];
+                c <<= 4;
+                switch (digit) {
+                    case '0': case '1': case '2': case '3': case '4':
+                    case '5': case '6': case '7': case '8': case '9':
+                        c |= (digit - '0'); break;
+                    case 'a': case 'b': case 'c': case 'd': case 'e':
+                    case 'f':
+                        c |= (digit - 'a' + 10); break;
+                    case 'A': case 'B': case 'C': case 'D': case 'E':
+                    case 'F':
+                        c |= (digit - 'A' + 10); break;
+                    default:
+                        raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+                        goto bail;
+                }
+            }
+#ifdef Py_UNICODE_WIDE
+            /* Surrogate pair */
+            if ((c & 0xfc00) == 0xd800) {
+                Py_UNICODE c2 = 0;
+                if (end + 6 >= len) {
+                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+                    goto bail;
+                }
+                if (buf[next++] != '\\' || buf[next++] != 'u') {
+                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+                    goto bail;
+                }
+                end += 6;
+                /* Decode 4 hex digits */
+                for (; next < end; next++) {
+                    c2 <<= 4;
+                    Py_UNICODE digit = buf[next];
+                    switch (digit) {
+                        case '0': case '1': case '2': case '3': case '4':
+                        case '5': case '6': case '7': case '8': case '9':
+                            c2 |= (digit - '0'); break;
+                        case 'a': case 'b': case 'c': case 'd': case 'e':
+                        case 'f':
+                            c2 |= (digit - 'a' + 10); break;
+                        case 'A': case 'B': case 'C': case 'D': case 'E':
+                        case 'F':
+                            c2 |= (digit - 'A' + 10); break;
+                        default:
+                            raise_errmsg("Invalid \\uXXXX escape", pystr, end - 5);
+                            goto bail;
+                    }
+                }
+                if ((c2 & 0xfc00) != 0xdc00) {
+                    raise_errmsg("Unpaired high surrogate", pystr, end - 5);
+                    goto bail;
+                }
+                c = 0x10000 + (((c - 0xd800) << 10) | (c2 - 0xdc00));
+            }
+            else if ((c & 0xfc00) == 0xdc00) {
+                raise_errmsg("Unpaired low surrogate", pystr, end - 5);
+                goto bail;
+            }
+#endif
+        }
+        APPEND_OLD_CHUNK
+        chunk = PyUnicode_FromUnicode(&c, 1);
+        if (chunk == NULL) {
+            goto bail;
+        }
+    }
+
+    if (chunks == NULL) {
+        if (chunk != NULL)
+            rval = chunk;
+        else
+            rval = PyUnicode_FromUnicode(NULL, 0);
+    }
+    else {
+        APPEND_OLD_CHUNK
+        rval = join_list_unicode(chunks);
+        if (rval == NULL) {
+            goto bail;
+        }
+        Py_CLEAR(chunks);
+    }
+    *next_end_ptr = end;
+    return rval;
+bail:
+    *next_end_ptr = -1;
+    Py_XDECREF(chunk);
+    Py_XDECREF(chunks);
+    return NULL;
+}
+
+PyDoc_STRVAR(pydoc_scanstring,
+    "scanstring(basestring, end, encoding, strict=True) -> (str, end)\n"
+    "\n"
+    "Scan the string s for a JSON string. End is the index of the\n"
+    "character in s after the quote that started the JSON string.\n"
+    "Unescapes all valid JSON string escape sequences and raises ValueError\n"
+    "on attempt to decode an invalid string. If strict is False then literal\n"
+    "control characters are allowed in the string.\n"
+    "\n"
+    "Returns a tuple of the decoded string and the index of the character in s\n"
+    "after the end quote."
+);
+
+static PyObject *
+py_scanstring(PyObject* self UNUSED, PyObject *args)
+{
+    PyObject *pystr;
+    PyObject *rval;
+    Py_ssize_t end;
+    Py_ssize_t next_end = -1;
+    char *encoding = NULL;
+    int strict = 1;
+    if (!PyArg_ParseTuple(args, "OO&|zi:scanstring", &pystr, _convertPyInt_AsSsize_t, &end, &encoding, &strict)) {
+        return NULL;
+    }
+    if (encoding == NULL) {
+        encoding = DEFAULT_ENCODING;
+    }
+    if (PyString_Check(pystr)) {
+        rval = scanstring_str(pystr, end, encoding, strict, &next_end);
+    }
+    else if (PyUnicode_Check(pystr)) {
+        rval = scanstring_unicode(pystr, end, strict, &next_end);
+    }
+    else {
+        PyErr_Format(PyExc_TypeError,
+                     "first argument must be a string, not %.80s",
+                     Py_TYPE(pystr)->tp_name);
+        return NULL;
+    }
+    return _build_rval_index_tuple(rval, next_end);
+}
+
+PyDoc_STRVAR(pydoc_encode_basestring_ascii,
+    "encode_basestring_ascii(basestring) -> str\n"
+    "\n"
+    "Return an ASCII-only JSON representation of a Python string"
+);
+
+static PyObject *
+py_encode_basestring_ascii(PyObject* self UNUSED, PyObject *pystr)
+{
+    /* Return an ASCII-only JSON representation of a Python string */
+    /* METH_O */
+    if (PyString_Check(pystr)) {
+        return ascii_escape_str(pystr);
+    }
+    else if (PyUnicode_Check(pystr)) {
+        return ascii_escape_unicode(pystr);
+    }
+    else {
+        PyErr_Format(PyExc_TypeError,
+                     "first argument must be a string, not %.80s",
+                     Py_TYPE(pystr)->tp_name);
+        return NULL;
+    }
+}
+
+static void
+scanner_dealloc(PyObject *self)
+{
+    /* Deallocate scanner object */
+    scanner_clear(self);
+    Py_TYPE(self)->tp_free(self);
+}
+
+static int
+scanner_traverse(PyObject *self, visitproc visit, void *arg)
+{
+    PyScannerObject *s;
+    assert(PyScanner_Check(self));
+    s = (PyScannerObject *)self;
+    Py_VISIT(s->encoding);
+    Py_VISIT(s->strict);
+    Py_VISIT(s->object_hook);
+    Py_VISIT(s->pairs_hook);
+    Py_VISIT(s->parse_float);
+    Py_VISIT(s->parse_int);
+    Py_VISIT(s->parse_constant);
+    Py_VISIT(s->memo);
+    return 0;
+}
+
+static int
+scanner_clear(PyObject *self)
+{
+    PyScannerObject *s;
+    assert(PyScanner_Check(self));
+    s = (PyScannerObject *)self;
+    Py_CLEAR(s->encoding);
+    Py_CLEAR(s->strict);
+    Py_CLEAR(s->object_hook);
+    Py_CLEAR(s->pairs_hook);
+    Py_CLEAR(s->parse_float);
+    Py_CLEAR(s->parse_int);
+    Py_CLEAR(s->parse_constant);
+    Py_CLEAR(s->memo);
+    return 0;
+}
+
+static PyObject *
+_parse_object_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+    /* Read a JSON object from PyString pystr.
+    idx is the index of the first character after the opening curly brace.
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the closing curly brace.
+
+    Returns a new PyObject (usually a dict, but object_hook or
+    object_pairs_hook can change that)
+    */
+    char *str = PyString_AS_STRING(pystr);
+    Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+    PyObject *rval = NULL;
+    PyObject *pairs = NULL;
+    PyObject *item;
+    PyObject *key = NULL;
+    PyObject *val = NULL;
+    char *encoding = PyString_AS_STRING(s->encoding);
+    int strict = PyObject_IsTrue(s->strict);
+    int has_pairs_hook = (s->pairs_hook != Py_None);
+    Py_ssize_t next_idx;
+    if (has_pairs_hook) {
+        pairs = PyList_New(0);
+        if (pairs == NULL)
+            return NULL;
+    }
+    else {
+        rval = PyDict_New();
+        if (rval == NULL)
+            return NULL;
+    }
+
+    /* skip whitespace after { */
+    while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+    /* only loop if the object is non-empty */
+    if (idx <= end_idx && str[idx] != '}') {
+        while (idx <= end_idx) {
+            PyObject *memokey;
+
+            /* read key */
+            if (str[idx] != '"') {
+                raise_errmsg("Expecting property name", pystr, idx);
+                goto bail;
+            }
+            key = scanstring_str(pystr, idx + 1, encoding, strict, &next_idx);
+            if (key == NULL)
+                goto bail;
+            memokey = PyDict_GetItem(s->memo, key);
+            if (memokey != NULL) {
+                Py_INCREF(memokey);
+                Py_DECREF(key);
+                key = memokey;
+            }
+            else {
+                if (PyDict_SetItem(s->memo, key, key) < 0)
+                    goto bail;
+            }
+            idx = next_idx;
+
+            /* skip whitespace between key and : delimiter, read :, skip whitespace */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+            if (idx > end_idx || str[idx] != ':') {
+                raise_errmsg("Expecting : delimiter", pystr, idx);
+                goto bail;
+            }
+            idx++;
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+            /* read any JSON data type */
+            val = scan_once_str(s, pystr, idx, &next_idx);
+            if (val == NULL)
+                goto bail;
+
+            if (has_pairs_hook) {
+                item = PyTuple_Pack(2, key, val);
+                if (item == NULL)
+                    goto bail;
+                Py_CLEAR(key);
+                Py_CLEAR(val);
+                if (PyList_Append(pairs, item) == -1) {
+                    Py_DECREF(item);
+                    goto bail;
+                }
+                Py_DECREF(item);
+            }
+            else {
+                if (PyDict_SetItem(rval, key, val) < 0)
+                    goto bail;
+                Py_CLEAR(key);
+                Py_CLEAR(val);
+            }
+            idx = next_idx;
+
+            /* skip whitespace before } or , */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+            /* bail if the object is closed or we didn't get the , delimiter */
+            if (idx > end_idx) break;
+            if (str[idx] == '}') {
+                break;
+            }
+            else if (str[idx] != ',') {
+                raise_errmsg("Expecting , delimiter", pystr, idx);
+                goto bail;
+            }
+            idx++;
+
+            /* skip whitespace after , delimiter */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+        }
+    }
+    /* verify that idx < end_idx, str[idx] should be '}' */
+    if (idx > end_idx || str[idx] != '}') {
+        raise_errmsg("Expecting object", pystr, end_idx);
+        goto bail;
+    }
+
+    /* if pairs_hook is not None: rval = object_pairs_hook(pairs) */
+    if (s->pairs_hook != Py_None) {
+        val = PyObject_CallFunctionObjArgs(s->pairs_hook, pairs, NULL);
+        if (val == NULL)
+            goto bail;
+        Py_DECREF(pairs);
+        *next_idx_ptr = idx + 1;
+        return val;
+    }
+
+    /* if object_hook is not None: rval = object_hook(rval) */
+    if (s->object_hook != Py_None) {
+        val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL);
+        if (val == NULL)
+            goto bail;
+        Py_DECREF(rval);
+        rval = val;
+        val = NULL;
+    }
+    *next_idx_ptr = idx + 1;
+    return rval;
+bail:
+    Py_XDECREF(rval);
+    Py_XDECREF(key);
+    Py_XDECREF(val);
+    Py_XDECREF(pairs);
+    return NULL;
+}
+
+static PyObject *
+_parse_object_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+    /* Read a JSON object from PyUnicode pystr.
+    idx is the index of the first character after the opening curly brace.
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the closing curly brace.
+
+    Returns a new PyObject (usually a dict, but object_hook can change that)
+    */
+    Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+    Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+    PyObject *rval = NULL;
+    PyObject *pairs = NULL;
+    PyObject *item;
+    PyObject *key = NULL;
+    PyObject *val = NULL;
+    int strict = PyObject_IsTrue(s->strict);
+    int has_pairs_hook = (s->pairs_hook != Py_None);
+    Py_ssize_t next_idx;
+
+    if (has_pairs_hook) {
+        pairs = PyList_New(0);
+        if (pairs == NULL)
+            return NULL;
+    }
+    else {
+        rval = PyDict_New();
+        if (rval == NULL)
+            return NULL;
+    }
+    
+    /* skip whitespace after { */
+    while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+    /* only loop if the object is non-empty */
+    if (idx <= end_idx && str[idx] != '}') {
+        while (idx <= end_idx) {
+            PyObject *memokey;
+
+            /* read key */
+            if (str[idx] != '"') {
+                raise_errmsg("Expecting property name", pystr, idx);
+                goto bail;
+            }
+            key = scanstring_unicode(pystr, idx + 1, strict, &next_idx);
+            if (key == NULL)
+                goto bail;
+            memokey = PyDict_GetItem(s->memo, key);
+            if (memokey != NULL) {
+                Py_INCREF(memokey);
+                Py_DECREF(key);
+                key = memokey;
+            }
+            else {
+                if (PyDict_SetItem(s->memo, key, key) < 0)
+                    goto bail;
+            }
+            idx = next_idx;
+
+            /* skip whitespace between key and : delimiter, read :, skip whitespace */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+            if (idx > end_idx || str[idx] != ':') {
+                raise_errmsg("Expecting : delimiter", pystr, idx);
+                goto bail;
+            }
+            idx++;
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+            /* read any JSON term */
+            val = scan_once_unicode(s, pystr, idx, &next_idx);
+            if (val == NULL)
+                goto bail;
+
+            if (has_pairs_hook) {
+                item = PyTuple_Pack(2, key, val);
+                if (item == NULL)
+                    goto bail;
+                Py_CLEAR(key);
+                Py_CLEAR(val);
+                if (PyList_Append(pairs, item) == -1) {
+                    Py_DECREF(item);
+                    goto bail;
+                }
+                Py_DECREF(item);
+            }
+            else {
+                if (PyDict_SetItem(rval, key, val) < 0)
+                    goto bail;
+                Py_CLEAR(key);
+                Py_CLEAR(val);
+            }
+            idx = next_idx;
+
+            /* skip whitespace before } or , */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+            /* bail if the object is closed or we didn't get the , delimiter */
+            if (idx > end_idx) break;
+            if (str[idx] == '}') {
+                break;
+            }
+            else if (str[idx] != ',') {
+                raise_errmsg("Expecting , delimiter", pystr, idx);
+                goto bail;
+            }
+            idx++;
+
+            /* skip whitespace after , delimiter */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+        }
+    }
+
+    /* verify that idx < end_idx, str[idx] should be '}' */
+    if (idx > end_idx || str[idx] != '}') {
+        raise_errmsg("Expecting object", pystr, end_idx);
+        goto bail;
+    }
+
+    /* if pairs_hook is not None: rval = object_pairs_hook(pairs) */
+    if (s->pairs_hook != Py_None) {
+        val = PyObject_CallFunctionObjArgs(s->pairs_hook, pairs, NULL);
+        if (val == NULL)
+            goto bail;
+        Py_DECREF(pairs);
+        *next_idx_ptr = idx + 1;
+        return val;
+    }
+
+    /* if object_hook is not None: rval = object_hook(rval) */
+    if (s->object_hook != Py_None) {
+        val = PyObject_CallFunctionObjArgs(s->object_hook, rval, NULL);
+        if (val == NULL)
+            goto bail;
+        Py_DECREF(rval);
+        rval = val;
+        val = NULL;
+    }
+    *next_idx_ptr = idx + 1;
+    return rval;
+bail:
+    Py_XDECREF(rval);
+    Py_XDECREF(key);
+    Py_XDECREF(val);
+    Py_XDECREF(pairs);
+    return NULL;
+}
+
+static PyObject *
+_parse_array_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+    /* Read a JSON array from PyString pystr.
+    idx is the index of the first character after the opening brace.
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the closing brace.
+
+    Returns a new PyList
+    */
+    char *str = PyString_AS_STRING(pystr);
+    Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+    PyObject *val = NULL;
+    PyObject *rval = PyList_New(0);
+    Py_ssize_t next_idx;
+    if (rval == NULL)
+        return NULL;
+
+    /* skip whitespace after [ */
+    while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+    /* only loop if the array is non-empty */
+    if (idx <= end_idx && str[idx] != ']') {
+        while (idx <= end_idx) {
+
+            /* read any JSON term and de-tuplefy the (rval, idx) */
+            val = scan_once_str(s, pystr, idx, &next_idx);
+            if (val == NULL) {
+                if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+                    PyErr_Clear();
+                    raise_errmsg("Expecting object", pystr, idx);
+                }
+                goto bail;
+            }
+
+            if (PyList_Append(rval, val) == -1)
+                goto bail;
+
+            Py_CLEAR(val);
+            idx = next_idx;
+
+            /* skip whitespace between term and , */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+            /* bail if the array is closed or we didn't get the , delimiter */
+            if (idx > end_idx) break;
+            if (str[idx] == ']') {
+                break;
+            }
+            else if (str[idx] != ',') {
+                raise_errmsg("Expecting , delimiter", pystr, idx);
+                goto bail;
+            }
+            idx++;
+
+            /* skip whitespace after , */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+        }
+    }
+
+    /* verify that idx < end_idx, str[idx] should be ']' */
+    if (idx > end_idx || str[idx] != ']') {
+        raise_errmsg("Expecting object", pystr, end_idx);
+        goto bail;
+    }
+    *next_idx_ptr = idx + 1;
+    return rval;
+bail:
+    Py_XDECREF(val);
+    Py_DECREF(rval);
+    return NULL;
+}
+
+static PyObject *
+_parse_array_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+    /* Read a JSON array from PyString pystr.
+    idx is the index of the first character after the opening brace.
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the closing brace.
+
+    Returns a new PyList
+    */
+    Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+    Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+    PyObject *val = NULL;
+    PyObject *rval = PyList_New(0);
+    Py_ssize_t next_idx;
+    if (rval == NULL)
+        return NULL;
+
+    /* skip whitespace after [ */
+    while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+    /* only loop if the array is non-empty */
+    if (idx <= end_idx && str[idx] != ']') {
+        while (idx <= end_idx) {
+
+            /* read any JSON term  */
+            val = scan_once_unicode(s, pystr, idx, &next_idx);
+            if (val == NULL) {
+                if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+                    PyErr_Clear();
+                    raise_errmsg("Expecting object", pystr, idx);
+                }
+                goto bail;
+            }
+
+            if (PyList_Append(rval, val) == -1)
+                goto bail;
+
+            Py_CLEAR(val);
+            idx = next_idx;
+
+            /* skip whitespace between term and , */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+
+            /* bail if the array is closed or we didn't get the , delimiter */
+            if (idx > end_idx) break;
+            if (str[idx] == ']') {
+                break;
+            }
+            else if (str[idx] != ',') {
+                raise_errmsg("Expecting , delimiter", pystr, idx);
+                goto bail;
+            }
+            idx++;
+
+            /* skip whitespace after , */
+            while (idx <= end_idx && IS_WHITESPACE(str[idx])) idx++;
+        }
+    }
+
+    /* verify that idx < end_idx, str[idx] should be ']' */
+    if (idx > end_idx || str[idx] != ']') {
+        raise_errmsg("Expecting object", pystr, end_idx);
+        goto bail;
+    }
+    *next_idx_ptr = idx + 1;
+    return rval;
+bail:
+    Py_XDECREF(val);
+    Py_DECREF(rval);
+    return NULL;
+}
+
+static PyObject *
+_parse_constant(PyScannerObject *s, char *constant, Py_ssize_t idx, Py_ssize_t *next_idx_ptr) {
+    /* Read a JSON constant from PyString pystr.
+    constant is the constant string that was found
+        ("NaN", "Infinity", "-Infinity").
+    idx is the index of the first character of the constant
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the constant.
+
+    Returns the result of parse_constant
+    */
+    PyObject *cstr;
+    PyObject *rval;
+    /* constant is "NaN", "Infinity", or "-Infinity" */
+    cstr = PyString_InternFromString(constant);
+    if (cstr == NULL)
+        return NULL;
+
+    /* rval = parse_constant(constant) */
+    rval = PyObject_CallFunctionObjArgs(s->parse_constant, cstr, NULL);
+    idx += PyString_GET_SIZE(cstr);
+    Py_DECREF(cstr);
+    *next_idx_ptr = idx;
+    return rval;
+}
+
+static PyObject *
+_match_number_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) {
+    /* Read a JSON number from PyString pystr.
+    idx is the index of the first character of the number
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the number.
+
+    Returns a new PyObject representation of that number:
+        PyInt, PyLong, or PyFloat.
+        May return other types if parse_int or parse_float are set
+    */
+    char *str = PyString_AS_STRING(pystr);
+    Py_ssize_t end_idx = PyString_GET_SIZE(pystr) - 1;
+    Py_ssize_t idx = start;
+    int is_float = 0;
+    PyObject *rval;
+    PyObject *numstr;
+
+    /* read a sign if it's there, make sure it's not the end of the string */
+    if (str[idx] == '-') {
+        idx++;
+        if (idx > end_idx) {
+            PyErr_SetNone(PyExc_StopIteration);
+            return NULL;
+        }
+    }
+
+    /* read as many integer digits as we find as long as it doesn't start with 0 */
+    if (str[idx] >= '1' && str[idx] <= '9') {
+        idx++;
+        while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+    }
+    /* if it starts with 0 we only expect one integer digit */
+    else if (str[idx] == '0') {
+        idx++;
+    }
+    /* no integer digits, error */
+    else {
+        PyErr_SetNone(PyExc_StopIteration);
+        return NULL;
+    }
+
+    /* if the next char is '.' followed by a digit then read all float digits */
+    if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') {
+        is_float = 1;
+        idx += 2;
+        while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+    }
+
+    /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */
+    if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) {
+
+        /* save the index of the 'e' or 'E' just in case we need to backtrack */
+        Py_ssize_t e_start = idx;
+        idx++;
+
+        /* read an exponent sign if present */
+        if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++;
+
+        /* read all digits */
+        while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+
+        /* if we got a digit, then parse as float. if not, backtrack */
+        if (str[idx - 1] >= '0' && str[idx - 1] <= '9') {
+            is_float = 1;
+        }
+        else {
+            idx = e_start;
+        }
+    }
+
+    /* copy the section we determined to be a number */
+    numstr = PyString_FromStringAndSize(&str[start], idx - start);
+    if (numstr == NULL)
+        return NULL;
+    if (is_float) {
+        /* parse as a float using a fast path if available, otherwise call user defined method */
+        if (s->parse_float != (PyObject *)&PyFloat_Type) {
+            rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL);
+        }
+        else {
+            /* rval = PyFloat_FromDouble(PyOS_ascii_atof(PyString_AS_STRING(numstr))); */
+            double d = PyOS_string_to_double(PyString_AS_STRING(numstr),
+                                             NULL, NULL);
+            if (d == -1.0 && PyErr_Occurred())
+                return NULL;
+            rval = PyFloat_FromDouble(d);
+        }
+    }
+    else {
+        /* parse as an int using a fast path if available, otherwise call user defined method */
+        if (s->parse_int != (PyObject *)&PyInt_Type) {
+            rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL);
+        }
+        else {
+            rval = PyInt_FromString(PyString_AS_STRING(numstr), NULL, 10);
+        }
+    }
+    Py_DECREF(numstr);
+    *next_idx_ptr = idx;
+    return rval;
+}
+
+static PyObject *
+_match_number_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssize_t *next_idx_ptr) {
+    /* Read a JSON number from PyUnicode pystr.
+    idx is the index of the first character of the number
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the number.
+
+    Returns a new PyObject representation of that number:
+        PyInt, PyLong, or PyFloat.
+        May return other types if parse_int or parse_float are set
+    */
+    Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+    Py_ssize_t end_idx = PyUnicode_GET_SIZE(pystr) - 1;
+    Py_ssize_t idx = start;
+    int is_float = 0;
+    PyObject *rval;
+    PyObject *numstr;
+
+    /* read a sign if it's there, make sure it's not the end of the string */
+    if (str[idx] == '-') {
+        idx++;
+        if (idx > end_idx) {
+            PyErr_SetNone(PyExc_StopIteration);
+            return NULL;
+        }
+    }
+
+    /* read as many integer digits as we find as long as it doesn't start with 0 */
+    if (str[idx] >= '1' && str[idx] <= '9') {
+        idx++;
+        while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+    }
+    /* if it starts with 0 we only expect one integer digit */
+    else if (str[idx] == '0') {
+        idx++;
+    }
+    /* no integer digits, error */
+    else {
+        PyErr_SetNone(PyExc_StopIteration);
+        return NULL;
+    }
+
+    /* if the next char is '.' followed by a digit then read all float digits */
+    if (idx < end_idx && str[idx] == '.' && str[idx + 1] >= '0' && str[idx + 1] <= '9') {
+        is_float = 1;
+        idx += 2;
+        while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+    }
+
+    /* if the next char is 'e' or 'E' then maybe read the exponent (or backtrack) */
+    if (idx < end_idx && (str[idx] == 'e' || str[idx] == 'E')) {
+        Py_ssize_t e_start = idx;
+        idx++;
+
+        /* read an exponent sign if present */
+        if (idx < end_idx && (str[idx] == '-' || str[idx] == '+')) idx++;
+
+        /* read all digits */
+        while (idx <= end_idx && str[idx] >= '0' && str[idx] <= '9') idx++;
+
+        /* if we got a digit, then parse as float. if not, backtrack */
+        if (str[idx - 1] >= '0' && str[idx - 1] <= '9') {
+            is_float = 1;
+        }
+        else {
+            idx = e_start;
+        }
+    }
+
+    /* copy the section we determined to be a number */
+    numstr = PyUnicode_FromUnicode(&str[start], idx - start);
+    if (numstr == NULL)
+        return NULL;
+    if (is_float) {
+        /* parse as a float using a fast path if available, otherwise call user defined method */
+        if (s->parse_float != (PyObject *)&PyFloat_Type) {
+            rval = PyObject_CallFunctionObjArgs(s->parse_float, numstr, NULL);
+        }
+        else {
+            rval = PyFloat_FromString(numstr, NULL);
+        }
+    }
+    else {
+        /* no fast path for unicode -> int, just call */
+        rval = PyObject_CallFunctionObjArgs(s->parse_int, numstr, NULL);
+    }
+    Py_DECREF(numstr);
+    *next_idx_ptr = idx;
+    return rval;
+}
+
+static PyObject *
+scan_once_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr)
+{
+    /* Read one JSON term (of any kind) from PyString pystr.
+    idx is the index of the first character of the term
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the number.
+
+    Returns a new PyObject representation of the term.
+    */
+    char *str = PyString_AS_STRING(pystr);
+    Py_ssize_t length = PyString_GET_SIZE(pystr);
+    if (idx >= length) {
+        PyErr_SetNone(PyExc_StopIteration);
+        return NULL;
+    }
+    switch (str[idx]) {
+        case '"':
+            /* string */
+            return scanstring_str(pystr, idx + 1,
+                PyString_AS_STRING(s->encoding),
+                PyObject_IsTrue(s->strict),
+                next_idx_ptr);
+        case '{':
+            /* object */
+            return _parse_object_str(s, pystr, idx + 1, next_idx_ptr);
+        case '[':
+            /* array */
+            return _parse_array_str(s, pystr, idx + 1, next_idx_ptr);
+        case 'n':
+            /* null */
+            if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') {
+                Py_INCREF(Py_None);
+                *next_idx_ptr = idx + 4;
+                return Py_None;
+            }
+            break;
+        case 't':
+            /* true */
+            if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') {
+                Py_INCREF(Py_True);
+                *next_idx_ptr = idx + 4;
+                return Py_True;
+            }
+            break;
+        case 'f':
+            /* false */
+            if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') {
+                Py_INCREF(Py_False);
+                *next_idx_ptr = idx + 5;
+                return Py_False;
+            }
+            break;
+        case 'N':
+            /* NaN */
+            if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') {
+                return _parse_constant(s, "NaN", idx, next_idx_ptr);
+            }
+            break;
+        case 'I':
+            /* Infinity */
+            if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') {
+                return _parse_constant(s, "Infinity", idx, next_idx_ptr);
+            }
+            break;
+        case '-':
+            /* -Infinity */
+            if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') {
+                return _parse_constant(s, "-Infinity", idx, next_idx_ptr);
+            }
+            break;
+    }
+    /* Didn't find a string, object, array, or named constant. Look for a number. */
+    return _match_number_str(s, pystr, idx, next_idx_ptr);
+}
+
+static PyObject *
+scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_t *next_idx_ptr)
+{
+    /* Read one JSON term (of any kind) from PyUnicode pystr.
+    idx is the index of the first character of the term
+    *next_idx_ptr is a return-by-reference index to the first character after
+        the number.
+
+    Returns a new PyObject representation of the term.
+    */
+    Py_UNICODE *str = PyUnicode_AS_UNICODE(pystr);
+    Py_ssize_t length = PyUnicode_GET_SIZE(pystr);
+    if (idx >= length) {
+        PyErr_SetNone(PyExc_StopIteration);
+        return NULL;
+    }
+    switch (str[idx]) {
+        case '"':
+            /* string */
+            return scanstring_unicode(pystr, idx + 1,
+                PyObject_IsTrue(s->strict),
+                next_idx_ptr);
+        case '{':
+            /* object */
+            return _parse_object_unicode(s, pystr, idx + 1, next_idx_ptr);
+        case '[':
+            /* array */
+            return _parse_array_unicode(s, pystr, idx + 1, next_idx_ptr);
+        case 'n':
+            /* null */
+            if ((idx + 3 < length) && str[idx + 1] == 'u' && str[idx + 2] == 'l' && str[idx + 3] == 'l') {
+                Py_INCREF(Py_None);
+                *next_idx_ptr = idx + 4;
+                return Py_None;
+            }
+            break;
+        case 't':
+            /* true */
+            if ((idx + 3 < length) && str[idx + 1] == 'r' && str[idx + 2] == 'u' && str[idx + 3] == 'e') {
+                Py_INCREF(Py_True);
+                *next_idx_ptr = idx + 4;
+                return Py_True;
+            }
+            break;
+        case 'f':
+            /* false */
+            if ((idx + 4 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'l' && str[idx + 3] == 's' && str[idx + 4] == 'e') {
+                Py_INCREF(Py_False);
+                *next_idx_ptr = idx + 5;
+                return Py_False;
+            }
+            break;
+        case 'N':
+            /* NaN */
+            if ((idx + 2 < length) && str[idx + 1] == 'a' && str[idx + 2] == 'N') {
+                return _parse_constant(s, "NaN", idx, next_idx_ptr);
+            }
+            break;
+        case 'I':
+            /* Infinity */
+            if ((idx + 7 < length) && str[idx + 1] == 'n' && str[idx + 2] == 'f' && str[idx + 3] == 'i' && str[idx + 4] == 'n' && str[idx + 5] == 'i' && str[idx + 6] == 't' && str[idx + 7] == 'y') {
+                return _parse_constant(s, "Infinity", idx, next_idx_ptr);
+            }
+            break;
+        case '-':
+            /* -Infinity */
+            if ((idx + 8 < length) && str[idx + 1] == 'I' && str[idx + 2] == 'n' && str[idx + 3] == 'f' && str[idx + 4] == 'i' && str[idx + 5] == 'n' && str[idx + 6] == 'i' && str[idx + 7] == 't' && str[idx + 8] == 'y') {
+                return _parse_constant(s, "-Infinity", idx, next_idx_ptr);
+            }
+            break;
+    }
+    /* Didn't find a string, object, array, or named constant. Look for a number. */
+    return _match_number_unicode(s, pystr, idx, next_idx_ptr);
+}
+
+static PyObject *
+scanner_call(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    /* Python callable interface to scan_once_{str,unicode} */
+    PyObject *pystr;
+    PyObject *rval;
+    Py_ssize_t idx;
+    Py_ssize_t next_idx = -1;
+    static char *kwlist[] = {"string", "idx", NULL};
+    PyScannerObject *s;
+    assert(PyScanner_Check(self));
+    s = (PyScannerObject *)self;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:scan_once", kwlist, &pystr, _convertPyInt_AsSsize_t, &idx))
+        return NULL;
+
+    if (PyString_Check(pystr)) {
+        rval = scan_once_str(s, pystr, idx, &next_idx);
+    }
+    else if (PyUnicode_Check(pystr)) {
+        rval = scan_once_unicode(s, pystr, idx, &next_idx);
+    }
+    else {
+        PyErr_Format(PyExc_TypeError,
+                 "first argument must be a string, not %.80s",
+                 Py_TYPE(pystr)->tp_name);
+        return NULL;
+    }
+    PyDict_Clear(s->memo);
+    return _build_rval_index_tuple(rval, next_idx);
+}
+
+static PyObject *
+scanner_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyScannerObject *s;
+    s = (PyScannerObject *)type->tp_alloc(type, 0);
+    if (s != NULL) {
+        s->encoding = NULL;
+        s->strict = NULL;
+        s->object_hook = NULL;
+        s->pairs_hook = NULL;
+        s->parse_float = NULL;
+        s->parse_int = NULL;
+        s->parse_constant = NULL;
+    }
+    return (PyObject *)s;
+}
+
+static int
+scanner_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    /* Initialize Scanner object */
+    PyObject *ctx;
+    static char *kwlist[] = {"context", NULL};
+    PyScannerObject *s;
+
+    assert(PyScanner_Check(self));
+    s = (PyScannerObject *)self;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:make_scanner", kwlist, &ctx))
+        return -1;
+    
+    if (s->memo == NULL) {
+        s->memo = PyDict_New();
+        if (s->memo == NULL)
+            goto bail;
+    }
+
+    /* PyString_AS_STRING is used on encoding */
+    s->encoding = PyObject_GetAttrString(ctx, "encoding");
+    if (s->encoding == NULL)
+        goto bail;
+    if (s->encoding == Py_None) {
+        Py_DECREF(Py_None);
+        s->encoding = PyString_InternFromString(DEFAULT_ENCODING);
+    }
+    else if (PyUnicode_Check(s->encoding)) {
+        PyObject *tmp = PyUnicode_AsEncodedString(s->encoding, NULL, NULL);
+        Py_DECREF(s->encoding);
+        s->encoding = tmp;
+    }
+    if (s->encoding == NULL || !PyString_Check(s->encoding))
+        goto bail;
+
+    /* All of these will fail "gracefully" so we don't need to verify them */
+    s->strict = PyObject_GetAttrString(ctx, "strict");
+    if (s->strict == NULL)
+        goto bail;
+    s->object_hook = PyObject_GetAttrString(ctx, "object_hook");
+    if (s->object_hook == NULL)
+        goto bail;
+    s->pairs_hook = PyObject_GetAttrString(ctx, "object_pairs_hook");
+    if (s->pairs_hook == NULL)
+        goto bail;
+    s->parse_float = PyObject_GetAttrString(ctx, "parse_float");
+    if (s->parse_float == NULL)
+        goto bail;
+    s->parse_int = PyObject_GetAttrString(ctx, "parse_int");
+    if (s->parse_int == NULL)
+        goto bail;
+    s->parse_constant = PyObject_GetAttrString(ctx, "parse_constant");
+    if (s->parse_constant == NULL)
+        goto bail;
+
+    return 0;
+
+bail:
+    Py_CLEAR(s->encoding);
+    Py_CLEAR(s->strict);
+    Py_CLEAR(s->object_hook);
+    Py_CLEAR(s->pairs_hook);
+    Py_CLEAR(s->parse_float);
+    Py_CLEAR(s->parse_int);
+    Py_CLEAR(s->parse_constant);
+    return -1;
+}
+
+PyDoc_STRVAR(scanner_doc, "JSON scanner object");
+
+static
+PyTypeObject PyScannerType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                    /* tp_internal */
+    "simplejson._speedups.Scanner",       /* tp_name */
+    sizeof(PyScannerObject), /* tp_basicsize */
+    0,                    /* tp_itemsize */
+    scanner_dealloc, /* tp_dealloc */
+    0,                    /* tp_print */
+    0,                    /* tp_getattr */
+    0,                    /* tp_setattr */
+    0,                    /* tp_compare */
+    0,                    /* tp_repr */
+    0,                    /* tp_as_number */
+    0,                    /* tp_as_sequence */
+    0,                    /* tp_as_mapping */
+    0,                    /* tp_hash */
+    scanner_call,         /* tp_call */
+    0,                    /* tp_str */
+    0,/* PyObject_GenericGetAttr, */                    /* tp_getattro */
+    0,/* PyObject_GenericSetAttr, */                    /* tp_setattro */
+    0,                    /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,   /* tp_flags */
+    scanner_doc,          /* tp_doc */
+    scanner_traverse,                    /* tp_traverse */
+    scanner_clear,                    /* tp_clear */
+    0,                    /* tp_richcompare */
+    0,                    /* tp_weaklistoffset */
+    0,                    /* tp_iter */
+    0,                    /* tp_iternext */
+    0,                    /* tp_methods */
+    scanner_members,                    /* tp_members */
+    0,                    /* tp_getset */
+    0,                    /* tp_base */
+    0,                    /* tp_dict */
+    0,                    /* tp_descr_get */
+    0,                    /* tp_descr_set */
+    0,                    /* tp_dictoffset */
+    scanner_init,                    /* tp_init */
+    0,/* PyType_GenericAlloc, */        /* tp_alloc */
+    scanner_new,          /* tp_new */
+    0,/* PyObject_GC_Del, */              /* tp_free */
+};
+
+static PyObject *
+encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyEncoderObject *s;
+    s = (PyEncoderObject *)type->tp_alloc(type, 0);
+    if (s != NULL) {
+        s->markers = NULL;
+        s->defaultfn = NULL;
+        s->encoder = NULL;
+        s->indent = NULL;
+        s->key_separator = NULL;
+        s->item_separator = NULL;
+        s->sort_keys = NULL;
+        s->skipkeys = NULL;
+        s->key_memo = NULL;
+    }
+    return (PyObject *)s;
+}
+
+static int
+encoder_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    /* initialize Encoder object */
+    static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", "key_memo", "use_decimal", NULL};
+
+    PyEncoderObject *s;
+    PyObject *markers, *defaultfn, *encoder, *indent, *key_separator;
+    PyObject *item_separator, *sort_keys, *skipkeys, *allow_nan, *key_memo, *use_decimal;
+
+    assert(PyEncoder_Check(self));
+    s = (PyEncoderObject *)self;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOOOO:make_encoder", kwlist,
+        &markers, &defaultfn, &encoder, &indent, &key_separator, &item_separator,
+        &sort_keys, &skipkeys, &allow_nan, &key_memo, &use_decimal))
+        return -1;
+
+    s->markers = markers;
+    s->defaultfn = defaultfn;
+    s->encoder = encoder;
+    s->indent = indent;
+    s->key_separator = key_separator;
+    s->item_separator = item_separator;
+    s->sort_keys = sort_keys;
+    s->skipkeys = skipkeys;
+    s->key_memo = key_memo;
+    s->fast_encode = (PyCFunction_Check(s->encoder) && PyCFunction_GetFunction(s->encoder) == (PyCFunction)py_encode_basestring_ascii);
+    s->allow_nan = PyObject_IsTrue(allow_nan);
+    s->use_decimal = PyObject_IsTrue(use_decimal);
+
+    Py_INCREF(s->markers);
+    Py_INCREF(s->defaultfn);
+    Py_INCREF(s->encoder);
+    Py_INCREF(s->indent);
+    Py_INCREF(s->key_separator);
+    Py_INCREF(s->item_separator);
+    Py_INCREF(s->sort_keys);
+    Py_INCREF(s->skipkeys);
+    Py_INCREF(s->key_memo);
+    return 0;
+}
+
+static PyObject *
+encoder_call(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    /* Python callable interface to encode_listencode_obj */
+    static char *kwlist[] = {"obj", "_current_indent_level", NULL};
+    PyObject *obj;
+    PyObject *rval;
+    Py_ssize_t indent_level;
+    PyEncoderObject *s;
+    assert(PyEncoder_Check(self));
+    s = (PyEncoderObject *)self;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&:_iterencode", kwlist,
+        &obj, _convertPyInt_AsSsize_t, &indent_level))
+        return NULL;
+    rval = PyList_New(0);
+    if (rval == NULL)
+        return NULL;
+    if (encoder_listencode_obj(s, rval, obj, indent_level)) {
+        Py_DECREF(rval);
+        return NULL;
+    }
+    return rval;
+}
+
+static PyObject *
+_encoded_const(PyObject *obj)
+{
+    /* Return the JSON string representation of None, True, False */
+    if (obj == Py_None) {
+        static PyObject *s_null = NULL;
+        if (s_null == NULL) {
+            s_null = PyString_InternFromString("null");
+        }
+        Py_INCREF(s_null);
+        return s_null;
+    }
+    else if (obj == Py_True) {
+        static PyObject *s_true = NULL;
+        if (s_true == NULL) {
+            s_true = PyString_InternFromString("true");
+        }
+        Py_INCREF(s_true);
+        return s_true;
+    }
+    else if (obj == Py_False) {
+        static PyObject *s_false = NULL;
+        if (s_false == NULL) {
+            s_false = PyString_InternFromString("false");
+        }
+        Py_INCREF(s_false);
+        return s_false;
+    }
+    else {
+        PyErr_SetString(PyExc_ValueError, "not a const");
+        return NULL;
+    }
+}
+
+static PyObject *
+encoder_encode_float(PyEncoderObject *s, PyObject *obj)
+{
+    /* Return the JSON representation of a PyFloat */
+    double i = PyFloat_AS_DOUBLE(obj);
+    if (!Py_IS_FINITE(i)) {
+        if (!s->allow_nan) {
+            PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
+            return NULL;
+        }
+        if (i > 0) {
+            return PyString_FromString("Infinity");
+        }
+        else if (i < 0) {
+            return PyString_FromString("-Infinity");
+        }
+        else {
+            return PyString_FromString("NaN");
+        }
+    }
+    /* Use a better float format here? */
+    return PyObject_Repr(obj);
+}
+
+static PyObject *
+encoder_encode_string(PyEncoderObject *s, PyObject *obj)
+{
+    /* Return the JSON representation of a string */
+    if (s->fast_encode)
+        return py_encode_basestring_ascii(NULL, obj);
+    else
+        return PyObject_CallFunctionObjArgs(s->encoder, obj, NULL);
+}
+
+static int
+_steal_list_append(PyObject *lst, PyObject *stolen)
+{
+    /* Append stolen and then decrement its reference count */
+    int rval = PyList_Append(lst, stolen);
+    Py_DECREF(stolen);
+    return rval;
+}
+
+static int
+encoder_listencode_obj(PyEncoderObject *s, PyObject *rval, PyObject *obj, Py_ssize_t indent_level)
+{
+    /* Encode Python object obj to a JSON term, rval is a PyList */
+    PyObject *newobj;
+    int rv;
+
+    if (obj == Py_None || obj == Py_True || obj == Py_False) {
+        PyObject *cstr = _encoded_const(obj);
+        if (cstr == NULL)
+            return -1;
+        return _steal_list_append(rval, cstr);
+    }
+    else if (PyString_Check(obj) || PyUnicode_Check(obj))
+    {
+        PyObject *encoded = encoder_encode_string(s, obj);
+        if (encoded == NULL)
+            return -1;
+        return _steal_list_append(rval, encoded);
+    }
+    else if (PyInt_Check(obj) || PyLong_Check(obj)) {
+        PyObject *encoded = PyObject_Str(obj);
+        if (encoded == NULL)
+            return -1;
+        return _steal_list_append(rval, encoded);
+    }
+    else if (PyFloat_Check(obj)) {
+        PyObject *encoded = encoder_encode_float(s, obj);
+        if (encoded == NULL)
+            return -1;
+        return _steal_list_append(rval, encoded);
+    }
+    else if (PyList_Check(obj) || PyTuple_Check(obj)) {
+        return encoder_listencode_list(s, rval, obj, indent_level);
+    }
+    else if (PyDict_Check(obj)) {
+        return encoder_listencode_dict(s, rval, obj, indent_level);
+    }
+    else if (s->use_decimal && Decimal_Check(obj)) {
+        PyObject *encoded = PyObject_Str(obj);
+        if (encoded == NULL)
+            return -1;
+        return _steal_list_append(rval, encoded);
+    }
+    else {
+        PyObject *ident = NULL;
+        if (s->markers != Py_None) {
+            int has_key;
+            ident = PyLong_FromVoidPtr(obj);
+            if (ident == NULL)
+                return -1;
+            has_key = PyDict_Contains(s->markers, ident);
+            if (has_key) {
+                if (has_key != -1)
+                    PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+                Py_DECREF(ident);
+                return -1;
+            }
+            if (PyDict_SetItem(s->markers, ident, obj)) {
+                Py_DECREF(ident);
+                return -1;
+            }
+        }
+        newobj = PyObject_CallFunctionObjArgs(s->defaultfn, obj, NULL);
+        if (newobj == NULL) {
+            Py_XDECREF(ident);
+            return -1;
+        }
+        rv = encoder_listencode_obj(s, rval, newobj, indent_level);
+        Py_DECREF(newobj);
+        if (rv) {
+            Py_XDECREF(ident);
+            return -1;
+        }
+        if (ident != NULL) {
+            if (PyDict_DelItem(s->markers, ident)) {
+                Py_XDECREF(ident);
+                return -1;
+            }
+            Py_XDECREF(ident);
+        }
+        return rv;
+    }
+}
+
+static int
+encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ssize_t indent_level)
+{
+    /* Encode Python dict dct a JSON term, rval is a PyList */
+    static PyObject *open_dict = NULL;
+    static PyObject *close_dict = NULL;
+    static PyObject *empty_dict = NULL;
+    static PyObject *iteritems = NULL;
+    PyObject *kstr = NULL;
+    PyObject *ident = NULL;
+    PyObject *key, *value;
+    PyObject *iter = NULL;
+    PyObject *item = NULL;
+    PyObject *encoded = NULL;
+    int skipkeys;
+    Py_ssize_t idx;
+
+    if (open_dict == NULL || close_dict == NULL || empty_dict == NULL || iteritems == NULL) {
+        open_dict = PyString_InternFromString("{");
+        close_dict = PyString_InternFromString("}");
+        empty_dict = PyString_InternFromString("{}");
+        iteritems = PyString_InternFromString("iteritems");
+        if (open_dict == NULL || close_dict == NULL || empty_dict == NULL || iteritems == NULL)
+            return -1;
+    }
+    if (PyDict_Size(dct) == 0)
+        return PyList_Append(rval, empty_dict);
+
+    if (s->markers != Py_None) {
+        int has_key;
+        ident = PyLong_FromVoidPtr(dct);
+        if (ident == NULL)
+            goto bail;
+        has_key = PyDict_Contains(s->markers, ident);
+        if (has_key) {
+            if (has_key != -1)
+                PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+            goto bail;
+        }
+        if (PyDict_SetItem(s->markers, ident, dct)) {
+            goto bail;
+        }
+    }
+
+    if (PyList_Append(rval, open_dict))
+        goto bail;
+
+    if (s->indent != Py_None) {
+        /* TODO: DOES NOT RUN */
+        indent_level += 1;
+        /*
+            newline_indent = '\n' + (_indent * _current_indent_level)
+            separator = _item_separator + newline_indent
+            buf += newline_indent
+        */
+    }
+
+    /* TODO: C speedup not implemented for sort_keys */
+
+    skipkeys = PyObject_IsTrue(s->skipkeys);
+    idx = 0;
+    iter = PyObject_CallMethodObjArgs(dct, iteritems, NULL);
+    if (iter == NULL)
+        goto bail;
+    while ((item = PyIter_Next(iter))) {
+
+        key = PyTuple_GetItem(item, 0);
+        if (key == NULL)
+            goto bail;
+        value = PyTuple_GetItem(item, 1);
+        if (value == NULL)
+            goto bail;
+        
+        encoded = PyDict_GetItem(s->key_memo, key);
+        if (encoded != NULL) {
+            Py_INCREF(encoded);
+        }
+        else if (PyString_Check(key) || PyUnicode_Check(key)) {
+            Py_INCREF(key);
+            kstr = key;
+        }
+        else if (PyFloat_Check(key)) {
+            kstr = encoder_encode_float(s, key);
+            if (kstr == NULL)
+                goto bail;
+        }
+        else if (PyInt_Check(key) || PyLong_Check(key)) {
+            kstr = PyObject_Str(key);
+            if (kstr == NULL)
+                goto bail;
+        }
+        else if (key == Py_True || key == Py_False || key == Py_None) {
+            kstr = _encoded_const(key);
+            if (kstr == NULL)
+                goto bail;
+        }
+        else if (skipkeys) {
+            Py_DECREF(item);
+            continue;
+        }
+        else {
+            /* TODO: include repr of key */
+            PyErr_SetString(PyExc_ValueError, "keys must be a string");
+            goto bail;
+        }
+
+        if (idx) {
+            if (PyList_Append(rval, s->item_separator))
+                goto bail;
+        }
+
+        if (encoded == NULL) {
+            encoded = encoder_encode_string(s, kstr);
+            Py_CLEAR(kstr);
+            if (encoded == NULL)
+                goto bail;
+            if (PyDict_SetItem(s->key_memo, key, encoded))
+                goto bail;
+        }
+        if (PyList_Append(rval, encoded)) {
+            goto bail;
+        }
+        Py_CLEAR(encoded);
+        if (PyList_Append(rval, s->key_separator))
+            goto bail;
+        if (encoder_listencode_obj(s, rval, value, indent_level))
+            goto bail;
+        Py_CLEAR(item);
+        idx += 1;
+    }
+    Py_CLEAR(iter);
+    if (PyErr_Occurred())
+        goto bail;
+    if (ident != NULL) {
+        if (PyDict_DelItem(s->markers, ident))
+            goto bail;
+        Py_CLEAR(ident);
+    }
+    if (s->indent != Py_None) {
+        /* TODO: DOES NOT RUN */
+        indent_level -= 1;
+        /*
+            yield '\n' + (_indent * _current_indent_level)
+        */
+    }
+    if (PyList_Append(rval, close_dict))
+        goto bail;
+    return 0;
+
+bail:
+    Py_XDECREF(encoded);
+    Py_XDECREF(item);
+    Py_XDECREF(iter);
+    Py_XDECREF(kstr);
+    Py_XDECREF(ident);
+    return -1;
+}
+
+
+static int
+encoder_listencode_list(PyEncoderObject *s, PyObject *rval, PyObject *seq, Py_ssize_t indent_level)
+{
+    /* Encode Python list seq to a JSON term, rval is a PyList */
+    static PyObject *open_array = NULL;
+    static PyObject *close_array = NULL;
+    static PyObject *empty_array = NULL;
+    PyObject *ident = NULL;
+    PyObject *iter = NULL;
+    PyObject *obj = NULL;
+    int is_true;
+    int i = 0;
+
+    if (open_array == NULL || close_array == NULL || empty_array == NULL) {
+        open_array = PyString_InternFromString("[");
+        close_array = PyString_InternFromString("]");
+        empty_array = PyString_InternFromString("[]");
+        if (open_array == NULL || close_array == NULL || empty_array == NULL)
+            return -1;
+    }
+    ident = NULL;
+    is_true = PyObject_IsTrue(seq);
+    if (is_true == -1)
+        return -1;
+    else if (is_true == 0)
+        return PyList_Append(rval, empty_array);
+
+    if (s->markers != Py_None) {
+        int has_key;
+        ident = PyLong_FromVoidPtr(seq);
+        if (ident == NULL)
+            goto bail;
+        has_key = PyDict_Contains(s->markers, ident);
+        if (has_key) {
+            if (has_key != -1)
+                PyErr_SetString(PyExc_ValueError, "Circular reference detected");
+            goto bail;
+        }
+        if (PyDict_SetItem(s->markers, ident, seq)) {
+            goto bail;
+        }
+    }
+
+    iter = PyObject_GetIter(seq);
+    if (iter == NULL)
+        goto bail;
+
+    if (PyList_Append(rval, open_array))
+        goto bail;
+    if (s->indent != Py_None) {
+        /* TODO: DOES NOT RUN */
+        indent_level += 1;
+        /*
+            newline_indent = '\n' + (_indent * _current_indent_level)
+            separator = _item_separator + newline_indent
+            buf += newline_indent
+        */
+    }
+    while ((obj = PyIter_Next(iter))) {
+        if (i) {
+            if (PyList_Append(rval, s->item_separator))
+                goto bail;
+        }
+        if (encoder_listencode_obj(s, rval, obj, indent_level))
+            goto bail;
+        i++;
+        Py_CLEAR(obj);
+    }
+    Py_CLEAR(iter);
+    if (PyErr_Occurred())
+        goto bail;
+    if (ident != NULL) {
+        if (PyDict_DelItem(s->markers, ident))
+            goto bail;
+        Py_CLEAR(ident);
+    }
+    if (s->indent != Py_None) {
+        /* TODO: DOES NOT RUN */
+        indent_level -= 1;
+        /*
+            yield '\n' + (_indent * _current_indent_level)
+        */
+    }
+    if (PyList_Append(rval, close_array))
+        goto bail;
+    return 0;
+
+bail:
+    Py_XDECREF(obj);
+    Py_XDECREF(iter);
+    Py_XDECREF(ident);
+    return -1;
+}
+
+static void
+encoder_dealloc(PyObject *self)
+{
+    /* Deallocate Encoder */
+    encoder_clear(self);
+    Py_TYPE(self)->tp_free(self);
+}
+
+static int
+encoder_traverse(PyObject *self, visitproc visit, void *arg)
+{
+    PyEncoderObject *s;
+    assert(PyEncoder_Check(self));
+    s = (PyEncoderObject *)self;
+    Py_VISIT(s->markers);
+    Py_VISIT(s->defaultfn);
+    Py_VISIT(s->encoder);
+    Py_VISIT(s->indent);
+    Py_VISIT(s->key_separator);
+    Py_VISIT(s->item_separator);
+    Py_VISIT(s->sort_keys);
+    Py_VISIT(s->skipkeys);
+    Py_VISIT(s->key_memo);
+    return 0;
+}
+
+static int
+encoder_clear(PyObject *self)
+{
+    /* Deallocate Encoder */
+    PyEncoderObject *s;
+    assert(PyEncoder_Check(self));
+    s = (PyEncoderObject *)self;
+    Py_CLEAR(s->markers);
+    Py_CLEAR(s->defaultfn);
+    Py_CLEAR(s->encoder);
+    Py_CLEAR(s->indent);
+    Py_CLEAR(s->key_separator);
+    Py_CLEAR(s->item_separator);
+    Py_CLEAR(s->sort_keys);
+    Py_CLEAR(s->skipkeys);
+    Py_CLEAR(s->key_memo);
+    return 0;
+}
+
+PyDoc_STRVAR(encoder_doc, "_iterencode(obj, _current_indent_level) -> iterable");
+
+static
+PyTypeObject PyEncoderType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                    /* tp_internal */
+    "simplejson._speedups.Encoder",       /* tp_name */
+    sizeof(PyEncoderObject), /* tp_basicsize */
+    0,                    /* tp_itemsize */
+    encoder_dealloc, /* tp_dealloc */
+    0,                    /* tp_print */
+    0,                    /* tp_getattr */
+    0,                    /* tp_setattr */
+    0,                    /* tp_compare */
+    0,                    /* tp_repr */
+    0,                    /* tp_as_number */
+    0,                    /* tp_as_sequence */
+    0,                    /* tp_as_mapping */
+    0,                    /* tp_hash */
+    encoder_call,         /* tp_call */
+    0,                    /* tp_str */
+    0,                    /* tp_getattro */
+    0,                    /* tp_setattro */
+    0,                    /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,   /* tp_flags */
+    encoder_doc,          /* tp_doc */
+    encoder_traverse,     /* tp_traverse */
+    encoder_clear,        /* tp_clear */
+    0,                    /* tp_richcompare */
+    0,                    /* tp_weaklistoffset */
+    0,                    /* tp_iter */
+    0,                    /* tp_iternext */
+    0,                    /* tp_methods */
+    encoder_members,      /* tp_members */
+    0,                    /* tp_getset */
+    0,                    /* tp_base */
+    0,                    /* tp_dict */
+    0,                    /* tp_descr_get */
+    0,                    /* tp_descr_set */
+    0,                    /* tp_dictoffset */
+    encoder_init,         /* tp_init */
+    0,                    /* tp_alloc */
+    encoder_new,          /* tp_new */
+    0,                    /* tp_free */
+};
+
+static PyMethodDef speedups_methods[] = {
+    {"encode_basestring_ascii",
+        (PyCFunction)py_encode_basestring_ascii,
+        METH_O,
+        pydoc_encode_basestring_ascii},
+    {"scanstring",
+        (PyCFunction)py_scanstring,
+        METH_VARARGS,
+        pydoc_scanstring},
+    {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(module_doc,
+"simplejson speedups\n");
+
+void
+init_speedups(void)
+{
+    PyObject *m, *decimal;
+    PyScannerType.tp_new = PyType_GenericNew;
+    if (PyType_Ready(&PyScannerType) < 0)
+        return;
+    PyEncoderType.tp_new = PyType_GenericNew;
+    if (PyType_Ready(&PyEncoderType) < 0)
+        return;
+
+    decimal = PyImport_ImportModule("decimal");
+    if (decimal == NULL)
+        return;
+    DecimalTypePtr = (PyTypeObject*)PyObject_GetAttrString(decimal, "Decimal");
+    Py_DECREF(decimal);
+    if (DecimalTypePtr == NULL)
+        return;
+
+    m = Py_InitModule3("_speedups", speedups_methods, module_doc);
+    Py_INCREF((PyObject*)&PyScannerType);
+    PyModule_AddObject(m, "make_scanner", (PyObject*)&PyScannerType);
+    Py_INCREF((PyObject*)&PyEncoderType);
+    PyModule_AddObject(m, "make_encoder", (PyObject*)&PyEncoderType);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/simplejson/decoder.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,421 @@
+"""Implementation of JSONDecoder
+"""
+import re
+import sys
+import struct
+
+from simplejson.scanner import make_scanner
+def _import_c_scanstring():
+    try:
+        from simplejson._speedups import scanstring
+        return scanstring
+    except ImportError:
+        return None
+c_scanstring = _import_c_scanstring()
+
+__all__ = ['JSONDecoder']
+
+FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
+
+def _floatconstants():
+    _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
+    # The struct module in Python 2.4 would get frexp() out of range here
+    # when an endian is specified in the format string. Fixed in Python 2.5+
+    if sys.byteorder != 'big':
+        _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
+    nan, inf = struct.unpack('dd', _BYTES)
+    return nan, inf, -inf
+
+NaN, PosInf, NegInf = _floatconstants()
+
+
+class JSONDecodeError(ValueError):
+    """Subclass of ValueError with the following additional properties:
+    
+    msg: The unformatted error message
+    doc: The JSON document being parsed
+    pos: The start index of doc where parsing failed
+    end: The end index of doc where parsing failed (may be None)
+    lineno: The line corresponding to pos
+    colno: The column corresponding to pos
+    endlineno: The line corresponding to end (may be None)
+    endcolno: The column corresponding to end (may be None)
+    
+    """
+    def __init__(self, msg, doc, pos, end=None):
+        ValueError.__init__(self, errmsg(msg, doc, pos, end=end))
+        self.msg = msg
+        self.doc = doc
+        self.pos = pos
+        self.end = end
+        self.lineno, self.colno = linecol(doc, pos)
+        if end is not None:
+            self.endlineno, self.endcolno = linecol(doc, pos)
+        else:
+            self.endlineno, self.endcolno = None, None
+
+
+def linecol(doc, pos):
+    lineno = doc.count('\n', 0, pos) + 1
+    if lineno == 1:
+        colno = pos
+    else:
+        colno = pos - doc.rindex('\n', 0, pos)
+    return lineno, colno
+
+
+def errmsg(msg, doc, pos, end=None):
+    # Note that this function is called from _speedups
+    lineno, colno = linecol(doc, pos)
+    if end is None:
+        #fmt = '{0}: line {1} column {2} (char {3})'
+        #return fmt.format(msg, lineno, colno, pos)
+        fmt = '%s: line %d column %d (char %d)'
+        return fmt % (msg, lineno, colno, pos)
+    endlineno, endcolno = linecol(doc, end)
+    #fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
+    #return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
+    fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
+    return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
+
+
+_CONSTANTS = {
+    '-Infinity': NegInf,
+    'Infinity': PosInf,
+    'NaN': NaN,
+}
+
+STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
+BACKSLASH = {
+    '"': u'"', '\\': u'\\', '/': u'/',
+    'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
+}
+
+DEFAULT_ENCODING = "utf-8"
+
+def py_scanstring(s, end, encoding=None, strict=True,
+        _b=BACKSLASH, _m=STRINGCHUNK.match):
+    """Scan the string s for a JSON string. End is the index of the
+    character in s after the quote that started the JSON string.
+    Unescapes all valid JSON string escape sequences and raises ValueError
+    on attempt to decode an invalid string. If strict is False then literal
+    control characters are allowed in the string.
+
+    Returns a tuple of the decoded string and the index of the character in s
+    after the end quote."""
+    if encoding is None:
+        encoding = DEFAULT_ENCODING
+    chunks = []
+    _append = chunks.append
+    begin = end - 1
+    while 1:
+        chunk = _m(s, end)
+        if chunk is None:
+            raise JSONDecodeError(
+                "Unterminated string starting at", s, begin)
+        end = chunk.end()
+        content, terminator = chunk.groups()
+        # Content is contains zero or more unescaped string characters
+        if content:
+            if not isinstance(content, unicode):
+                content = unicode(content, encoding)
+            _append(content)
+        # Terminator is the end of string, a literal control character,
+        # or a backslash denoting that an escape sequence follows
+        if terminator == '"':
+            break
+        elif terminator != '\\':
+            if strict:
+                msg = "Invalid control character %r at" % (terminator,)
+                #msg = "Invalid control character {0!r} at".format(terminator)
+                raise JSONDecodeError(msg, s, end)
+            else:
+                _append(terminator)
+                continue
+        try:
+            esc = s[end]
+        except IndexError:
+            raise JSONDecodeError(
+                "Unterminated string starting at", s, begin)
+        # If not a unicode escape sequence, must be in the lookup table
+        if esc != 'u':
+            try:
+                char = _b[esc]
+            except KeyError:
+                msg = "Invalid \\escape: " + repr(esc)
+                raise JSONDecodeError(msg, s, end)
+            end += 1
+        else:
+            # Unicode escape sequence
+            esc = s[end + 1:end + 5]
+            next_end = end + 5
+            if len(esc) != 4:
+                msg = "Invalid \\uXXXX escape"
+                raise JSONDecodeError(msg, s, end)
+            uni = int(esc, 16)
+            # Check for surrogate pair on UCS-4 systems
+            if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
+                msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
+                if not s[end + 5:end + 7] == '\\u':
+                    raise JSONDecodeError(msg, s, end)
+                esc2 = s[end + 7:end + 11]
+                if len(esc2) != 4:
+                    raise JSONDecodeError(msg, s, end)
+                uni2 = int(esc2, 16)
+                uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
+                next_end += 6
+            char = unichr(uni)
+            end = next_end
+        # Append the unescaped character
+        _append(char)
+    return u''.join(chunks), end
+
+
+# Use speedup if available
+scanstring = c_scanstring or py_scanstring
+
+WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
+WHITESPACE_STR = ' \t\n\r'
+
+def JSONObject((s, end), encoding, strict, scan_once, object_hook,
+        object_pairs_hook, memo=None,
+        _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+    # Backwards compatibility
+    if memo is None:
+        memo = {}
+    memo_get = memo.setdefault
+    pairs = []
+    # Use a slice to prevent IndexError from being raised, the following
+    # check will raise a more specific ValueError if the string is empty
+    nextchar = s[end:end + 1]
+    # Normally we expect nextchar == '"'
+    if nextchar != '"':
+        if nextchar in _ws:
+            end = _w(s, end).end()
+            nextchar = s[end:end + 1]
+        # Trivial empty object
+        if nextchar == '}':
+            if object_pairs_hook is not None:
+                result = object_pairs_hook(pairs)
+                return result, end
+            pairs = {}
+            if object_hook is not None:
+                pairs = object_hook(pairs)
+            return pairs, end + 1
+        elif nextchar != '"':
+            raise JSONDecodeError("Expecting property name", s, end)
+    end += 1
+    while True:
+        key, end = scanstring(s, end, encoding, strict)
+        key = memo_get(key, key)
+
+        # To skip some function call overhead we optimize the fast paths where
+        # the JSON key separator is ": " or just ":".
+        if s[end:end + 1] != ':':
+            end = _w(s, end).end()
+            if s[end:end + 1] != ':':
+                raise JSONDecodeError("Expecting : delimiter", s, end)
+
+        end += 1
+
+        try:
+            if s[end] in _ws:
+                end += 1
+                if s[end] in _ws:
+                    end = _w(s, end + 1).end()
+        except IndexError:
+            pass
+
+        try:
+            value, end = scan_once(s, end)
+        except StopIteration:
+            raise JSONDecodeError("Expecting object", s, end)
+        pairs.append((key, value))
+
+        try:
+            nextchar = s[end]
+            if nextchar in _ws:
+                end = _w(s, end + 1).end()
+                nextchar = s[end]
+        except IndexError:
+            nextchar = ''
+        end += 1
+
+        if nextchar == '}':
+            break
+        elif nextchar != ',':
+            raise JSONDecodeError("Expecting , delimiter", s, end - 1)
+
+        try:
+            nextchar = s[end]
+            if nextchar in _ws:
+                end += 1
+                nextchar = s[end]
+                if nextchar in _ws:
+                    end = _w(s, end + 1).end()
+                    nextchar = s[end]
+        except IndexError:
+            nextchar = ''
+
+        end += 1
+        if nextchar != '"':
+            raise JSONDecodeError("Expecting property name", s, end - 1)
+
+    if object_pairs_hook is not None:
+        result = object_pairs_hook(pairs)
+        return result, end
+    pairs = dict(pairs)
+    if object_hook is not None:
+        pairs = object_hook(pairs)
+    return pairs, end
+
+def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+    values = []
+    nextchar = s[end:end + 1]
+    if nextchar in _ws:
+        end = _w(s, end + 1).end()
+        nextchar = s[end:end + 1]
+    # Look-ahead for trivial empty array
+    if nextchar == ']':
+        return values, end + 1
+    _append = values.append
+    while True:
+        try:
+            value, end = scan_once(s, end)
+        except StopIteration:
+            raise JSONDecodeError("Expecting object", s, end)
+        _append(value)
+        nextchar = s[end:end + 1]
+        if nextchar in _ws:
+            end = _w(s, end + 1).end()
+            nextchar = s[end:end + 1]
+        end += 1
+        if nextchar == ']':
+            break
+        elif nextchar != ',':
+            raise JSONDecodeError("Expecting , delimiter", s, end)
+
+        try:
+            if s[end] in _ws:
+                end += 1
+                if s[end] in _ws:
+                    end = _w(s, end + 1).end()
+        except IndexError:
+            pass
+
+    return values, end
+
+class JSONDecoder(object):
+    """Simple JSON <http://json.org> decoder
+
+    Performs the following translations in decoding by default:
+
+    +---------------+-------------------+
+    | JSON          | Python            |
+    +===============+===================+
+    | object        | dict              |
+    +---------------+-------------------+
+    | array         | list              |
+    +---------------+-------------------+
+    | string        | unicode           |
+    +---------------+-------------------+
+    | number (int)  | int, long         |
+    +---------------+-------------------+
+    | number (real) | float             |
+    +---------------+-------------------+
+    | true          | True              |
+    +---------------+-------------------+
+    | false         | False             |
+    +---------------+-------------------+
+    | null          | None              |
+    +---------------+-------------------+
+
+    It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
+    their corresponding ``float`` values, which is outside the JSON spec.
+
+    """
+
+    def __init__(self, encoding=None, object_hook=None, parse_float=None,
+            parse_int=None, parse_constant=None, strict=True,
+            object_pairs_hook=None):
+        """
+        *encoding* determines the encoding used to interpret any
+        :class:`str` objects decoded by this instance (``'utf-8'`` by
+        default).  It has no effect when decoding :class:`unicode` objects.
+
+        Note that currently only encodings that are a superset of ASCII work,
+        strings of other encodings should be passed in as :class:`unicode`.
+
+        *object_hook*, if specified, will be called with the result of every
+        JSON object decoded and its return value will be used in place of the
+        given :class:`dict`.  This can be used to provide custom
+        deserializations (e.g. to support JSON-RPC class hinting).
+
+        *object_pairs_hook* is an optional function that will be called with
+        the result of any object literal decode with an ordered list of pairs.
+        The return value of *object_pairs_hook* will be used instead of the
+        :class:`dict`.  This feature can be used to implement custom decoders
+        that rely on the order that the key and value pairs are decoded (for
+        example, :func:`collections.OrderedDict` will remember the order of
+        insertion). If *object_hook* is also defined, the *object_pairs_hook*
+        takes priority.
+
+        *parse_float*, if specified, will be called with the string of every
+        JSON float to be decoded.  By default, this is equivalent to
+        ``float(num_str)``. This can be used to use another datatype or parser
+        for JSON floats (e.g. :class:`decimal.Decimal`).
+
+        *parse_int*, if specified, will be called with the string of every
+        JSON int to be decoded.  By default, this is equivalent to
+        ``int(num_str)``.  This can be used to use another datatype or parser
+        for JSON integers (e.g. :class:`float`).
+
+        *parse_constant*, if specified, will be called with one of the
+        following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``.  This
+        can be used to raise an exception if invalid JSON numbers are
+        encountered.
+
+        *strict* controls the parser's behavior when it encounters an
+        invalid control character in a string. The default setting of
+        ``True`` means that unescaped control characters are parse errors, if
+        ``False`` then control characters will be allowed in strings.
+
+        """
+        self.encoding = encoding
+        self.object_hook = object_hook
+        self.object_pairs_hook = object_pairs_hook
+        self.parse_float = parse_float or float
+        self.parse_int = parse_int or int
+        self.parse_constant = parse_constant or _CONSTANTS.__getitem__
+        self.strict = strict
+        self.parse_object = JSONObject
+        self.parse_array = JSONArray
+        self.parse_string = scanstring
+        self.memo = {}
+        self.scan_once = make_scanner(self)
+
+    def decode(self, s, _w=WHITESPACE.match):
+        """Return the Python representation of ``s`` (a ``str`` or ``unicode``
+        instance containing a JSON document)
+
+        """
+        obj, end = self.raw_decode(s, idx=_w(s, 0).end())
+        end = _w(s, end).end()
+        if end != len(s):
+            raise JSONDecodeError("Extra data", s, end, len(s))
+        return obj
+
+    def raw_decode(self, s, idx=0):
+        """Decode a JSON document from ``s`` (a ``str`` or ``unicode``
+        beginning with a JSON document) and return a 2-tuple of the Python
+        representation and the index in ``s`` where the document ended.
+
+        This can be used to decode a JSON document from a string that may
+        have extraneous data at the end.
+
+        """
+        try:
+            obj, end = self.scan_once(s, idx)
+        except StopIteration:
+            raise JSONDecodeError("No JSON object could be decoded", s, idx)
+        return obj, end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/simplejson/encoder.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,501 @@
+"""Implementation of JSONEncoder
+"""
+import re
+from decimal import Decimal
+
+def _import_speedups():
+    try:
+        from simplejson import _speedups
+        return _speedups.encode_basestring_ascii, _speedups.make_encoder
+    except ImportError:
+        return None, None
+c_encode_basestring_ascii, c_make_encoder = _import_speedups()
+
+from simplejson.decoder import PosInf
+
+ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
+ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
+HAS_UTF8 = re.compile(r'[\x80-\xff]')
+ESCAPE_DCT = {
+    '\\': '\\\\',
+    '"': '\\"',
+    '\b': '\\b',
+    '\f': '\\f',
+    '\n': '\\n',
+    '\r': '\\r',
+    '\t': '\\t',
+}
+for i in range(0x20):
+    #ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
+    ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
+
+FLOAT_REPR = repr
+
+def encode_basestring(s):
+    """Return a JSON representation of a Python string
+
+    """
+    if isinstance(s, str) and HAS_UTF8.search(s) is not None:
+        s = s.decode('utf-8')
+    def replace(match):
+        return ESCAPE_DCT[match.group(0)]
+    return u'"' + ESCAPE.sub(replace, s) + u'"'
+
+
+def py_encode_basestring_ascii(s):
+    """Return an ASCII-only JSON representation of a Python string
+
+    """
+    if isinstance(s, str) and HAS_UTF8.search(s) is not None:
+        s = s.decode('utf-8')
+    def replace(match):
+        s = match.group(0)
+        try:
+            return ESCAPE_DCT[s]
+        except KeyError:
+            n = ord(s)
+            if n < 0x10000:
+                #return '\\u{0:04x}'.format(n)
+                return '\\u%04x' % (n,)
+            else:
+                # surrogate pair
+                n -= 0x10000
+                s1 = 0xd800 | ((n >> 10) & 0x3ff)
+                s2 = 0xdc00 | (n & 0x3ff)
+                #return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
+                return '\\u%04x\\u%04x' % (s1, s2)
+    return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
+
+
+encode_basestring_ascii = (
+    c_encode_basestring_ascii or py_encode_basestring_ascii)
+
+class JSONEncoder(object):
+    """Extensible JSON <http://json.org> encoder for Python data structures.
+
+    Supports the following objects and types by default:
+
+    +-------------------+---------------+
+    | Python            | JSON          |
+    +===================+===============+
+    | dict              | object        |
+    +-------------------+---------------+
+    | list, tuple       | array         |
+    +-------------------+---------------+
+    | str, unicode      | string        |
+    +-------------------+---------------+
+    | int, long, float  | number        |
+    +-------------------+---------------+
+    | True              | true          |
+    +-------------------+---------------+
+    | False             | false         |
+    +-------------------+---------------+
+    | None              | null          |
+    +-------------------+---------------+
+
+    To extend this to recognize other objects, subclass and implement a
+    ``.default()`` method with another method that returns a serializable
+    object for ``o`` if possible, otherwise it should call the superclass
+    implementation (to raise ``TypeError``).
+
+    """
+    item_separator = ', '
+    key_separator = ': '
+    def __init__(self, skipkeys=False, ensure_ascii=True,
+            check_circular=True, allow_nan=True, sort_keys=False,
+            indent=None, separators=None, encoding='utf-8', default=None,
+            use_decimal=False):
+        """Constructor for JSONEncoder, with sensible defaults.
+
+        If skipkeys is false, then it is a TypeError to attempt
+        encoding of keys that are not str, int, long, float or None.  If
+        skipkeys is True, such items are simply skipped.
+
+        If ensure_ascii is true, the output is guaranteed to be str
+        objects with all incoming unicode characters escaped.  If
+        ensure_ascii is false, the output will be unicode object.
+
+        If check_circular is true, then lists, dicts, and custom encoded
+        objects will be checked for circular references during encoding to
+        prevent an infinite recursion (which would cause an OverflowError).
+        Otherwise, no such check takes place.
+
+        If allow_nan is true, then NaN, Infinity, and -Infinity will be
+        encoded as such.  This behavior is not JSON specification compliant,
+        but is consistent with most JavaScript based encoders and decoders.
+        Otherwise, it will be a ValueError to encode such floats.
+
+        If sort_keys is true, then the output of dictionaries will be
+        sorted by key; this is useful for regression tests to ensure
+        that JSON serializations can be compared on a day-to-day basis.
+
+        If indent is a string, then JSON array elements and object members
+        will be pretty-printed with a newline followed by that string repeated
+        for each level of nesting. ``None`` (the default) selects the most compact
+        representation without any newlines. For backwards compatibility with
+        versions of simplejson earlier than 2.1.0, an integer is also accepted
+        and is converted to a string with that many spaces.
+
+        If specified, separators should be a (item_separator, key_separator)
+        tuple.  The default is (', ', ': ').  To get the most compact JSON
+        representation you should specify (',', ':') to eliminate whitespace.
+
+        If specified, default is a function that gets called for objects
+        that can't otherwise be serialized.  It should return a JSON encodable
+        version of the object or raise a ``TypeError``.
+
+        If encoding is not None, then all input strings will be
+        transformed into unicode using that encoding prior to JSON-encoding.
+        The default is UTF-8.
+        
+        If use_decimal is true (not the default), ``decimal.Decimal`` will
+        be supported directly by the encoder. For the inverse, decode JSON
+        with ``parse_float=decimal.Decimal``.
+
+        """
+
+        self.skipkeys = skipkeys
+        self.ensure_ascii = ensure_ascii
+        self.check_circular = check_circular
+        self.allow_nan = allow_nan
+        self.sort_keys = sort_keys
+        self.use_decimal = use_decimal
+        if isinstance(indent, (int, long)):
+            indent = ' ' * indent
+        self.indent = indent
+        if separators is not None:
+            self.item_separator, self.key_separator = separators
+        if default is not None:
+            self.default = default
+        self.encoding = encoding
+
+    def default(self, o):
+        """Implement this method in a subclass such that it returns
+        a serializable object for ``o``, or calls the base implementation
+        (to raise a ``TypeError``).
+
+        For example, to support arbitrary iterators, you could
+        implement default like this::
+
+            def default(self, o):
+                try:
+                    iterable = iter(o)
+                except TypeError:
+                    pass
+                else:
+                    return list(iterable)
+                return JSONEncoder.default(self, o)
+
+        """
+        raise TypeError(repr(o) + " is not JSON serializable")
+
+    def encode(self, o):
+        """Return a JSON string representation of a Python data structure.
+
+        >>> from simplejson import JSONEncoder
+        >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
+        '{"foo": ["bar", "baz"]}'
+
+        """
+        # This is for extremely simple cases and benchmarks.
+        if isinstance(o, basestring):
+            if isinstance(o, str):
+                _encoding = self.encoding
+                if (_encoding is not None
+                        and not (_encoding == 'utf-8')):
+                    o = o.decode(_encoding)
+            if self.ensure_ascii:
+                return encode_basestring_ascii(o)
+            else:
+                return encode_basestring(o)
+        # This doesn't pass the iterator directly to ''.join() because the
+        # exceptions aren't as detailed.  The list call should be roughly
+        # equivalent to the PySequence_Fast that ''.join() would do.
+        chunks = self.iterencode(o, _one_shot=True)
+        if not isinstance(chunks, (list, tuple)):
+            chunks = list(chunks)
+        if self.ensure_ascii:
+            return ''.join(chunks)
+        else:
+            return u''.join(chunks)
+
+    def iterencode(self, o, _one_shot=False):
+        """Encode the given object and yield each string
+        representation as available.
+
+        For example::
+
+            for chunk in JSONEncoder().iterencode(bigobject):
+                mysocket.write(chunk)
+
+        """
+        if self.check_circular:
+            markers = {}
+        else:
+            markers = None
+        if self.ensure_ascii:
+            _encoder = encode_basestring_ascii
+        else:
+            _encoder = encode_basestring
+        if self.encoding != 'utf-8':
+            def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
+                if isinstance(o, str):
+                    o = o.decode(_encoding)
+                return _orig_encoder(o)
+
+        def floatstr(o, allow_nan=self.allow_nan,
+                _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf):
+            # Check for specials. Note that this type of test is processor
+            # and/or platform-specific, so do tests which don't depend on
+            # the internals.
+
+            if o != o:
+                text = 'NaN'
+            elif o == _inf:
+                text = 'Infinity'
+            elif o == _neginf:
+                text = '-Infinity'
+            else:
+                return _repr(o)
+
+            if not allow_nan:
+                raise ValueError(
+                    "Out of range float values are not JSON compliant: " +
+                    repr(o))
+
+            return text
+
+
+        key_memo = {}
+        if (_one_shot and c_make_encoder is not None
+                and not self.indent and not self.sort_keys):
+            _iterencode = c_make_encoder(
+                markers, self.default, _encoder, self.indent,
+                self.key_separator, self.item_separator, self.sort_keys,
+                self.skipkeys, self.allow_nan, key_memo, self.use_decimal)
+        else:
+            _iterencode = _make_iterencode(
+                markers, self.default, _encoder, self.indent, floatstr,
+                self.key_separator, self.item_separator, self.sort_keys,
+                self.skipkeys, _one_shot, self.use_decimal)
+        try:
+            return _iterencode(o, 0)
+        finally:
+            key_memo.clear()
+
+
+class JSONEncoderForHTML(JSONEncoder):
+    """An encoder that produces JSON safe to embed in HTML.
+
+    To embed JSON content in, say, a script tag on a web page, the
+    characters &, < and > should be escaped. They cannot be escaped
+    with the usual entities (e.g. &amp;) because they are not expanded
+    within <script> tags.
+    """
+
+    def encode(self, o):
+        # Override JSONEncoder.encode because it has hacks for
+        # performance that make things more complicated.
+        chunks = self.iterencode(o, True)
+        if self.ensure_ascii:
+            return ''.join(chunks)
+        else:
+            return u''.join(chunks)
+
+    def iterencode(self, o, _one_shot=False):
+        chunks = super(JSONEncoderForHTML, self).iterencode(o, _one_shot)
+        for chunk in chunks:
+            chunk = chunk.replace('&', '\\u0026')
+            chunk = chunk.replace('<', '\\u003c')
+            chunk = chunk.replace('>', '\\u003e')
+            yield chunk
+
+
+def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
+        _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
+        _use_decimal,
+        ## HACK: hand-optimized bytecode; turn globals into locals
+        False=False,
+        True=True,
+        ValueError=ValueError,
+        basestring=basestring,
+        Decimal=Decimal,
+        dict=dict,
+        float=float,
+        id=id,
+        int=int,
+        isinstance=isinstance,
+        list=list,
+        long=long,
+        str=str,
+        tuple=tuple,
+    ):
+
+    def _iterencode_list(lst, _current_indent_level):
+        if not lst:
+            yield '[]'
+            return
+        if markers is not None:
+            markerid = id(lst)
+            if markerid in markers:
+                raise ValueError("Circular reference detected")
+            markers[markerid] = lst
+        buf = '['
+        if _indent is not None:
+            _current_indent_level += 1
+            newline_indent = '\n' + (_indent * _current_indent_level)
+            separator = _item_separator + newline_indent
+            buf += newline_indent
+        else:
+            newline_indent = None
+            separator = _item_separator
+        first = True
+        for value in lst:
+            if first:
+                first = False
+            else:
+                buf = separator
+            if isinstance(value, basestring):
+                yield buf + _encoder(value)
+            elif value is None:
+                yield buf + 'null'
+            elif value is True:
+                yield buf + 'true'
+            elif value is False:
+                yield buf + 'false'
+            elif isinstance(value, (int, long)):
+                yield buf + str(value)
+            elif isinstance(value, float):
+                yield buf + _floatstr(value)
+            elif _use_decimal and isinstance(value, Decimal):
+                yield buf + str(value)
+            else:
+                yield buf
+                if isinstance(value, (list, tuple)):
+                    chunks = _iterencode_list(value, _current_indent_level)
+                elif isinstance(value, dict):
+                    chunks = _iterencode_dict(value, _current_indent_level)
+                else:
+                    chunks = _iterencode(value, _current_indent_level)
+                for chunk in chunks:
+                    yield chunk
+        if newline_indent is not None:
+            _current_indent_level -= 1
+            yield '\n' + (_indent * _current_indent_level)
+        yield ']'
+        if markers is not None:
+            del markers[markerid]
+
+    def _iterencode_dict(dct, _current_indent_level):
+        if not dct:
+            yield '{}'
+            return
+        if markers is not None:
+            markerid = id(dct)
+            if markerid in markers:
+                raise ValueError("Circular reference detected")
+            markers[markerid] = dct
+        yield '{'
+        if _indent is not None:
+            _current_indent_level += 1
+            newline_indent = '\n' + (_indent * _current_indent_level)
+            item_separator = _item_separator + newline_indent
+            yield newline_indent
+        else:
+            newline_indent = None
+            item_separator = _item_separator
+        first = True
+        if _sort_keys:
+            items = dct.items()
+            items.sort(key=lambda kv: kv[0])
+        else:
+            items = dct.iteritems()
+        for key, value in items:
+            if isinstance(key, basestring):
+                pass
+            # JavaScript is weakly typed for these, so it makes sense to
+            # also allow them.  Many encoders seem to do something like this.
+            elif isinstance(key, float):
+                key = _floatstr(key)
+            elif key is True:
+                key = 'true'
+            elif key is False:
+                key = 'false'
+            elif key is None:
+                key = 'null'
+            elif isinstance(key, (int, long)):
+                key = str(key)
+            elif _skipkeys:
+                continue
+            else:
+                raise TypeError("key " + repr(key) + " is not a string")
+            if first:
+                first = False
+            else:
+                yield item_separator
+            yield _encoder(key)
+            yield _key_separator
+            if isinstance(value, basestring):
+                yield _encoder(value)
+            elif value is None:
+                yield 'null'
+            elif value is True:
+                yield 'true'
+            elif value is False:
+                yield 'false'
+            elif isinstance(value, (int, long)):
+                yield str(value)
+            elif isinstance(value, float):
+                yield _floatstr(value)
+            elif _use_decimal and isinstance(value, Decimal):
+                yield str(value)
+            else:
+                if isinstance(value, (list, tuple)):
+                    chunks = _iterencode_list(value, _current_indent_level)
+                elif isinstance(value, dict):
+                    chunks = _iterencode_dict(value, _current_indent_level)
+                else:
+                    chunks = _iterencode(value, _current_indent_level)
+                for chunk in chunks:
+                    yield chunk
+        if newline_indent is not None:
+            _current_indent_level -= 1
+            yield '\n' + (_indent * _current_indent_level)
+        yield '}'
+        if markers is not None:
+            del markers[markerid]
+
+    def _iterencode(o, _current_indent_level):
+        if isinstance(o, basestring):
+            yield _encoder(o)
+        elif o is None:
+            yield 'null'
+        elif o is True:
+            yield 'true'
+        elif o is False:
+            yield 'false'
+        elif isinstance(o, (int, long)):
+            yield str(o)
+        elif isinstance(o, float):
+            yield _floatstr(o)
+        elif isinstance(o, (list, tuple)):
+            for chunk in _iterencode_list(o, _current_indent_level):
+                yield chunk
+        elif isinstance(o, dict):
+            for chunk in _iterencode_dict(o, _current_indent_level):
+                yield chunk
+        elif _use_decimal and isinstance(o, Decimal):
+            yield str(o)
+        else:
+            if markers is not None:
+                markerid = id(o)
+                if markerid in markers:
+                    raise ValueError("Circular reference detected")
+                markers[markerid] = o
+            o = _default(o)
+            for chunk in _iterencode(o, _current_indent_level):
+                yield chunk
+            if markers is not None:
+                del markers[markerid]
+
+    return _iterencode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/simplejson/ordered_dict.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,119 @@
+"""Drop-in replacement for collections.OrderedDict by Raymond Hettinger
+
+http://code.activestate.com/recipes/576693/
+
+"""
+from UserDict import DictMixin
+
+# Modified from original to support Python 2.4, see
+# http://code.google.com/p/simplejson/issues/detail?id=53
+try:
+    all
+except NameError:
+    def all(seq):
+        for elem in seq:
+            if not elem:
+                return False
+        return True
+
+class OrderedDict(dict, DictMixin):
+
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        try:
+            self.__end
+        except AttributeError:
+            self.clear()
+        self.update(*args, **kwds)
+
+    def clear(self):
+        self.__end = end = []
+        end += [None, end, end]         # sentinel node for doubly linked list
+        self.__map = {}                 # key --> [key, prev, next]
+        dict.clear(self)
+
+    def __setitem__(self, key, value):
+        if key not in self:
+            end = self.__end
+            curr = end[1]
+            curr[2] = end[1] = self.__map[key] = [key, curr, end]
+        dict.__setitem__(self, key, value)
+
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        key, prev, next = self.__map.pop(key)
+        prev[2] = next
+        next[1] = prev
+
+    def __iter__(self):
+        end = self.__end
+        curr = end[2]
+        while curr is not end:
+            yield curr[0]
+            curr = curr[2]
+
+    def __reversed__(self):
+        end = self.__end
+        curr = end[1]
+        while curr is not end:
+            yield curr[0]
+            curr = curr[1]
+
+    def popitem(self, last=True):
+        if not self:
+            raise KeyError('dictionary is empty')
+        # Modified from original to support Python 2.4, see
+        # http://code.google.com/p/simplejson/issues/detail?id=53
+        if last:
+            key = reversed(self).next()
+        else:
+            key = iter(self).next()
+        value = self.pop(key)
+        return key, value
+
+    def __reduce__(self):
+        items = [[k, self[k]] for k in self]
+        tmp = self.__map, self.__end
+        del self.__map, self.__end
+        inst_dict = vars(self).copy()
+        self.__map, self.__end = tmp
+        if inst_dict:
+            return (self.__class__, (items,), inst_dict)
+        return self.__class__, (items,)
+
+    def keys(self):
+        return list(self)
+
+    setdefault = DictMixin.setdefault
+    update = DictMixin.update
+    pop = DictMixin.pop
+    values = DictMixin.values
+    items = DictMixin.items
+    iterkeys = DictMixin.iterkeys
+    itervalues = DictMixin.itervalues
+    iteritems = DictMixin.iteritems
+
+    def __repr__(self):
+        if not self:
+            return '%s()' % (self.__class__.__name__,)
+        return '%s(%r)' % (self.__class__.__name__, self.items())
+
+    def copy(self):
+        return self.__class__(self)
+
+    @classmethod
+    def fromkeys(cls, iterable, value=None):
+        d = cls()
+        for key in iterable:
+            d[key] = value
+        return d
+
+    def __eq__(self, other):
+        if isinstance(other, OrderedDict):
+            return len(self)==len(other) and \
+                   all(p==q for p, q in  zip(self.items(), other.items()))
+        return dict.__eq__(self, other)
+
+    def __ne__(self, other):
+        return not self == other
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/simplejson/scanner.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,77 @@
+"""JSON token scanner
+"""
+import re
+def _import_c_make_scanner():
+    try:
+        from simplejson._speedups import make_scanner
+        return make_scanner
+    except ImportError:
+        return None
+c_make_scanner = _import_c_make_scanner()
+
+__all__ = ['make_scanner']
+
+NUMBER_RE = re.compile(
+    r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
+    (re.VERBOSE | re.MULTILINE | re.DOTALL))
+
+def py_make_scanner(context):
+    parse_object = context.parse_object
+    parse_array = context.parse_array
+    parse_string = context.parse_string
+    match_number = NUMBER_RE.match
+    encoding = context.encoding
+    strict = context.strict
+    parse_float = context.parse_float
+    parse_int = context.parse_int
+    parse_constant = context.parse_constant
+    object_hook = context.object_hook
+    object_pairs_hook = context.object_pairs_hook
+    memo = context.memo
+
+    def _scan_once(string, idx):
+        try:
+            nextchar = string[idx]
+        except IndexError:
+            raise StopIteration
+
+        if nextchar == '"':
+            return parse_string(string, idx + 1, encoding, strict)
+        elif nextchar == '{':
+            return parse_object((string, idx + 1), encoding, strict,
+                _scan_once, object_hook, object_pairs_hook, memo)
+        elif nextchar == '[':
+            return parse_array((string, idx + 1), _scan_once)
+        elif nextchar == 'n' and string[idx:idx + 4] == 'null':
+            return None, idx + 4
+        elif nextchar == 't' and string[idx:idx + 4] == 'true':
+            return True, idx + 4
+        elif nextchar == 'f' and string[idx:idx + 5] == 'false':
+            return False, idx + 5
+
+        m = match_number(string, idx)
+        if m is not None:
+            integer, frac, exp = m.groups()
+            if frac or exp:
+                res = parse_float(integer + (frac or '') + (exp or ''))
+            else:
+                res = parse_int(integer)
+            return res, m.end()
+        elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
+            return parse_constant('NaN'), idx + 3
+        elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
+            return parse_constant('Infinity'), idx + 8
+        elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
+            return parse_constant('-Infinity'), idx + 9
+        else:
+            raise StopIteration
+
+    def scan_once(string, idx):
+        try:
+            return _scan_once(string, idx)
+        finally:
+            memo.clear()
+
+    return scan_once
+
+make_scanner = c_make_scanner or py_make_scanner
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/simplejson/simplejson/tool.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,39 @@
+r"""Command-line tool to validate and pretty-print JSON
+
+Usage::
+
+    $ echo '{"json":"obj"}' | python -m simplejson.tool
+    {
+        "json": "obj"
+    }
+    $ echo '{ 1.2:3.4}' | python -m simplejson.tool
+    Expecting property name: line 1 column 2 (char 2)
+
+"""
+import sys
+import simplejson as json
+
+def main():
+    if len(sys.argv) == 1:
+        infile = sys.stdin
+        outfile = sys.stdout
+    elif len(sys.argv) == 2:
+        infile = open(sys.argv[1], 'rb')
+        outfile = sys.stdout
+    elif len(sys.argv) == 3:
+        infile = open(sys.argv[1], 'rb')
+        outfile = open(sys.argv[2], 'wb')
+    else:
+        raise SystemExit(sys.argv[0] + " [infile [outfile]]")
+    try:
+        obj = json.load(infile,
+                        object_pairs_hook=json.OrderedDict,
+                        use_decimal=True)
+    except ValueError, e:
+        raise SystemExit(e)
+    json.dump(obj, outfile, sort_keys=True, indent='    ', use_decimal=True)
+    outfile.write('\n')
+
+
+if __name__ == '__main__':
+    main()
--- a/bundled/webpy/.gitignore	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-*.pyc
-.DS_Store
\ No newline at end of file
--- a/bundled/webpy/ChangeLog.txt	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,293 +0,0 @@
-# web.py changelog
-
-## 2009-06-04 0.32
-
-* optional from_address to web.emailerrors
-* upgrade wsgiserver to CherryPy/3.1.2
-* support for extensions in Jinja2 templates (tx Zhang Huangbin)
-* support web.datestr for datetime.date objects also 
-* support for lists in db queries
-* new: uniq and iterview
-* fix: set debug=False when application is run with mod_wsgi (tx Patrick Swieskowski) [Bug#370904](https://bugs.launchpad.net/webpy/+bug/370904)
-* fix: make web.commify work  with decimals [Bug#317204](https://bugs.launchpad.net/webpy/+bug/317204)
-* fix: unicode issues with sqlite database [Bug#373219](https://bugs.launchpad.net/webpy/+bug/373219)
-* fix: urlquote url when the server is lighttpd [Bug#339858](https://bugs.launchpad.net/webpy/+bug/339858)
-* fix: issue with using date.format in templates
-* fix: use TOP instead of LIMIT in mssql database [Bug#324049](https://bugs.launchpad.net/webpy/+bug/324049)
-* fix: make sessions work well with expirations
-* fix: accept both list and tuple as arg values in form.Dropdown [Bug#314970](https://bugs.launchpad.net/webpy/+bug/314970)
-* fix: match parenthesis when parsing `for` statement in templates
-* fix: fix python 2.3 compatibility 
-* fix: ignore dot folders when compiling templates (tx Stuart Langridge) 
-* fix: don't consume KeyboardInterrupt and SystemExit errors 
-* fix: make application work well with iterators 
-
-## 2008-12-10: 0.31
-
-* new: browser module
-* new: test utilities
-* new: ShelfStore
-* fix: web.cookies error when default is None
-* fix: paramstyle for OracleDB (tx kromakey)
-* fix: performance issue in SQLQuery.join
-* fix: use wsgi.url_scheme to find ctx.protocol
-
-## 2008-12-06: 0.3
-
-* new: replace print with return (<i>backward-incompatible</i>)
-* new: application framework (<i>backward-incompatible</i>)
-* new: modular database system (<i>backward-incompatible</i>)
-* new: templetor reimplementation
-* new: better unicode support
-* new: debug mode (web.config.debug)
-* new: better db pooling
-* new: sessions
-* new: support for GAE
-* new: etag support
-* new: web.openid module
-* new: web.nthstr
-* fix: various form.py fixes
-* fix: python 2.6 compatibility
-* fix: file uploads are not loaded into memory
-* fix: SQLLiteral issue (Bug#180027)
-* change: web.background is moved to experimental (<i>backward-incompatible</i>) 
-* improved API doc generation (tx Colin Rothwell)
-
-## 2008-01-19: 0.23
-
-* fix: for web.background gotcha ([133079](http://bugs.launchpad.net/webpy/+bug/133079))
-* fix: for postgres unicode bug ([177265](http://bugs.launchpad.net/webpy/+bug/177265))
-* fix: web.profile behavior in python 2.5 ([133080](http://bugs.launchpad.net/webpy/+bug/133080))
-* fix: only uppercase HTTP methods are allowed. ([176415](http://bugs.launchpad.net/webpy/+bug/176415))
-* fix: transaction error in with statement ([125118](http://bugs.launchpad.net/webpy/+bug/125118))
-* fix: fix in web.reparam ([162085](http://bugs.launchpad.net/webpy/+bug/162085))
-* fix: various unicode issues ([137042](http://bugs.launchpad.net/webpy/+bug/137042), [180510](http://bugs.launchpad.net/webpy/+bug/180510), [180549](http://bugs.launchpad.net/webpy/+bug/180549), [180653](http://bugs.launchpad.net/webpy/+bug/180653))
-* new: support for https
-* new: support for secure cookies
-* new: sendmail
-* new: htmlunquote
-
-## 2007-08-23: 0.22
-
-* compatibility with new DBUtils API ([122112](https://bugs.launchpad.net/webpy/+bug/122112))
-* fix reloading ([118683](https://bugs.launchpad.net/webpy/+bug/118683))
-* fix compatibility between `changequery` and `redirect` ([118234](https://bugs.launchpad.net/webpy/+bug/118234))
-* fix relative URI in `web.redirect` ([118236](https://bugs.launchpad.net/webpy/+bug/118236))
-* fix `ctx._write` support in built-in HTTP server ([121908](https://bugs.launchpad.net/webpy/+bug/121908))
-* fix `numify` strips things after '.'s ([118644](https://bugs.launchpad.net/webpy/+bug/118644))
-* fix various unicode isssues ([114703](https://bugs.launchpad.net/webpy/+bug/114703), [120644](https://bugs.launchpad.net/webpy/+bug/120644), [124280](https://bugs.launchpad.net/webpy/+bug/124280))
-
-## 2007-05-28: 0.21
-
-* <strong>security fix:</strong> prevent bad characters in headers
-* support for cheetah template reloading                    
-* support for form validation                               
-* new `form.File`                                           
-* new `web.url`                                             
-* fix rendering issues with hidden and button inputs        
-* fix 2.3 incompatability with `numify`                     
-* fix multiple headers with same name                       
-* fix web.redirect issues when homepath is not /            
-* new CherryPy wsgi server                                  
-* new nested transactions                                   
-* new sqlliteral                                            
-
-## 2006-05-09: 0.138
-
-* New function: `intget`
-* New function: `datestr`
-* New function: `validaddr`
-* New function: `sqlwhere`
-* New function: `background`, `backgrounder`
-* New function: `changequery`
-* New function: `flush`
-* New function: `load`, `unload`
-* New variable: `loadhooks`, `unloadhooks`
-* Better docs; generating [docs](documentation) from web.py now
-* global variable `REAL_SCRIPT_NAME` can now be used to work around lighttpd madness
-* fastcgi/scgi servers now can listen on sockets
-* `output` now encodes Unicode
-* `input` now takes optional `_method` argument
-* <strong>Potentially-incompatible change:</strong> `input` now returns `badrequest` automatically when `requireds` aren't found
-* `storify` now takes lists and dictionaries as requests (see docs)
-* `redirect` now blanks any existing output
-* Quote SQL better when `db_printing` is on
-* Fix delay in `nomethod`
-* Fix `urlquote` to encode better.
-* Fix 2.3 incompatibility with `iters` (tx ??)
-* Fix duplicate headers
-* Improve `storify` docs
-* Fix `IterBetter` to raise IndexError, not KeyError
-
-## 2006-03-27: 0.137
-
-* Add function `dictfindall` (tx Steve Huffman)
-* Add support to `autodelegate` for arguments
-* Add functions `httpdate` and `parsehttpdate`
-* Add function `modified`
-* Add support for FastCGI server mode
-* Clarify `dictadd` documentation (tx Steve Huffman)
-* Changed license to public domain
-* Clean up to use `ctx` and `env` instead of `context` and `environ`
-* Improved support for PUT, DELETE, etc. (tx list)
-* Fix `ctx.fullpath` (tx Jesir Vargas)
-* Fix sqlite support (tx Dubhead)
-* Fix documentation bug in `lstrips` (tx Gregory Petrosyan)
-* Fix support for IPs and ports (1/2 tx Jesir Vargas)
-* Fix `ctx.fullpath` (tx Jesir Vargas)
-* Fix sqlite support (tx Dubhead)
-* Fix documentation bug in `lstrips` (tx Gregory Petrosyan)
-* Fix `iters` bug with sets
-* Fix some breakage introduced by Vargas's patch
-* Fix `sqlors` bug
-* Fix various small style things (tx Jesir Vargas)
-* Fix bug with `input` ignoring GET input
-
-## 2006-02-22: 0.136 (svn)
-
-* Major code cleanup (tx to Jesir Vargas for the patch).
-* 2006-02-15: 0.135
-* Really fix that mysql regression (tx Sean Leach).
-* 2006-02-15: 0.134
-* The `StopIteration` exception is now caught. This can be used by functions that do things like check to see if a user is logged in. If the user isn't, they can output a message with a login box and raise StopIteration, preventing the caller from executing.
-* Fix some documentation bugs.
-* Fix mysql regression (tx mrstone).
-
-## 2006-02-12: 0.133
-
-* Docstrings! (tx numerous, esp. Jonathan Mark (for the patch) and Guido van Rossum (for the prod))
-* Add `set` to web.iters.
-* Make the `len` returned by `query` an int (tx ??).
-* <strong>Backwards-incompatible change:</strong> `base` now called `prefixurl`.
-* <strong>Backwards-incompatible change:</strong> `autoassign` now takes `self` and `locals()` as arguments.
-
-## 2006-02-07: 0.132
-
-* New variable `iters` is now a listing of possible list-like types (currently list, tuple, and, if it exists, Set).
-* New function `dictreverse` turns `{1:2}` into `{2:1}`.
-* `Storage` now a dictionary subclass.
-* `tryall` now takes an optional prefix of functions to run.
-* `sqlors` has various improvements.
-* Fix a bunch of DB API bugs.
-* Fix bug with `storify` when it received multiple inputs (tx Ben Woosley).
-* Fix bug with returning a generator (tx Zbynek Winkler).
-* Fix bug where len returned a long on query results (tx F.S).
-
-
-## 2006-01-31: 0.131 (not officially released)
-
-* New function `_interpolate` used internally for interpolating strings.
-* Redone database API. `select`, `insert`, `update`, and `delete` all made consistent. Database queries can now do more complicated expressions like `$foo.bar` and `${a+b}`. You now have to explicitly pass the dictionary to look up variables in. Pass `vars=locals()` to get the old functionality of looking up variables .
-* New functions `sqllist` and `sqlors` generate certain kinds of SQL.
-
-## 2006-01-30: 0.13
-
-* New functions `found`, `seeother`, and `tempredirect` now let you do other kinds of redirects. `redirect` now also takes an optional status parameter. (tx many)
-* New functions `expires` and `lastmodified` make it easy to send those headers.
-* New function `gone` returns a 410 Gone (tx David Terrell).
-* New function `urlquote` applies url encoding to a string.
-* New function `iterbetter` wraps an iterator and allows you to do __getitem__s on it.
-* Have `query` return an `iterbetter` instead of an iterator.
-* Have `debugerror` show tracebacks with the innermost frame first.
-* Add `__hash__` function to `threadeddict` (and thus, `ctx`).
-* Add `context.host` value for the requested host name.
-* Add option `db_printing` that prints database queries and the time they take.
-* Add support for database pooling (tx Steve Huffman).
-* Add support for passing values to functions called by `handle`. If you do `('foo', 'value')` it will add `'value'` as an argument when it calls `foo`.
-* Add support for scgi (tx David Terrell for the patch).
-* Add support for web.py functions that are iterators (tx Brendan O'Connor for the patch).
-* Use new database cursors on each call instead of reusing one.
-* `setcookie` now takes an optional `domain` argument.
-* Fix bug in autoassign.
-* Fix bug where `debugerror` would break on objects it couldn't display.
-* Fix bug where you couldn't do `#include`s inline.
-* Fix bug with `reloader` and database calls.
-* Fix bug with `reloader` and base templates.
-* Fix bug with CGI mode on certain operating systems.
-* Fix bug where `debug` would crash if called outside a request.
-* Fix bug with `context.ip` giving weird values with proxies.
-
-## 2006-01-29: 0.129
-
-* Add Python 2.2 support.
-
-## 2006-01-28: 0.128
-
-* Fix typo in `web.profile`.
-
-## 2006-01-28: 0.127
-
-* Fix bug in error message if invalid dbn is sent (tx Panos Laganakos).
-
-## 2006-01-27: 0.126
-
-* Fix typos in Content-Type headers (tx Beat Bolli for the prod).
-
-## 2006-01-22: 0.125
-
-* Support Cheetah 2.0.
-
-## 2006-01-22: 0.124
-
-* Fix spacing bug (tx Tommi Raivio for the prod).
-
-## 2006-01-16: 0.123
-
-* Fix bug with CGI usage (tx Eddie Sowden for the prod).
-
-## 2006-01-14: 0.122
-
-* Allow DELETEs from `web.query` (tx Joost Molenaar for the prod).
-
-## 2006-01-08: 0.121
-
-* Allow import of submodules like `pkg.mod.cn` (tx Sridhar Ratna).
-* Fix a bug in `update` (tx Sergey Khenkin).
-
-## 2006-01-05: 0.12
-
-* <strong>Backwards-incompatible change:</strong> `db_parameters` is now a dictionary.
-* <strong>Backwards-incompatible change:</strong> `sumdicts` is now `dictadd`.
-* Add support for PyGreSQL, MySQL (tx Hallgrimur H. Gunnarsson).
-* Use HTML for non-Cheetah error message.
-* New function `htmlquote()`.
-* New function `tryall()`.
-* `ctx.output` can now be set to a generator. (tx Brendan O'Connor)
-
-## 2006-01-04: 0.117
-
-* Add support for psycopg 1.x. (tx Gregory Price)
-
-## 2006-01-04: 0.116
-
-* Add support for Python 2.3. (tx Evan Jones)
-
-## 2006-01-04: 0.115
-
-* Fix some bugs where database queries weren't reparameterized. Oops!
-* Fix a bug where `run()` wasn't getting the right functions.
-* Remove a debug statement accidentally left in.
-* Allow `storify` to be used on dictionaries. (tx Joseph Trent)
-
-## 2006-01-04: 0.114
-
-* Make `reloader` work on Windows. (tx manatlan)
-* Fix some small typos that affected colorization. (tx Gregory Price)
-
-## 2006-01-03: 0.113
-
-* Reorganize `run()` internals so mod_python can be used. (tx Nicholas Matsakis)
-
-## 2006-01-03: 0.112
-
-* Make `reloader` work when `code.py` is called with a full path. (tx David Terrell)
-
-## 2006-01-03: 0.111
-
-* Fixed bug in `strips()`. (tx Michael Josephson)
-
-## 2006-01-03: 0.11
-
-* First public version.
-
-
--- a/bundled/webpy/LICENSE.txt	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-web.py is in the public domain; it can be used for whatever purpose with absolutely no restrictions.
-
-CherryPy WSGI server that is included in the web.py as web.wsgiserver is licensed under CherryPy License. See web/wsgiserver/LICENSE.txt for more details.
-
--- a/bundled/webpy/experimental/background.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-"""Helpers functions to run log-running tasks."""
-from web import utils
-from web import webapi as web
-
-def background(func):
-    """A function decorator to run a long-running function as a background thread."""
-    def internal(*a, **kw):
-        web.data() # cache it
-
-        tmpctx = web._context[threading.currentThread()]
-        web._context[threading.currentThread()] = utils.storage(web.ctx.copy())
-
-        def newfunc():
-            web._context[threading.currentThread()] = tmpctx
-            func(*a, **kw)
-            myctx = web._context[threading.currentThread()]
-            for k in myctx.keys():
-                if k not in ['status', 'headers', 'output']:
-                    try: del myctx[k]
-                    except KeyError: pass
-        
-        t = threading.Thread(target=newfunc)
-        background.threaddb[id(t)] = t
-        t.start()
-        web.ctx.headers = []
-        return seeother(changequery(_t=id(t)))
-    return internal
-background.threaddb = {}
-
-def backgrounder(func):
-    def internal(*a, **kw):
-        i = web.input(_method='get')
-        if '_t' in i:
-            try:
-                t = background.threaddb[int(i._t)]
-            except KeyError:
-                return web.notfound()
-            web._context[threading.currentThread()] = web._context[t]
-            return
-        else:
-            return func(*a, **kw)
-    return internal
-
--- a/bundled/webpy/experimental/migration.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-"""Migration script to run web.py 0.23 programs using 0.3.
-
-Import this module at the beginning of your program.
-"""
-import web
-import sys
-
-def setup_database():
-    if web.config.get('db_parameters'):
-        db = web.database(**web.config.db_parameters)
-        web.insert = db.insert
-        web.select = db.select
-        web.update = db.update
-        web.delete = db.delete
-        web.query = db.query
-
-        def transact():
-            t = db.transaction()
-            web.ctx.setdefault('transaction_stack', []).append(t)
-
-        def rollback():
-            stack = web.ctx.get('transaction_stack')
-            t = stack and stack.pop()
-            t and t.rollback()
-
-        def commit():
-            stack = web.ctx.get('transaction_stack')
-            t = stack and stack.pop()
-            t and t.commit()
-
-        web.transact = transact
-        web.rollback = rollback
-        web.commit = commit
-        
-web.loadhooks = web.webapi.loadhooks = {}
-web._loadhooks = web.webapi._loadhooks = {}
-web.unloadhooks = web.webapi.unloadhooks = {}
-
-def load():
-    setup_database()
-
-web.load = load
-
-def run(urls, fvars, *middleware):
-    setup_database()
-
-    def stdout_processor(handler):
-        handler()
-        return web.ctx.get('output', '')
-
-    def hook_processor(handler):
-        for h in web.loadhooks.values() + web._loadhooks.values(): h()
-        output = handler()
-        for h in web.unloadhooks.values(): h()
-        return output
-
-    app = web.application(urls, fvars)
-    app.add_processor(stdout_processor)
-    app.add_processor(hook_processor)
-    app.run(*middleware)
-
-class _outputter:
-    """Wraps `sys.stdout` so that print statements go into the response."""
-    def __init__(self, file): self.file = file
-    def write(self, string_):
-        if hasattr(web.ctx, 'output'):
-            return output(string_)
-        else:
-            self.file.write(string_)
-    def __getattr__(self, attr): return getattr(self.file, attr)
-    def __getitem__(self, item): return self.file[item]
-
-def output(string_):
-    """Appends `string_` to the response."""
-    string_ = web.utf8(string_)
-    if web.ctx.get('flush'):
-        web.ctx._write(string_)
-    else:
-        web.ctx.output += str(string_)
-
-def _capturedstdout():
-    sysstd = sys.stdout
-    while hasattr(sysstd, 'file'):
-        if isinstance(sys.stdout, _outputter): return True
-        sysstd = sysstd.file
-    if isinstance(sys.stdout, _outputter): return True
-    return False
-
-if not _capturedstdout():
-    sys.stdout = _outputter(sys.stdout)
-
-web.run = run
-
-class Stowage(web.storage):
-    def __str__(self):
-        return self._str
-
-web.template.Stowage = web.template.stowage = Stowage
-
--- a/bundled/webpy/experimental/pwt.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-import web
-import simplejson, sudo
-urls = (
-    '/sudo', 'sudoku',
-    '/length', 'length',
-)
-
-
-class pwt(object):
-    _inFunc = False
-    updated = {}
-    page = """
-<script src="/static/prototype.js"></script>
-<script src="/static/behaviour.js"></script>
-<script>
-Behaviour.register({'input': function (e) { 
-    e.onmouseup = e.onkeyup = e.onchange = function () { send(e) }
-}})
-</script>
-
-<form name="main" onsubmit="return false;">%s</form>
-
-<script>
-function send(e) {
-    ajax =  new Ajax.Request(document.location, {method:'post', parameters: 
-      Form.serialize(document.forms.main)
-    });
-}
-
-function receive(d) {
-    $H(d).keys().each(function (key) {
-        v = d[key];
-        k = document.forms.main[key];
-
-        if (k) k.value = v;
-        else $(key).innerHTML = v;
-    })
-}
-</script>
-"""
-
-    def GET(self):
-        web.header('Content-Type', 'text/html')
-        print self.page % self.form()
-    
-    def POST(self):
-        i = web.input()
-        if '_' in i: del i['_']
-        #for k, v in i.iteritems(): setattr(self, k, v)
-        
-        self._inFunc = True
-        self.work(**i)
-        self._inFunc = False
-        
-        web.header('Content-Type', 'text/javascript')
-        print 'receive('+simplejson.dumps(self.updated)+');'
-    
-    def __setattr__(self, k, v):
-        if self._inFunc and k != '_inFunc':
-            self.updated[k] = v
-        object.__setattr__(self, k, v)
-
-class sudoku(pwt):
-    def form(self):
-        import sudo
-        out = ''
-        n = 0
-        for i in range(9):
-            for j in range(9):
-                out += '<input type="text" size="1" name="%s" />' % (sudo.squares[n])
-                n += 1
-            out += '<br />'
-
-        return out
-    
-    def work(self, **kw):
-        values = dict((s, sudo.digits) for s in sudo.squares)
-        for k, v in kw.iteritems():
-            if v:
-                sudo.assign(values, k, v)
-
-        for k, v in values.iteritems():
-            if len(v) == 1:
-                setattr(self, k, v)
-
-        return values
-
-class length(pwt):
-    def form(self):
-        return '<p id="output">&nbsp;</p><input type="range" name="n" value="0" />'
-    
-    def work(self):
-        self.output = ('a' * web.intget(self.n, 0) or '&nbsp;')
-
-if __name__ == "__main__":
-    web.run(urls, globals(), web.reloader)
\ No newline at end of file
--- a/bundled/webpy/experimental/untwisted.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-import random
-
-from twisted.internet import reactor, defer
-from twisted.web import http
-
-import simplejson
-
-import web
-
-class Request(http.Request):
-    def process(self):
-        self.content.seek(0, 0)
-        env = {
-          'REMOTE_ADDR': self.client.host,
-          'REQUEST_METHOD': self.method,
-          'PATH_INFO': self.path,
-          'CONTENT_LENGTH': web.intget(self.getHeader('content-length'), 0),
-          'wsgi.input': self.content
-        }
-        if '?' in self.uri:
-            env['QUERY_STRING'] = self.uri.split('?', 1)[1]
-
-        for k, v in self.received_headers.iteritems():
-            env['HTTP_' + k.upper()] = v
-        
-        if self.path.startswith('/static/'):
-            f = web.lstrips(self.path, '/static/')
-            assert '/' not in f
-            #@@@ big security hole
-            self.write(file('static/' + f).read())
-            return self.finish()
-
-        web.webapi._load(env)
-        web.ctx.trequest = self
-        result = self.actualfunc()
-        self.setResponseCode(int(web.ctx.status.split()[0]))
-        for (h, v) in web.ctx.headers:
-            self.setHeader(h, v)
-        self.write(web.ctx.output)
-        if not web.ctx.get('persist'):
-            self.finish()
-
-class Server(http.HTTPFactory):
-    def __init__(self, func):
-        self.func = func
-
-    def buildProtocol(self, addr):
-        """Generate a channel attached to this site.
-        """
-        channel = http.HTTPFactory.buildProtocol(self, addr)
-        class MyRequest(Request):
-            actualfunc = staticmethod(self.func)
-        channel.requestFactory = MyRequest
-        channel.site = self
-        return channel
-
-def runtwisted(func):
-    reactor.listenTCP(8086, Server(func))
-    reactor.run()
-
-def newrun(inp, fvars):
-    print "Running on http://0.0.0.0:8086/"
-    runtwisted(web.webpyfunc(inp, fvars, False))
-
-def iframe(url):
-    return """
-    <iframe height="0" width="0" style="display: none" src="%s"/></iframe>
-    """ % url #("http://%s.ajaxpush.lh.theinfo.org:8086%s" % (random.random(), url))
-
-class Feed:
-    def __init__(self):
-        self.sessions = []
-    
-    def subscribe(self):
-        request = web.ctx.trequest
-        self.sessions.append(request)
-        request.connectionLost = lambda reason: self.sessions.remove(request)
-        web.ctx.persist = True
-    
-    def publish(self, text):
-        for x in self.sessions:
-            x.write(text)
-
-class JSFeed(Feed):
-    def __init__(self, callback="callback"):
-        Feed.__init__(self)
-        self.callback = callback
-        
-    def publish(self, obj):
-        web.debug("publishing")
-        Feed.publish(self, 
-          '<script type="text/javascript">window.parent.%s(%s)</script>' % (self.callback, simplejson.dumps(obj) + 
-          " " * 2048))
-
-if __name__ == "__main__":
-    mfeed = JSFeed()
-
-    urls = (
-      '/', 'view',
-      '/js', 'js',
-      '/send', 'send'
-    )
-
-    class view:
-        def GET(self):
-            print """
-<script type="text/javascript">
-function callback(item) {
-  document.getElementById('content').innerHTML += "<p>" + item + "</p>";
-}
-</script>
-
-<h2>Today's News</h2>
-
-<div id="content"></div>
-
-<h2>Contribute</h2>
-<form method="post" action="/send">
-  <textarea name="text"></textarea>
-  <input type="submit" value="send" />
-</form>
-<iframe id="foo" height="0" width="0" style="display: none" src="/js"/></iframe>
-            """
-        
-    class js:
-        def GET(self):
-            mfeed.subscribe()
-    
-    class send:
-        def POST(self):
-            mfeed.publish('<p>%s</p>' % web.input().text + (" " * 2048))
-            web.seeother('/')
-    
-    newrun(urls, globals())
\ No newline at end of file
--- a/bundled/webpy/setup.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#!/usr/bin/env python
-
-# ...
-
-from distutils.core import setup
-
-setup(name='web.py',
-      version='0.32',
-      description='web.py: makes web apps',
-      author='Aaron Swartz',
-      author_email='me@aaronsw.com',
-      maintainer='Anand Chitipothu',
-      maintainer_email='anandology@gmail.com',
-      url=' http://webpy.org/',
-      packages=['web', 'web.wsgiserver', 'web.contrib'],
-      long_description="Think about the ideal way to write a web app. Write the code to make it happen.",
-      license="Public domain",
-      platforms=["any"],
-     )
--- a/bundled/webpy/test/README	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-# web.py unit tests
-
-## Setup
-
-All databases expect a database with name `webpy` with username `scott` and password `tiger`.
-
-## Running all tests
-
-To run all tests:
-
-    $ python test/alltests.py
-
-## Running individual tests
-
-To run all tests in a file:
-
-    $ python test/db.py
-
-To run all tests in a class:
-
-    $ python test/db.py SqliteTest
-
-To run a single test:
-
-    $ python test/db.py SqliteTest.testUnicode
-
-
--- a/bundled/webpy/test/alltests.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-import webtest
-
-def suite():
-    modules = ["doctests", "db", "application", "session"]
-    return webtest.suite(modules)
-    
-if __name__ == "__main__":
-    webtest.main()
--- a/bundled/webpy/test/application.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,297 +0,0 @@
-import webtest
-import time
-
-import web
-import urllib
-
-data = """
-import web
-
-urls = ("/", "%(classname)s")
-app = web.application(urls, globals(), autoreload=True)
-
-class %(classname)s:
-    def GET(self):
-        return "%(output)s"
-
-"""
-
-urls = (
-    "/iter", "do_iter",
-)
-app = web.application(urls, globals())
-
-class do_iter:
-    def GET(self):
-        yield 'hello, '
-        yield web.input(name='world').name
-
-    POST = GET
-
-def write(filename, data):
-    f = open(filename, 'w')
-    f.write(data)
-    f.close()
-
-class ApplicationTest(webtest.TestCase):
-    def test_reloader(self):
-        write('foo.py', data % dict(classname='a', output='a'))
-        import foo
-        app = foo.app
-        
-        self.assertEquals(app.request('/').data, 'a')
-        
-        # test class change
-        time.sleep(1)
-        write('foo.py', data % dict(classname='a', output='b'))
-        self.assertEquals(app.request('/').data, 'b')
-
-        # test urls change
-        time.sleep(1)
-        write('foo.py', data % dict(classname='c', output='c'))
-        self.assertEquals(app.request('/').data, 'c')
-        
-    def testUppercaseMethods(self):
-        urls = ("/", "hello")
-        app = web.application(urls, locals())
-        class hello:
-            def GET(self): return "hello"
-            def internal(self): return "secret"
-            
-        response = app.request('/', method='internal')
-        self.assertEquals(response.status, '405 Method Not Allowed')
-        
-    def testRedirect(self):
-        urls = (
-            "/a", "redirect /hello/",
-            "/b/(.*)", r"redirect /hello/\1",
-            "/hello/(.*)", "hello"
-        )
-        app = web.application(urls, locals())
-        class hello:
-            def GET(self, name): 
-                name = name or 'world'
-                return "hello " + name
-            
-        response = app.request('/a')
-        self.assertEquals(response.status, '301 Moved Permanently')
-        self.assertEquals(response.headers['Location'], 'http://0.0.0.0:8080/hello/')
-
-        response = app.request('/a?x=2')
-        self.assertEquals(response.status, '301 Moved Permanently')
-        self.assertEquals(response.headers['Location'], 'http://0.0.0.0:8080/hello/?x=2')
-
-        response = app.request('/b/foo?x=2')
-        self.assertEquals(response.status, '301 Moved Permanently')
-        self.assertEquals(response.headers['Location'], 'http://0.0.0.0:8080/hello/foo?x=2')
-        
-    def test_subdirs(self):
-        urls = (
-            "/(.*)", "blog"
-        )
-        class blog:
-            def GET(self, path):
-                return "blog " + path
-        app_blog = web.application(urls, locals())
-        
-        urls = (
-            "/blog", app_blog,
-            "/(.*)", "index"
-        )
-        class index:
-            def GET(self, path):
-                return "hello " + path
-        app = web.application(urls, locals())
-        
-        self.assertEquals(app.request('/blog/foo').data, 'blog foo')
-        self.assertEquals(app.request('/foo').data, 'hello foo')
-        
-        def processor(handler):
-            return web.ctx.path + ":" + handler()
-        app.add_processor(processor)
-        self.assertEquals(app.request('/blog/foo').data, '/blog/foo:blog foo')
-    
-    def test_subdomains(self):
-        def create_app(name):
-            urls = ("/", "index")
-            class index:
-                def GET(self):
-                    return name
-            return web.application(urls, locals())
-        
-        urls = (
-            "a.example.com", create_app('a'),
-            "b.example.com", create_app('b'),
-            ".*.example.com", create_app('*')
-        )
-        app = web.subdomain_application(urls, locals())
-        
-        def test(host, expected_result):
-            result = app.request('/', host=host)
-            self.assertEquals(result.data, expected_result)
-            
-        test('a.example.com', 'a')
-        test('b.example.com', 'b')
-        test('c.example.com', '*')
-        test('d.example.com', '*')
-        
-    def test_redirect(self):
-        urls = (
-            "/(.*)", "blog"
-        )
-        class blog:
-            def GET(self, path):
-                if path == 'foo':
-                    raise web.seeother('/login', absolute=True)
-                else:
-                    raise web.seeother('/bar')
-        app_blog = web.application(urls, locals())
-        
-        urls = (
-            "/blog", app_blog,
-            "/(.*)", "index"
-        )
-        class index:
-            def GET(self, path):
-                return "hello " + path
-        app = web.application(urls, locals())
-        
-        response = app.request('/blog/foo')
-        self.assertEquals(response.headers['Location'], 'http://0.0.0.0:8080/login')
-        
-        response = app.request('/blog/foo', env={'SCRIPT_NAME': '/x'})
-        self.assertEquals(response.headers['Location'], 'http://0.0.0.0:8080/x/login')
-
-        response = app.request('/blog/foo2')
-        self.assertEquals(response.headers['Location'], 'http://0.0.0.0:8080/blog/bar')
-        
-        response = app.request('/blog/foo2', env={'SCRIPT_NAME': '/x'})
-        self.assertEquals(response.headers['Location'], 'http://0.0.0.0:8080/x/blog/bar')
-
-    def test_processors(self):
-        urls = (
-            "/(.*)", "blog"
-        )
-        class blog:
-            def GET(self, path):
-                return 'blog ' + path
-
-        state = web.storage(x=0, y=0)
-        def f():
-            state.x += 1
-
-        app_blog = web.application(urls, locals())
-        app_blog.add_processor(web.loadhook(f))
-        
-        urls = (
-            "/blog", app_blog,
-            "/(.*)", "index"
-        )
-        class index:
-            def GET(self, path):
-                return "hello " + path
-        app = web.application(urls, locals())
-        def g():
-            state.y += 1
-        app.add_processor(web.loadhook(g))
-
-        app.request('/blog/foo')
-        assert state.x == 1 and state.y == 1, repr(state)
-        app.request('/foo')
-        assert state.x == 1 and state.y == 2, repr(state)
-        
-    def testUnicodeInput(self):
-        urls = (
-            "(/.*)", "foo"
-        )
-        class foo:
-            def GET(self, path):
-                i = web.input(name='')
-                return repr(i.name)
-                
-            def POST(self, path):
-                if path == '/multipart':
-                    i = web.input(file={})
-                    return i.file.value
-                else:
-                    i = web.input()
-                    return repr(dict(i))
-                
-        app = web.application(urls, locals())
-        
-        def f(name):
-            path = '/?' + urllib.urlencode({"name": name.encode('utf-8')})
-            self.assertEquals(app.request(path).data, repr(name))
-            
-        f(u'\u1234')
-        f(u'foo')
-
-        response = app.request('/', method='POST', data=dict(name='foo'))
-        self.assertEquals(response.data, "{'name': u'foo'}")
-        
-        data = '--boundary\r\nContent-Disposition: form-data; name="x"\r\nfoo\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="a.txt"\r\nContent-Type: text/plain\r\n\r\na\r\n--boundary--\r\n'
-        headers = {'Content-Type': 'multipart/form-data; boundary=boundary'}
-        response = app.request('/multipart', method="POST", data=data, headers=headers)
-        self.assertEquals(response.data, 'a')
-        
-    def testCustomNotFound(self):
-        urls_a = ("/", "a")
-        urls_b = ("/", "b")
-        
-        app_a = web.application(urls_a, locals())
-        app_b = web.application(urls_b, locals())
-        
-        app_a.notfound = lambda: web.HTTPError("404 Not Found", {}, "not found 1")
-        
-        urls = (
-            "/a", app_a,
-            "/b", app_b
-        )
-        app = web.application(urls, locals())
-        
-        def assert_notfound(path, message):
-            response = app.request(path)
-            self.assertEquals(response.status.split()[0], "404")
-            self.assertEquals(response.data, message)
-            
-        assert_notfound("/a/foo", "not found 1")
-        assert_notfound("/b/foo", "not found")
-        
-        app.notfound = lambda: web.HTTPError("404 Not Found", {}, "not found 2")
-        assert_notfound("/a/foo", "not found 1")
-        assert_notfound("/b/foo", "not found 2")
-
-    def testIter(self):
-        self.assertEquals(app.request('/iter').data, 'hello, world')
-        self.assertEquals(app.request('/iter?name=web').data, 'hello, web')
-
-        self.assertEquals(app.request('/iter', method='POST').data, 'hello, world')
-        self.assertEquals(app.request('/iter', method='POST', data='name=web').data, 'hello, web')
-
-    def testUnload(self):
-        x = web.storage(a=0)
-
-        urls = (
-            "/foo", "foo",
-            "/bar", "bar"
-        )
-        class foo:
-            def GET(self):
-                return "foo"
-        class bar:
-            def GET(self):
-                raise web.notfound()
-
-        app = web.application(urls, locals())
-        def unload():
-            x.a += 1
-        app.add_processor(web.unloadhook(unload))
-
-        app.request('/foo')
-        self.assertEquals(x.a, 1)
-
-        app.request('/bar')
-        self.assertEquals(x.a, 2)
-
-if __name__ == '__main__':
-    webtest.main()
--- a/bundled/webpy/test/browser.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-import webtest
-import web
-
-urls = (
-    "/", "index",
-    "/hello/(.*)", "hello",
-    "/cookie", "cookie",
-    "/setcookie", "setcookie",
-    "/redirect", "redirect",
-)
-app = web.application(urls, globals())
-
-class index:
-    def GET(self):
-        return "welcome"
-
-class hello:
-    def GET(self, name):
-        name = name or 'world'
-        return "hello, " + name + '!'
-
-class cookie:
-    def GET(self):
-        return ",".join(sorted(web.cookies().keys()))
-
-class setcookie:
-    def GET(self):
-        i = web.input()
-        for k, v in i.items():
-            web.setcookie(k, v)
-        return "done"
-
-class redirect:
-    def GET(self):
-        i = web.input(url='/')
-        raise web.seeother(i.url)
-
-class BrowserTest(webtest.TestCase):
-    def testCookies(self):
-        b = app.browser()
-        b.open('http://0.0.0.0/setcookie?x=1&y=2')
-        b.open('http://0.0.0.0/cookie')
-        self.assertEquals(b.data, 'x,y')
-
-    def testNotfound(self):
-        b = app.browser()
-        b.open('http://0.0.0.0/notfound')
-        self.assertEquals(b.status, 404)
-
-    def testRedirect(self):
-        b = app.browser()
-
-        b.open('http://0.0.0.0:8080/redirect')
-        self.assertEquals(b.url, 'http://0.0.0.0:8080/')
-        b.open('http://0.0.0.0:8080/redirect?url=/hello/foo')
-        self.assertEquals(b.url, 'http://0.0.0.0:8080/hello/foo')
-
-        b.open('https://0.0.0.0:8080/redirect')
-        self.assertEquals(b.url, 'https://0.0.0.0:8080/')
-        b.open('https://0.0.0.0:8080/redirect?url=/hello/foo')
-        self.assertEquals(b.url, 'https://0.0.0.0:8080/hello/foo')
-
-if __name__ == "__main__":
-    webtest.main()
--- a/bundled/webpy/test/db.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-"""DB test"""
-import webtest
-import web
-
-class DBTest(webtest.TestCase):
-    dbname = 'postgres'
-    driver = None
-    
-    def setUp(self):
-        self.db = webtest.setup_database(self.dbname, driver=self.driver)
-        self.db.query("CREATE TABLE person (name text, email text, active boolean)")
-
-    def tearDown(self):
-        # there might be some error with the current connection, delete from a new connection
-        self.db = webtest.setup_database(self.dbname, driver=self.driver)
-        self.db.query('DROP TABLE person')
-        
-    def _testable(self):
-        try:
-            webtest.setup_database(self.dbname, driver=self.driver)
-            return True
-        except ImportError, e:
-            print >> web.debug, str(e), "(ignoring %s)" % self.__class__.__name__
-            return False
-    
-    def testUnicode(self):
-        # Bug#177265: unicode queries throw errors
-        self.db.select('person', where='name=$name', vars={'name': u'\xf4'})
-    
-    def assertRows(self, n):
-        result = self.db.select('person')
-        self.assertEquals(len(list(result)), n)
-        
-    def testCommit(self):
-        t = self.db.transaction()
-        self.db.insert('person', False, name='user1')
-        t.commit()
-
-        t = self.db.transaction()
-        self.db.insert('person', False, name='user2')
-        self.db.insert('person', False, name='user3')
-        t.commit()
-    
-        self.assertRows(3)
-        
-    def testRollback(self):
-        t = self.db.transaction()
-        self.db.insert('person', False, name='user1')
-        self.db.insert('person', False, name='user2')
-        self.db.insert('person', False, name='user3')
-        t.rollback()        
-        self.assertRows(0)
-        
-    def testWrongQuery(self):
-        # It should be possible to run a correct query after getting an error from a wrong query.
-        try:
-            self.db.select('notthere')
-        except:
-            pass
-        self.db.select('person')
-        
-    def testNestedTransactions(self):
-        t1 = self.db.transaction()
-        self.db.insert('person', False, name='user1')
-        self.assertRows(1)        
-        
-        t2 = self.db.transaction()
-        self.db.insert('person', False, name='user2')
-        self.assertRows(2)  
-        t2.rollback()
-        self.assertRows(1)  
-        t3 = self.db.transaction()
-        self.db.insert('person', False, name='user3')
-        self.assertRows(2)  
-        t3.commit()
-        t1.commit()
-        self.assertRows(2)
-        
-    def testPooling(self):
-        # can't test pooling if DBUtils is not installed
-        try:
-            import DBUtils
-        except ImportError:
-            return
-        db = webtest.setup_database(self.dbname, pooling=True)
-        self.assertEquals(db.ctx.db.__class__.__module__, 'DBUtils.PooledDB')
-        db.select('person', limit=1)
-
-    def test_multiple_insert(self):
-        db = webtest.setup_database(self.dbname)
-        db.multiple_insert('person', [dict(name='a'), dict(name='b')], seqname=False)
-
-        assert db.select("person", where="name='a'")
-        assert db.select("person", where="name='b'")
-
-    def test_result_is_unicode(self):
-        db = webtest.setup_database(self.dbname)
-        self.db.insert('person', False, name='user')
-        name = db.select('person')[0].name
-        self.assertEquals(type(name), unicode)
-
-    def testBoolean(self):
-        def t(active):
-            name ='name-%s' % active
-            self.db.insert('person', False, name=name, active=active)
-            a = self.db.select('person', where='name=$name', vars=locals())[0].active
-            self.assertEquals(a, active)
-        t(False)
-        t(True)
-
-class PostgresTest(DBTest):
-    dbname = "postgres"
-    driver = "psycopg2"
-
-class PostgresTest_psycopg(PostgresTest):
-    driver = "psycopg"
-
-class PostgresTest_pgdb(PostgresTest):
-    driver = "pgdb"
-
-class SqliteTest(DBTest):
-    dbname = "sqlite"
-    driver = "sqlite3"
-    
-    def testNestedTransactions(self):
-        #nested transactions does not work with sqlite
-        pass
-
-class SqliteTest_pysqlite2(SqliteTest):
-    driver = "pysqlite2.dbapi2"
-
-class MySQLTest(DBTest):
-    dbname = "mysql"
-    
-    def setUp(self):
-        self.db = webtest.setup_database(self.dbname)
-        # In mysql, transactions are supported only with INNODB engine.
-        self.db.query("CREATE TABLE person (name text, email text) ENGINE=INNODB")
-
-    def testBoolean(self):
-        # boolean datatype is not suppoted in MySQL (at least until v5.0)
-        pass
-
-del DBTest
-
-def is_test(cls):
-    import inspect
-    return inspect.isclass(cls) and webtest.TestCase in inspect.getmro(cls)
-
-# ignore db tests when the required db adapter is not found.
-for t in globals().values():
-    if is_test(t) and not t('_testable')._testable():
-        del globals()[t.__name__]
-del t
-
-try:
-    import DBUtils
-except ImportError, e:
-    print >> web.debug, str(e) + "(ignoring testPooling)"
-
-if __name__ == '__main__':
-    webtest.main()
--- a/bundled/webpy/test/doctests.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-"""Run all doctests in web.py.
-"""
-import webtest
-
-def suite():
-    modules = [
-        "web.application",
-        "web.db", 
-        "web.http", 
-        "web.net", 
-        "web.session",
-        "web.template",
-        "web.utils", 
-#        "web.webapi", 
-#        "web.wsgi", 
-    ]
-    return webtest.doctest_suite(modules)
-    
-if __name__ == "__main__":
-    webtest.main()
--- a/bundled/webpy/test/session.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-import webtest
-import web
-import tempfile
-
-class SessionTest(webtest.TestCase):
-    def setUp(self):
-        app = web.auto_application()
-        session = self.make_session(app)
-        class count(app.page):
-            def GET(self):
-                session.count += 1
-                return str(session.count)
-        
-        class reset(app.page):
-            def GET(self):
-                session.kill()
-                return ""
-                
-        self.app = app
-        self.session = session
-        
-    def make_session(self, app):
-        dir = tempfile.mkdtemp()
-        store = web.session.DiskStore(tempfile.mkdtemp())
-        return web.session.Session(app, store, {'count': 0})
-        
-    def testSession(self):
-        b = self.app.browser() 
-        self.assertEquals(b.open('/count').read(), '1')
-        self.assertEquals(b.open('/count').read(), '2')
-        self.assertEquals(b.open('/count').read(), '3')
-        b.open('/reset')
-        self.assertEquals(b.open('/count').read(), '1')
-
-    def testParallelSessions(self):
-        b1 = self.app.browser()
-        b2 = self.app.browser()
-        
-        b1.open('/count')
-        
-        for i in range(1, 10):
-            self.assertEquals(b1.open('/count').read(), str(i+1))
-            self.assertEquals(b2.open('/count').read(), str(i))
-
-    def testBadSessionId(self):
-        b = self.app.browser()
-        self.assertEquals(b.open('/count').read(), '1')
-        self.assertEquals(b.open('/count').read(), '2')
-        
-        cookie = b.cookiejar._cookies['0.0.0.0']['/']['webpy_session_id']
-        cookie.value = '/etc/password'
-        self.assertEquals(b.open('/count').read(), '1')
-
-class DBSessionTest(SessionTest):
-    """Session test with db store."""
-    def make_session(self, app):
-        db = webtest.setup_database("postgres")
-        #db.printing = True
-        db.query("" 
-            + "CREATE TABLE session ("
-            + "    session_id char(128) unique not null,"
-            + "    atime timestamp default (current_timestamp at time zone 'utc'),"
-            + "    data text)"
-        )
-        store = web.session.DBStore(db, 'session')
-        return web.session.Session(app, store, {'count': 0})
-         
-    def tearDown(self):
-        # there might be some error with the current connection, delete from a new connection
-        self.db = webtest.setup_database("postgres")
-        self.db.query('DROP TABLE session')
-
-if __name__ == "__main__":
-    webtest.main()
--- a/bundled/webpy/test/webtest.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-"""webtest: test utilities.
-"""
-import sys, os
-
-# adding current directory to path to make sure local modules can be imported
-sys.path.insert(0, '.')
-
-from web.test import *
-    
-def setup_database(dbname, driver=None, pooling=False):
-    if dbname == 'sqlite':
-        db = web.database(dbn=dbname, db='webpy.db', pooling=pooling, driver=driver)
-    elif dbname == 'postgres':
-        user = os.getenv('USER')
-        db = web.database(dbn=dbname, db='webpy', user=user, pw='', pooling=pooling, driver=driver)
-    else:
-        db = web.database(dbn=dbname, db='webpy', user='scott', pw='tiger', pooling=pooling, driver=driver)
-
-    db.printing = '-v' in sys.argv
-    return db
--- a/bundled/webpy/tools/_makedoc.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-import os
-import web
-
-class Parser:
-    def __init__(self):
-        self.mode = 'normal'
-        self.text = ''
-        
-    def go(self, pyfile):
-        for line in file(pyfile):
-            if self.mode == 'in def':
-                self.text += ' ' + line.strip()
-                if line.strip().endswith(':'):
-                    if self.definition(self.text):
-                        self.text = ''
-                        self.mode = 'in func'
-                    else:
-                        self.text = ''
-                        self.mode = 'normal'
-
-            elif self.mode == 'in func':
-                if '"""' in line:
-                    self.text += line.strip().strip('"')
-                    self.mode = 'in doc'
-                    if line.count('"""') == 2:
-                        self.mode = 'normal'
-                        self.docstring(self.text)
-                        self.text = ''
-                else:
-                    self.mode = 'normal'
-
-            elif self.mode == 'in doc':
-                self.text += ' ' + line
-                if '"""' in line:
-                    self.mode = 'normal'
-                    self.docstring(self.text.strip().strip('"'))
-                    self.text = ''
-            
-            elif line.startswith('## '):
-                self.header(line.strip().strip('#'))
-            
-            elif line.startswith('def ') or line.startswith('class '):
-                self.text += line.strip().strip(':')
-                if line.strip().endswith(':'):
-                    if self.definition(self.text):
-                        self.text = ''
-                        self.mode = 'in func'
-                    else:
-                        self.text = ''
-                        self.mode = 'normal'
-                else:
-                    self.mode = 'in def'
-    
-    def clean(self, text):
-        text = text.strip()
-        text = text.replace('*', r'\*')
-        return text
-    
-    def definition(self, text):
-        text = web.lstrips(text, 'def ')
-        if text.startswith('_') or text.startswith('class _'):
-            return False
-        print '`'+text.strip()+'`'
-        return True
-    
-    def docstring(self, text):
-        print '   :', text.strip()
-        print
-    
-    def header(self, text):
-        print '##', text.strip()
-        print
-        
-for pyfile in os.listdir('trunk/web'):
-    if pyfile[-2:] == 'py':
-        print
-        print '## ' + pyfile
-        print
-        Parser().go('trunk/web/' + pyfile)
-print '`ctx`\n   :',
-print '\n'.join('    '+x for x in web.ctx.__doc__.strip().split('\n'))
--- a/bundled/webpy/tools/makedoc.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-"""
-Outputs web.py docs as html
-version 2.0: documents all code, and indents nicely.
-By Colin Rothwell (TheBoff)
-"""
-import sys
-import inspect
-import markdown
-sys.path.insert(0, '..')
-
-modules = [
-    'web.application',
-    'web.contrib.template',
-    'web.db',
-    'web.debugerror',
-    'web.form',
-    'web.http',
-    'web.httpserver',
-    'web.net',
-    'web.session',
-    'web.template',
-    'web.utils',
-    'web.webapi',
-    'web.webopenid',
-    'web.wsgi'
-]
-
-item_start = '<code class="%s">'
-item_end = '</code>'
-
-indent_amount = 30
-
-doc_these = ( #These are the types of object that should be docced
-    'module',
-    'classobj',
-    'instancemethod',
-    'function',
-    'type',
-    'property',    
-)
-
-not_these_names = ( #Any particular object names that shouldn't be doced
-    'fget',
-    'fset',
-    'fdel',
-    'storage', #These stop the lower case versions getting docced
-    'memoize',
-    'iterbetter',
-    'capturesstdout',
-    'profile',
-    'threadeddict',
-    'd', #Don't know what this is, but only only conclude it shouldn't be doc'd
-)
-
-css = '''
-<style type="text/css">
-.module {
-    font-size: 130%;
-    font-weight: bold;
-}
-
-.function, .class, .type {
-    font-size: 120%;
-    font-weight: bold;
-}
-
-.method, .property {
-    font-size: 115%;
-    font-weight: bold;
-}
-
-.ts {
-    font-size: small;
-    font-weight: lighter;
-    color: grey;
-}
-
-#contents_link {
-    position: fixed;
-    top: 0;
-    right: 0;
-    padding: 5px;
-    background: rgba(255, 255, 255, 0.5);
-}
-
-#contents_link a:hover {
-    font-weight: bold;
-}
-</style>
-'''
-
-
-indent_start = '<div style="margin-left:%dpx">'
-indent_end = '</div>'
-
-header = '''
-<div id="contents_link">
-<a href="#top">Back to contents</a>
-</div>
-'''
-
-def type_string(ob):
-    return str(type(ob)).split("'")[1]
-    
-def ts_css(text):
-    """applies nice css to the type string"""
-    return '<span class="ts">%s</span>' % text
-    
-def arg_string(func):
-    """Returns a nice argstring for a function or method"""
-    return inspect.formatargspec(*inspect.getargspec(func))
-
-def recurse_over(ob, name, indent_level=0):
-    ts = type_string(ob)    
-    if not ts in doc_these: return #stos what shouldn't be docced getting docced
-    if indent_level > 0 and ts == 'module': return #Stops it getting into the stdlib    
-    if name in not_these_names: return #Stops things we don't want getting docced
-    
-    indent = indent_level * indent_amount #Indents nicely
-    ds_indent = indent + (indent_amount / 2)
-    if indent_level > 0: print indent_start % indent
-    
-    argstr = ''
-    if ts.endswith(('function', 'method')):
-        argstr = arg_string(ob)
-    elif ts == 'classobj' or ts == 'type':
-        if ts == 'classobj': ts = 'class'
-        if hasattr(ob, '__init__'):
-            if type_string(ob.__init__) == 'instancemethod':
-                argstr = arg_string(ob.__init__)
-        else:
-            argstr = '(self)'
-    if ts == 'instancemethod': ts = 'method' #looks much nicer
-    
-    ds = inspect.getdoc(ob)
-    if ds is None: ds = ''
-    ds = markdown.Markdown(ds)
-    
-    mlink = '<a name="%s">' % name if ts == 'module' else '' 
-    mend = '</a>' if ts == 'module' else ''
-                
-    print ''.join(('<p>', ts_css(ts), item_start % ts, ' ', mlink, name, argstr,
-            mend, item_end, '<br />'))
-    print ''.join((indent_start % ds_indent, ds, indent_end, '</p>'))
-    #Although ''.join looks wierd, it's alot faster is string addition    
-    members = ''
-    
-    if hasattr(ob, '__all__'): members = ob.__all__
-    else: members = [item for item in dir(ob) if not item.startswith('_')] 
-    
-    if not 'im_class' in members:    
-        for name in members:
-            recurse_over(getattr(ob, name), name, indent_level + 1)
-    if indent_level > 0: print indent_end
-
-def main():
-    print '<div>' #Stops markdown vandalising my html.
-    print css
-    print header
-    print '<ul>'
-    for name in modules:
-        print '<li><a href="#%(name)s">%(name)s</a></li>' % dict(name=name)
-    print '</ul>' 
-    for name in modules:
-        mod = __import__(name, {}, {}, 'x')
-        recurse_over(mod, name)
-    print '</div>'
-        
-if __name__ == '__main__':
-    main()
\ No newline at end of file
--- a/bundled/webpy/tools/markdown.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,677 +0,0 @@
-#!/usr/bin/python
-import re, md5, sys, string
-
-"""markdown.py: A Markdown-styled-text to HTML converter in Python.
-
-Usage:
-  ./markdown.py textfile.markdown
- 
-Calling:
-  import markdown
-  somehtml = markdown.markdown(sometext)
-
-For other versions of markdown, see: 
-  http://www.freewisdom.org/projects/python-markdown/
-  http://en.wikipedia.org/wiki/Markdown
-"""
-
-__version__ = '1.0.1-2' # port of 1.0.1
-__license__ = "GNU GPL 2"
-__author__ = [
-  'John Gruber <http://daringfireball.net/>',
-  'Tollef Fog Heen <tfheen@err.no>', 
-  'Aaron Swartz <me@aaronsw.com>'
-]
-
-def htmlquote(text):
-    """Encodes `text` for raw use in HTML."""
-    text = text.replace("&", "&amp;") # Must be done first!
-    text = text.replace("<", "&lt;")
-    text = text.replace(">", "&gt;")
-    text = text.replace("'", "&#39;")
-    text = text.replace('"', "&quot;")
-    return text
-
-def semirandom(seed):
-    x = 0
-    for c in md5.new(seed).digest(): x += ord(c)
-    return x / (255*16.)
-
-class _Markdown:
-    emptyelt = " />"
-    tabwidth = 4
-
-    escapechars = '\\`*_{}[]()>#+-.!'
-    escapetable = {}
-    for char in escapechars:
-        escapetable[char] = md5.new(char).hexdigest()
-    
-    r_multiline = re.compile("\n{2,}")
-    r_stripspace = re.compile(r"^[ \t]+$", re.MULTILINE)
-    def parse(self, text):
-        self.urls = {}
-        self.titles = {}
-        self.html_blocks = {}
-        self.list_level = 0
-        
-        text = text.replace("\r\n", "\n")
-        text = text.replace("\r", "\n")
-        text += "\n\n"
-        text = self._Detab(text)
-        text = self.r_stripspace.sub("", text)
-        text = self._HashHTMLBlocks(text)
-        text = self._StripLinkDefinitions(text)
-        text = self._RunBlockGamut(text)
-        text = self._UnescapeSpecialChars(text)
-        return text
-    
-    r_StripLinkDefinitions = re.compile(r"""
-    ^[ ]{0,%d}\[(.+)\]:  # id = $1
-      [ \t]*\n?[ \t]*
-    <?(\S+?)>?           # url = $2
-      [ \t]*\n?[ \t]*
-    (?:
-      (?<=\s)            # lookbehind for whitespace
-      [\"\(]             # " is backlashed so it colorizes our code right
-      (.+?)              # title = $3
-      [\"\)]
-      [ \t]*
-    )?                   # title is optional
-    (?:\n+|\Z)
-    """ % (tabwidth-1), re.MULTILINE|re.VERBOSE)
-    def _StripLinkDefinitions(self, text):
-        def replacefunc(matchobj):
-            (t1, t2, t3) = matchobj.groups()
-            #@@ case sensitivity?
-            self.urls[t1.lower()] = self._EncodeAmpsAndAngles(t2)
-            if t3 is not None:
-                self.titles[t1.lower()] = t3.replace('"', '&quot;')
-            return ""
-
-        text = self.r_StripLinkDefinitions.sub(replacefunc, text)
-        return text
-
-    blocktagsb = r"p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|math"
-    blocktagsa = blocktagsb + "|ins|del"
-    
-    r_HashHTMLBlocks1 = re.compile(r"""
-    (            # save in $1
-    ^            # start of line  (with /m)
-    <(%s)        # start tag = $2
-    \b           # word break
-    (.*\n)*?     # any number of lines, minimally matching
-    </\2>        # the matching end tag
-    [ \t]*       # trailing spaces/tabs
-    (?=\n+|$)    # followed by a newline or end of document
-    )
-    """ % blocktagsa, re.MULTILINE | re.VERBOSE)
-
-    r_HashHTMLBlocks2 = re.compile(r"""
-    (            # save in $1
-    ^            # start of line  (with /m)
-    <(%s)        # start tag = $2
-    \b           # word break
-    (.*\n)*?     # any number of lines, minimally matching
-    .*</\2>      # the matching end tag
-    [ \t]*       # trailing spaces/tabs
-    (?=\n+|\Z)   # followed by a newline or end of document
-    )
-    """ % blocktagsb, re.MULTILINE | re.VERBOSE)
-
-    r_HashHR = re.compile(r"""
-    (?:
-    (?<=\n\n)    # Starting after a blank line
-    |            # or
-    \A\n?        # the beginning of the doc
-    )
-    (            # save in $1
-    [ ]{0,%d}
-    <(hr)        # start tag = $2
-    \b           # word break
-    ([^<>])*?    # 
-    /?>          # the matching end tag
-    [ \t]*
-    (?=\n{2,}|\Z)# followed by a blank line or end of document
-    )
-    """ % (tabwidth-1), re.VERBOSE)
-    r_HashComment = re.compile(r"""
-    (?:
-    (?<=\n\n)    # Starting after a blank line
-    |            # or
-    \A\n?        # the beginning of the doc
-    )
-    (            # save in $1
-    [ ]{0,%d}
-    (?: 
-      <!
-      (--.*?--\s*)+
-      >
-    )
-    [ \t]*
-    (?=\n{2,}|\Z)# followed by a blank line or end of document
-    )
-    """ % (tabwidth-1), re.VERBOSE)
-
-    def _HashHTMLBlocks(self, text):
-        def handler(m):
-            key = md5.new(m.group(1)).hexdigest()
-            self.html_blocks[key] = m.group(1)
-            return "\n\n%s\n\n" % key
-
-        text = self.r_HashHTMLBlocks1.sub(handler, text)
-        text = self.r_HashHTMLBlocks2.sub(handler, text)
-        oldtext = text
-        text = self.r_HashHR.sub(handler, text)
-        text = self.r_HashComment.sub(handler, text)
-        return text
-
-    #@@@ wrong!
-    r_hr1 = re.compile(r'^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$', re.M)
-    r_hr2 = re.compile(r'^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$', re.M)
-    r_hr3 = re.compile(r'^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$', re.M)
-	
-    def _RunBlockGamut(self, text):
-        text = self._DoHeaders(text)
-        for x in [self.r_hr1, self.r_hr2, self.r_hr3]:
-            text = x.sub("\n<hr%s\n" % self.emptyelt, text);
-        text = self._DoLists(text)
-        text = self._DoCodeBlocks(text)
-        text = self._DoBlockQuotes(text)
-
-    	# We did this in parse()
-    	# to escape the source
-    	# now it's stuff _we_ made
-    	# so we don't wrap it in <p>s.
-        text = self._HashHTMLBlocks(text)
-        text = self._FormParagraphs(text)
-        return text
-
-    r_NewLine = re.compile(" {2,}\n")
-    def _RunSpanGamut(self, text):
-        text = self._DoCodeSpans(text)
-        text = self._EscapeSpecialChars(text)
-        text = self._DoImages(text)
-        text = self._DoAnchors(text)
-        text = self._DoAutoLinks(text)
-        text = self._EncodeAmpsAndAngles(text)
-        text = self._DoItalicsAndBold(text)
-        text = self.r_NewLine.sub(" <br%s\n" % self.emptyelt, text)
-        return text
-
-    def _EscapeSpecialChars(self, text):
-        tokens = self._TokenizeHTML(text)
-        text = ""
-        for cur_token in tokens:
-            if cur_token[0] == "tag":
-                cur_token[1] = cur_token[1].replace('*', self.escapetable["*"])
-                cur_token[1] = cur_token[1].replace('_', self.escapetable["_"])
-                text += cur_token[1]
-            else:
-                text += self._EncodeBackslashEscapes(cur_token[1])
-        return text
-
-    r_DoAnchors1 = re.compile(
-          r""" (                 # wrap whole match in $1
-                  \[
-                    (.*?)        # link text = $2 
-                    # [for bracket nesting, see below]
-                  \]
-
-                  [ ]?           # one optional space
-                  (?:\n[ ]*)?    # one optional newline followed by spaces
-
-                  \[
-                    (.*?)        # id = $3
-                  \]
-                )
-    """, re.S|re.VERBOSE)
-    r_DoAnchors2 = re.compile(
-          r""" (                   # wrap whole match in $1
-                  \[
-                    (.*?)          # link text = $2
-                  \]
-                  \(               # literal paren
-                        [ \t]*
-                        <?(.+?)>?  # href = $3
-                        [ \t]*
-                        (          # $4
-                          ([\'\"]) # quote char = $5
-                          (.*?)    # Title = $6
-                          \5       # matching quote
-                        )?         # title is optional
-                  \)
-                )
-    """, re.S|re.VERBOSE)
-    def _DoAnchors(self, text): 
-        # We here don't do the same as the perl version, as python's regex
-        # engine gives us no way to match brackets.
-
-        def handler1(m):
-            whole_match = m.group(1)
-            link_text = m.group(2)
-            link_id = m.group(3).lower()
-            if not link_id: link_id = link_text.lower()
-            title = self.titles.get(link_id, None)
-                
-
-            if self.urls.has_key(link_id):
-                url = self.urls[link_id]
-                url = url.replace("*", self.escapetable["*"])
-                url = url.replace("_", self.escapetable["_"])
-                res = '<a href="%s"' % htmlquote(url)
-
-                if title:
-                    title = title.replace("*", self.escapetable["*"])
-                    title = title.replace("_", self.escapetable["_"])
-                    res += ' title="%s"' % htmlquote(title)
-                res += ">%s</a>" % htmlquote(link_text)
-            else:
-                res = whole_match
-            return res
-
-        def handler2(m):
-            whole_match = m.group(1)
-            link_text = m.group(2)
-            url = m.group(3)
-            title = m.group(6)
-
-            url = url.replace("*", self.escapetable["*"])
-            url = url.replace("_", self.escapetable["_"])
-            res = '''<a href="%s"''' % htmlquote(url)
-            
-            if title:
-                title = title.replace('"', '&quot;')
-                title = title.replace("*", self.escapetable["*"])
-                title = title.replace("_", self.escapetable["_"])
-                res += ' title="%s"' % htmlquote(title)
-            res += ">%s</a>" % htmlquote(link_text)
-            return res
-
-        text = self.r_DoAnchors1.sub(handler1, text)
-        text = self.r_DoAnchors2.sub(handler2, text)
-        return text
-
-    r_DoImages1 = re.compile(
-           r""" (                       # wrap whole match in $1
-                  !\[
-                    (.*?)               # alt text = $2
-                  \]
-
-                  [ ]?                  # one optional space
-                  (?:\n[ ]*)?           # one optional newline followed by spaces
-
-                  \[
-                    (.*?)               # id = $3
-                  \]
-
-                )
-    """, re.VERBOSE|re.S)
-
-    r_DoImages2 = re.compile(
-          r""" (                        # wrap whole match in $1
-                  !\[
-                    (.*?)               # alt text = $2
-                  \]
-                  \(                    # literal paren
-                        [ \t]*
-                        <?(\S+?)>?      # src url = $3
-                        [ \t]*
-                        (               # $4
-                        ([\'\"])        # quote char = $5
-                          (.*?)         # title = $6
-                          \5            # matching quote
-                          [ \t]*
-                        )?              # title is optional
-                  \)
-                )
-    """, re.VERBOSE|re.S)
-
-    def _DoImages(self, text):
-        def handler1(m):
-            whole_match = m.group(1)
-            alt_text = m.group(2)
-            link_id = m.group(3).lower()
-
-            if not link_id:
-                link_id = alt_text.lower()
-
-            alt_text = alt_text.replace('"', "&quot;")
-            if self.urls.has_key(link_id):
-                url = self.urls[link_id]
-                url = url.replace("*", self.escapetable["*"])
-                url = url.replace("_", self.escapetable["_"])
-                res = '''<img src="%s" alt="%s"''' % (htmlquote(url), htmlquote(alt_text))
-                if self.titles.has_key(link_id):
-                    title = self.titles[link_id]
-                    title = title.replace("*", self.escapetable["*"])
-                    title = title.replace("_", self.escapetable["_"])
-                    res += ' title="%s"' % htmlquote(title)
-                res += self.emptyelt
-            else:
-                res = whole_match
-            return res
-
-        def handler2(m):
-            whole_match = m.group(1)
-            alt_text = m.group(2)
-            url = m.group(3)
-            title = m.group(6) or ''
-            
-            alt_text = alt_text.replace('"', "&quot;")
-            title = title.replace('"', "&quot;")
-            url = url.replace("*", self.escapetable["*"])
-            url = url.replace("_", self.escapetable["_"])
-            res = '<img src="%s" alt="%s"' % (htmlquote(url), htmlquote(alt_text))
-            if title is not None:
-                title = title.replace("*", self.escapetable["*"])
-                title = title.replace("_", self.escapetable["_"])
-                res += ' title="%s"' % htmlquote(title)
-            res += self.emptyelt
-            return res
-
-        text = self.r_DoImages1.sub(handler1, text)
-        text = self.r_DoImages2.sub(handler2, text)
-        return text
-    
-    r_DoHeaders = re.compile(r"^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+", re.VERBOSE|re.M)
-    def _DoHeaders(self, text):
-        def findheader(text, c, n):
-            textl = text.split('\n')
-            for i in xrange(len(textl)):
-                if i >= len(textl): continue
-                count = textl[i].strip().count(c)
-                if count > 0 and count == len(textl[i].strip()) and textl[i+1].strip() == '' and textl[i-1].strip() != '':
-                    textl = textl[:i] + textl[i+1:]
-                    textl[i-1] = '<h'+n+'>'+self._RunSpanGamut(textl[i-1])+'</h'+n+'>'
-                    textl = textl[:i] + textl[i+1:]
-            text = '\n'.join(textl)
-            return text
-        
-        def handler(m):
-            level = len(m.group(1))
-            header = self._RunSpanGamut(m.group(2))
-            return "<h%s>%s</h%s>\n\n" % (level, header, level)
-
-        text = findheader(text, '=', '1')
-        text = findheader(text, '-', '2')
-        text = self.r_DoHeaders.sub(handler, text)
-        return text
-    
-    rt_l = r"""
-    (
-      (
-        [ ]{0,%d}
-        ([*+-]|\d+[.])
-        [ \t]+
-      )
-      (?:.+?)
-      (
-        \Z
-      |
-        \n{2,}
-        (?=\S)
-        (?![ \t]* ([*+-]|\d+[.])[ \t]+)
-      )
-    )
-    """ % (tabwidth - 1)
-    r_DoLists = re.compile('^'+rt_l, re.M | re.VERBOSE | re.S)
-    r_DoListsTop = re.compile(
-      r'(?:\A\n?|(?<=\n\n))'+rt_l, re.M | re.VERBOSE | re.S)
-    
-    def _DoLists(self, text):
-        def handler(m):
-            list_type = "ol"
-            if m.group(3) in [ "*", "-", "+" ]:
-                list_type = "ul"
-            listn = m.group(1)
-            listn = self.r_multiline.sub("\n\n\n", listn)
-            res = self._ProcessListItems(listn)
-            res = "<%s>\n%s</%s>\n" % (list_type, res, list_type)
-            return res
-            
-        if self.list_level:
-            text = self.r_DoLists.sub(handler, text)
-        else:
-            text = self.r_DoListsTop.sub(handler, text)
-        return text
-
-    r_multiend = re.compile(r"\n{2,}\Z")
-    r_ProcessListItems = re.compile(r"""
-    (\n)?                            # leading line = $1
-    (^[ \t]*)                        # leading whitespace = $2
-    ([*+-]|\d+[.]) [ \t]+            # list marker = $3
-    ((?:.+?)                         # list item text = $4
-    (\n{1,2}))
-    (?= \n* (\Z | \2 ([*+-]|\d+[.]) [ \t]+))
-    """, re.VERBOSE | re.M | re.S)
-
-    def _ProcessListItems(self, text):
-        self.list_level += 1
-        text = self.r_multiend.sub("\n", text)
-        
-        def handler(m):
-            item = m.group(4)
-            leading_line = m.group(1)
-            leading_space = m.group(2)
-
-            if leading_line or self.r_multiline.search(item):
-                item = self._RunBlockGamut(self._Outdent(item))
-            else:
-                item = self._DoLists(self._Outdent(item))
-                if item[-1] == "\n": item = item[:-1] # chomp
-                item = self._RunSpanGamut(item)
-            return "<li>%s</li>\n" % item
-
-        text = self.r_ProcessListItems.sub(handler, text)
-        self.list_level -= 1
-        return text
-    
-    r_DoCodeBlocks = re.compile(r"""
-    (?:\n\n|\A)
-    (                 # $1 = the code block
-    (?:
-    (?:[ ]{%d} | \t)  # Lines must start with a tab or equiv
-    .*\n+
-    )+
-    )
-    ((?=^[ ]{0,%d}\S)|\Z) # Lookahead for non-space/end of doc
-    """ % (tabwidth, tabwidth), re.M | re.VERBOSE)
-    def _DoCodeBlocks(self, text):
-        def handler(m):
-            codeblock = m.group(1)
-            codeblock = self._EncodeCode(self._Outdent(codeblock))
-            codeblock = self._Detab(codeblock)
-            codeblock = codeblock.lstrip("\n")
-            codeblock = codeblock.rstrip()
-            res = "\n\n<pre><code>%s\n</code></pre>\n\n" % codeblock
-            return res
-
-        text = self.r_DoCodeBlocks.sub(handler, text)
-        return text
-    r_DoCodeSpans = re.compile(r"""
-    (`+)            # $1 = Opening run of `
-    (.+?)           # $2 = The code block
-    (?<!`)
-    \1              # Matching closer
-    (?!`)
-    """, re.I|re.VERBOSE)
-    def _DoCodeSpans(self, text):
-        def handler(m):
-            c = m.group(2)
-            c = c.strip()
-            c = self._EncodeCode(c)
-            return "<code>%s</code>" % c
-
-        text = self.r_DoCodeSpans.sub(handler, text)
-        return text
-    
-    def _EncodeCode(self, text):
-        text = text.replace("&","&amp;")
-        text = text.replace("<","&lt;")
-        text = text.replace(">","&gt;")
-        for c in "*_{}[]\\":
-            text = text.replace(c, self.escapetable[c])
-        return text
-
-    
-    r_DoBold = re.compile(r"(\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1", re.VERBOSE | re.S)
-    r_DoItalics = re.compile(r"(\*|_) (?=\S) (.+?) (?<=\S) \1", re.VERBOSE | re.S)
-    def _DoItalicsAndBold(self, text):
-        text = self.r_DoBold.sub(r"<strong>\2</strong>", text)
-        text = self.r_DoItalics.sub(r"<em>\2</em>", text)
-        return text
-    
-    r_start = re.compile(r"^", re.M)
-    r_DoBlockQuotes1 = re.compile(r"^[ \t]*>[ \t]?", re.M)
-    r_DoBlockQuotes2 = re.compile(r"^[ \t]+$", re.M)
-    r_DoBlockQuotes3 = re.compile(r"""
-    (                       # Wrap whole match in $1
-     (
-       ^[ \t]*>[ \t]?       # '>' at the start of a line
-       .+\n                 # rest of the first line
-       (.+\n)*              # subsequent consecutive lines
-       \n*                  # blanks
-      )+
-    )""", re.M | re.VERBOSE)
-    r_protectpre = re.compile(r'(\s*<pre>.+?</pre>)', re.S)
-    r_propre = re.compile(r'^  ', re.M)
-
-    def _DoBlockQuotes(self, text):
-        def prehandler(m):
-            return self.r_propre.sub('', m.group(1))
-                
-        def handler(m):
-            bq = m.group(1)
-            bq = self.r_DoBlockQuotes1.sub("", bq)
-            bq = self.r_DoBlockQuotes2.sub("", bq)
-            bq = self._RunBlockGamut(bq)
-            bq = self.r_start.sub("  ", bq)
-            bq = self.r_protectpre.sub(prehandler, bq)
-            return "<blockquote>\n%s\n</blockquote>\n\n" % bq
-            
-        text = self.r_DoBlockQuotes3.sub(handler, text)
-        return text
-
-    r_tabbed = re.compile(r"^([ \t]*)")
-    def _FormParagraphs(self, text):
-        text = text.strip("\n")
-        grafs = self.r_multiline.split(text)
-
-        for g in xrange(len(grafs)):
-            t = grafs[g].strip() #@@?
-            if not self.html_blocks.has_key(t):
-                t = self._RunSpanGamut(t)
-                t = self.r_tabbed.sub(r"<p>", t)
-                t += "</p>"
-                grafs[g] = t
-
-        for g in xrange(len(grafs)):
-            t = grafs[g].strip()
-            if self.html_blocks.has_key(t):
-                grafs[g] = self.html_blocks[t]
-        
-        return "\n\n".join(grafs)
-
-    r_EncodeAmps = re.compile(r"&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)")
-    r_EncodeAngles = re.compile(r"<(?![a-z/?\$!])")
-    def _EncodeAmpsAndAngles(self, text):
-        text = self.r_EncodeAmps.sub("&amp;", text)
-        text = self.r_EncodeAngles.sub("&lt;", text)
-        return text
-
-    def _EncodeBackslashEscapes(self, text):
-        for char in self.escapechars:
-            text = text.replace("\\" + char, self.escapetable[char])
-        return text
-    
-    r_link = re.compile(r"<((https?|ftp):[^\'\">\s]+)>", re.I)
-    r_email = re.compile(r"""
-      <
-      (?:mailto:)?
-      (
-         [-.\w]+
-         \@
-         [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
-      )
-      >""", re.VERBOSE|re.I)
-    def _DoAutoLinks(self, text):
-        text = self.r_link.sub(r'<a href="\1">\1</a>', text)
-
-        def handler(m):
-            l = m.group(1)
-            return self._EncodeEmailAddress(self._UnescapeSpecialChars(l))
-    
-        text = self.r_email.sub(handler, text)
-        return text
-    
-    r_EncodeEmailAddress = re.compile(r">.+?:")
-    def _EncodeEmailAddress(self, text):
-        encode = [
-            lambda x: "&#%s;" % ord(x),
-            lambda x: "&#x%X;" % ord(x),
-            lambda x: x
-        ]
-
-        text = "mailto:" + text
-        addr = ""
-        for c in text:
-            if c == ':': addr += c; continue
-            
-            r = semirandom(addr)
-            if r < 0.45:
-                addr += encode[1](c)
-            elif r > 0.9 and c != '@':
-                addr += encode[2](c)
-            else:
-                addr += encode[0](c)
-
-        text = '<a href="%s">%s</a>' % (addr, addr)
-        text = self.r_EncodeEmailAddress.sub('>', text)
-        return text
-
-    def _UnescapeSpecialChars(self, text):
-        for key in self.escapetable.keys():
-            text = text.replace(self.escapetable[key], key)
-        return text
-    
-    tokenize_depth = 6
-    tokenize_nested_tags = '|'.join([r'(?:<[a-z/!$](?:[^<>]'] * tokenize_depth) + (')*>)' * tokenize_depth)
-    r_TokenizeHTML = re.compile(
-      r"""(?: <! ( -- .*? -- \s* )+ > ) |  # comment
-          (?: <\? .*? \?> ) |              # processing instruction
-          %s                               # nested tags
-    """ % tokenize_nested_tags, re.I|re.VERBOSE)
-    def _TokenizeHTML(self, text):
-        pos = 0
-        tokens = []
-        matchobj = self.r_TokenizeHTML.search(text, pos)
-        while matchobj:
-            whole_tag = matchobj.string[matchobj.start():matchobj.end()]
-            sec_start = matchobj.end()
-            tag_start = sec_start - len(whole_tag)
-            if pos < tag_start:
-                tokens.append(["text", matchobj.string[pos:tag_start]])
-
-            tokens.append(["tag", whole_tag])
-            pos = sec_start
-            matchobj = self.r_TokenizeHTML.search(text, pos)
-
-        if pos < len(text):
-            tokens.append(["text", text[pos:]])
-        return tokens
-
-    r_Outdent = re.compile(r"""^(\t|[ ]{1,%d})""" % tabwidth, re.M)
-    def _Outdent(self, text):
-        text = self.r_Outdent.sub("", text)
-        return text    
-
-    def _Detab(self, text): return text.expandtabs(self.tabwidth)
-
-def Markdown(*args, **kw): return _Markdown().parse(*args, **kw)
-markdown = Markdown
-
-if __name__ == '__main__':
-    if len(sys.argv) > 1:
-        print Markdown(open(sys.argv[1]).read())
-    else:
-        print Markdown(sys.stdin.read())
--- a/bundled/webpy/web/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-"""web.py: makes web apps (http://webpy.org)"""
-
-from __future__ import generators
-
-__version__ = "0.32"
-__author__ = [
-    "Aaron Swartz <me@aaronsw.com>",
-    "Anand Chitipothu <anandology@gmail.com>"
-]
-__license__ = "public domain"
-__contributors__ = "see http://webpy.org/changes"
-
-import utils, db, net, wsgi, http, webapi, httpserver, debugerror
-import template, form
-
-import session
-
-from utils import *
-from db import *
-from net import *
-from wsgi import *
-from http import *
-from webapi import *
-from httpserver import *
-from debugerror import *
-from application import *
-from browser import *
-import test
-try:
-    import webopenid as openid
-except ImportError:
-    pass # requires openid module
-
--- a/bundled/webpy/web/application.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,667 +0,0 @@
-#!/usr/bin/python
-"""
-Web application
-(from web.py)
-"""
-import webapi as web
-import webapi, wsgi, utils
-import debugerror
-from utils import lstrips, safeunicode
-import sys
-
-import urllib
-import traceback
-import itertools
-import os
-import re
-import types
-from exceptions import SystemExit
-
-try:
-    import wsgiref.handlers
-except ImportError:
-    pass # don't break people with old Pythons
-
-__all__ = [
-    "application", "auto_application",
-    "subdir_application", "subdomain_application", 
-    "loadhook", "unloadhook",
-    "autodelegate"
-]
-
-class application:
-    """
-    Application to delegate requests based on path.
-    
-        >>> urls = ("/hello", "hello")
-        >>> app = application(urls, globals())
-        >>> class hello:
-        ...     def GET(self): return "hello"
-        >>>
-        >>> app.request("/hello").data
-        'hello'
-    """
-    def __init__(self, mapping=(), fvars={}, autoreload=None):
-        if autoreload is None:
-            autoreload = web.config.get('debug', False)
-        self.mapping = mapping
-        self.fvars = fvars
-        self.processors = []
-        
-        self.add_processor(loadhook(self._load))
-        self.add_processor(unloadhook(self._unload))
-        
-        if autoreload:
-            def main_module_name():
-                mod = sys.modules['__main__']
-                file = getattr(mod, '__file__', None) # make sure this works even from python interpreter
-                return file and os.path.splitext(os.path.basename(file))[0]
-
-            def modname(fvars):
-                """find name of the module name from fvars."""
-                file, name = fvars.get('__file__'), fvars.get('__name__')
-                if file is None or name is None:
-                    return None
-
-                if name == '__main__':
-                    # Since the __main__ module can't be reloaded, the module has 
-                    # to be imported using its file name.                    
-                    name = main_module_name()
-                return name
-                
-            mapping_name = utils.dictfind(fvars, mapping)
-            module_name = modname(fvars)
-            
-            def reload_mapping():
-                """loadhook to reload mapping and fvars."""
-                mod = __import__(module_name)
-                mapping = getattr(mod, mapping_name, None)
-                if mapping:
-                    self.fvars = mod.__dict__
-                    self.mapping = mapping
-
-            self.add_processor(loadhook(Reloader()))
-            if mapping_name and module_name:
-                self.add_processor(loadhook(reload_mapping))
-
-            # load __main__ module usings its filename, so that it can be reloaded.
-            if main_module_name() and '__main__' in sys.argv:
-                try:
-                    __import__(main_module_name())
-                except ImportError:
-                    pass
-                    
-    def _load(self):
-        web.ctx.app_stack.append(self)
-        
-    def _unload(self):
-        web.ctx.app_stack = web.ctx.app_stack[:-1]
-        
-        if web.ctx.app_stack:
-            # this is a sub-application, revert ctx to earlier state.
-            oldctx = web.ctx.get('_oldctx')
-            if oldctx:
-                web.ctx.home = oldctx.home
-                web.ctx.homepath = oldctx.homepath
-                web.ctx.path = oldctx.path
-                web.ctx.fullpath = oldctx.fullpath
-                
-    def _cleanup(self):
-        #@@@
-        # Since the CherryPy Webserver uses thread pool, the thread-local state is never cleared.
-        # This interferes with the other requests. 
-        # clearing the thread-local storage to avoid that.
-        # see utils.ThreadedDict for details
-        import threading
-        t = threading.currentThread()
-        if hasattr(t, '_d'):
-            del t._d
-    
-    def add_mapping(self, pattern, classname):
-        self.mapping += (pattern, classname)
-        
-    def add_processor(self, processor):
-        """
-        Adds a processor to the application. 
-        
-            >>> urls = ("/(.*)", "echo")
-            >>> app = application(urls, globals())
-            >>> class echo:
-            ...     def GET(self, name): return name
-            ...
-            >>>
-            >>> def hello(handler): return "hello, " +  handler()
-            ...
-            >>> app.add_processor(hello)
-            >>> app.request("/web.py").data
-            'hello, web.py'
-        """
-        self.processors.append(processor)
-
-    def request(self, localpart='/', method='GET', data=None,
-                host="0.0.0.0:8080", headers=None, https=False, **kw):
-        """Makes request to this application for the specified path and method.
-        Response will be a storage object with data, status and headers.
-
-            >>> urls = ("/hello", "hello")
-            >>> app = application(urls, globals())
-            >>> class hello:
-            ...     def GET(self): 
-            ...         web.header('Content-Type', 'text/plain')
-            ...         return "hello"
-            ...
-            >>> response = app.request("/hello")
-            >>> response.data
-            'hello'
-            >>> response.status
-            '200 OK'
-            >>> response.headers['Content-Type']
-            'text/plain'
-
-        To use https, use https=True.
-
-            >>> urls = ("/redirect", "redirect")
-            >>> app = application(urls, globals())
-            >>> class redirect:
-            ...     def GET(self): raise web.seeother("/foo")
-            ...
-            >>> response = app.request("/redirect")
-            >>> response.headers['Location']
-            'http://0.0.0.0:8080/foo'
-            >>> response = app.request("/redirect", https=True)
-            >>> response.headers['Location']
-            'https://0.0.0.0:8080/foo'
-
-        The headers argument specifies HTTP headers as a mapping object
-        such as a dict.
-
-            >>> urls = ('/ua', 'uaprinter')
-            >>> class uaprinter:
-            ...     def GET(self):
-            ...         return 'your user-agent is ' + web.ctx.env['HTTP_USER_AGENT']
-            ... 
-            >>> app = application(urls, globals())
-            >>> app.request('/ua', headers = {
-            ...      'User-Agent': 'a small jumping bean/1.0 (compatible)'
-            ... }).data
-            'your user-agent is a small jumping bean/1.0 (compatible)'
-
-        """
-        path, maybe_query = urllib.splitquery(localpart)
-        query = maybe_query or ""
-        
-        if 'env' in kw:
-            env = kw['env']
-        else:
-            env = {}
-        env = dict(env, HTTP_HOST=host, REQUEST_METHOD=method, PATH_INFO=path, QUERY_STRING=query, HTTPS=str(https))
-        headers = headers or {}
-
-        for k, v in headers.items():
-            env['HTTP_' + k.upper().replace('-', '_')] = v
-
-        if 'HTTP_CONTENT_LENGTH' in env:
-            env['CONTENT_LENGTH'] = env.pop('HTTP_CONTENT_LENGTH')
-
-        if 'HTTP_CONTENT_TYPE' in env:
-            env['CONTENT_TYPE'] = env.pop('HTTP_CONTENT_TYPE')
-
-        if method in ["POST", "PUT"]:
-            data = data or ''
-            import StringIO
-            if isinstance(data, dict):
-                q = urllib.urlencode(data)
-            else:
-                q = data
-            env['wsgi.input'] = StringIO.StringIO(q)
-            if not env.get('CONTENT_TYPE', '').lower().startswith('multipart/') and 'CONTENT_LENGTH' not in env:
-                env['CONTENT_LENGTH'] = len(q)
-        response = web.storage()
-        def start_response(status, headers):
-            response.status = status
-            response.headers = dict(headers)
-            response.header_items = headers
-        response.data = "".join(self.wsgifunc()(env, start_response))
-        return response
-
-    def browser(self):
-        import browser
-        return browser.AppBrowser(self)
-
-    def handle(self):
-        fn, args = self._match(self.mapping, web.ctx.path)
-        return self._delegate(fn, self.fvars, args)
-        
-    def handle_with_processors(self):
-        def process(processors):
-            try:
-                if processors:
-                    p, processors = processors[0], processors[1:]
-                    return p(lambda: process(processors))
-                else:
-                    return self.handle()
-            except web.HTTPError:
-                raise
-            except (KeyboardInterrupt, SystemExit):
-                raise
-            except:
-                print >> web.debug, traceback.format_exc()
-                raise self.internalerror()
-        
-        # processors must be applied in the resvere order. (??)
-        return process(self.processors)
-                        
-    def wsgifunc(self, *middleware):
-        """Returns a WSGI-compatible function for this application."""
-        def peep(iterator):
-            """Peeps into an iterator by doing an iteration
-            and returns an equivalent iterator.
-            """
-            # wsgi requires the headers first
-            # so we need to do an iteration
-            # and save the result for later
-            try:
-                firstchunk = iterator.next()
-            except StopIteration:
-                firstchunk = ''
-
-            return itertools.chain([firstchunk], iterator)    
-                                
-        def is_generator(x): return x and hasattr(x, 'next')
-        
-        def wsgi(env, start_resp):
-            self.load(env)
-            try:
-                # allow uppercase methods only
-                if web.ctx.method.upper() != web.ctx.method:
-                    raise web.nomethod()
-
-                result = self.handle_with_processors()
-                if is_generator(result):
-                    result = peep(result)
-                else:
-                    result = [result]
-            except web.HTTPError, e:
-                result = [e.data]
-
-            result = web.utf8(iter(result))
-
-            status, headers = web.ctx.status, web.ctx.headers
-            start_resp(status, headers)
-            
-            def cleanup():
-                self._cleanup()
-                yield '' # force this function to be a generator
-                            
-            return itertools.chain(result, cleanup())
-
-        for m in middleware: 
-            wsgi = m(wsgi)
-
-        return wsgi
-
-    def run(self, *middleware):
-        """
-        Starts handling requests. If called in a CGI or FastCGI context, it will follow
-        that protocol. If called from the command line, it will start an HTTP
-        server on the port named in the first command line argument, or, if there
-        is no argument, on port 8080.
-        
-        `middleware` is a list of WSGI middleware which is applied to the resulting WSGI
-        function.
-        """
-        return wsgi.runwsgi(self.wsgifunc(*middleware))
-    
-    def cgirun(self, *middleware):
-        """
-        Return a CGI handler. This is mostly useful with Google App Engine.
-        There you can just do:
-        
-            main = app.cgirun()
-        """
-        wsgiapp = self.wsgifunc(*middleware)
-
-        try:
-            from google.appengine.ext.webapp.util import run_wsgi_app
-            return run_wsgi_app(wsgiapp)
-        except ImportError:
-            # we're not running from within Google App Engine
-            return wsgiref.handlers.CGIHandler().run(wsgiapp)
-    
-    def load(self, env):
-        """Initializes ctx using env."""
-        ctx = web.ctx
-        ctx.clear()
-        ctx.status = '200 OK'
-        ctx.headers = []
-        ctx.output = ''
-        ctx.environ = ctx.env = env
-        ctx.host = env.get('HTTP_HOST')
-
-        if env.get('wsgi.url_scheme') in ['http', 'https']:
-            ctx.protocol = env['wsgi.url_scheme']
-        elif env.get('HTTPS', '').lower() in ['on', 'true', '1']:
-            ctx.protocol = 'https'
-        else:
-            ctx.protocol = 'http'
-        ctx.homedomain = ctx.protocol + '://' + env.get('HTTP_HOST', '[unknown]')
-        ctx.homepath = os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', ''))
-        ctx.home = ctx.homedomain + ctx.homepath
-        #@@ home is changed when the request is handled to a sub-application.
-        #@@ but the real home is required for doing absolute redirects.
-        ctx.realhome = ctx.home
-        ctx.ip = env.get('REMOTE_ADDR')
-        ctx.method = env.get('REQUEST_METHOD')
-        ctx.path = env.get('PATH_INFO')
-        # http://trac.lighttpd.net/trac/ticket/406 requires:
-        if env.get('SERVER_SOFTWARE', '').startswith('lighttpd/'):
-            ctx.path = lstrips(env.get('REQUEST_URI').split('?')[0], ctx.homepath)
-            # Apache and CherryPy webservers unquote the url but lighttpd doesn't. 
-            # unquote explicitly for lighttpd to make ctx.path uniform across all servers.
-            ctx.path = urllib.unquote(ctx.path)
-
-        if env.get('QUERY_STRING'):
-            ctx.query = '?' + env.get('QUERY_STRING', '')
-        else:
-            ctx.query = ''
-
-        ctx.fullpath = ctx.path + ctx.query
-        
-        for k, v in ctx.iteritems():
-            if isinstance(v, str):
-                ctx[k] = safeunicode(v)
-
-        # status must always be str
-        ctx.status = '200 OK'
-        
-        ctx.app_stack = []
-
-    def _delegate(self, f, fvars, args=[]):
-        def handle_class(cls):
-            meth = web.ctx.method
-            if meth == 'HEAD' and not hasattr(cls, meth):
-                meth = 'GET'
-            if not hasattr(cls, meth):
-                raise web.nomethod(cls)
-            tocall = getattr(cls(), meth)
-            return tocall(*args)
-            
-        def is_class(o): return isinstance(o, (types.ClassType, type))
-            
-        if f is None:
-            raise web.notfound()
-        elif isinstance(f, application):
-            return f.handle_with_processors()
-        elif is_class(f):
-            return handle_class(f)
-        elif isinstance(f, basestring):
-            if f.startswith('redirect '):
-                url = f.split(' ', 1)[1]
-                if web.ctx.method == "GET":
-                    x = web.ctx.env.get('QUERY_STRING', '')
-                    if x:
-                        url += '?' + x
-                raise web.redirect(url)
-            elif '.' in f:
-                x = f.split('.')
-                mod, cls = '.'.join(x[:-1]), x[-1]
-                mod = __import__(mod, globals(), locals(), [""])
-                cls = getattr(mod, cls)
-            else:
-                cls = fvars[f]
-            return handle_class(cls)
-        elif hasattr(f, '__call__'):
-            return f()
-        else:
-            return web.notfound()
-
-    def _match(self, mapping, value):
-        for pat, what in utils.group(mapping, 2):
-            if isinstance(what, application):
-                if value.startswith(pat):
-                    f = lambda: self._delegate_sub_application(pat, what)
-                    return f, None
-                else:
-                    continue
-            elif isinstance(what, basestring):
-                what, result = utils.re_subm('^' + pat + '$', what, value)
-            else:
-                result = utils.re_compile('^' + pat + '$').match(value)
-                
-            if result: # it's a match
-                return what, [x for x in result.groups()]
-        return None, None
-        
-    def _delegate_sub_application(self, dir, app):
-        """Deletes request to sub application `app` rooted at the directory `dir`.
-        The home, homepath, path and fullpath values in web.ctx are updated to mimic request
-        to the subapp and are restored after it is handled. 
-        
-        @@Any issues with when used with yield?
-        """
-        web.ctx._oldctx = web.storage(web.ctx)
-        web.ctx.home += dir
-        web.ctx.homepath += dir
-        web.ctx.path = web.ctx.path[len(dir):]
-        web.ctx.fullpath = web.ctx.fullpath[len(dir):]
-        return app.handle_with_processors()
-            
-    def get_parent_app(self):
-        if self in web.ctx.app_stack:
-            index = web.ctx.app_stack.index(self)
-            if index > 0:
-                return web.ctx.app_stack[index-1]
-        
-    def notfound(self):
-        """Returns HTTPError with '404 not found' message"""
-        parent = self.get_parent_app()
-        if parent:
-            return parent.notfound()
-        else:
-            return web._NotFound()
-            
-    def internalerror(self):
-        """Returns HTTPError with '500 internal error' message"""
-        parent = self.get_parent_app()
-        if parent:
-            return parent.internalerror()
-        elif web.config.get('debug'):
-            import debugerror
-            return debugerror.debugerror()
-        else:
-            return web._InternalError()
-
-class auto_application(application):
-    """Application similar to `application` but urls are constructed 
-    automatiacally using metaclass.
-
-        >>> app = auto_application()
-        >>> class hello(app.page):
-        ...     def GET(self): return "hello, world"
-        ...
-        >>> class foo(app.page):
-        ...     path = '/foo/.*'
-        ...     def GET(self): return "foo"
-        >>> app.request("/hello").data
-        'hello, world'
-        >>> app.request('/foo/bar').data
-        'foo'
-    """
-    def __init__(self):
-        application.__init__(self)
-
-        class metapage(type):
-            def __init__(klass, name, bases, attrs):
-                type.__init__(klass, name, bases, attrs)
-                path = attrs.get('path', '/' + name)
-
-                # path can be specified as None to ignore that class
-                # typically required to create a abstract base class.
-                if path is not None:
-                    self.add_mapping(path, klass)
-
-        class page:
-            path = None
-            __metaclass__ = metapage
-
-        self.page = page
-
-# The application class already has the required functionality of subdir_application
-subdir_application = application
-                
-class subdomain_application(application):
-    """
-    Application to delegate requests based on the host.
-
-        >>> urls = ("/hello", "hello")
-        >>> app = application(urls, globals())
-        >>> class hello:
-        ...     def GET(self): return "hello"
-        >>>
-        >>> mapping = (r"hello\.example\.com", app)
-        >>> app2 = subdomain_application(mapping)
-        >>> app2.request("/hello", host="hello.example.com").data
-        'hello'
-        >>> response = app2.request("/hello", host="something.example.com")
-        >>> response.status
-        '404 Not Found'
-        >>> response.data
-        'not found'
-    """
-    def handle(self):
-        host = web.ctx.host.split(':')[0] #strip port
-        fn, args = self._match(self.mapping, host)
-        return self._delegate(fn, self.fvars, args)
-        
-    def _match(self, mapping, value):
-        for pat, what in utils.group(mapping, 2):
-            if isinstance(what, basestring):
-                what, result = utils.re_subm('^' + pat + '$', what, value)
-            else:
-                result = utils.re_compile('^' + pat + '$').match(value)
-
-            if result: # it's a match
-                return what, [x for x in result.groups()]
-        return None, None
-        
-def loadhook(h):
-    """
-    Converts a load hook into an application processor.
-    
-        >>> app = auto_application()
-        >>> def f(): "something done before handling request"
-        ...
-        >>> app.add_processor(loadhook(f))
-    """
-    def processor(handler):
-        h()
-        return handler()
-        
-    return processor
-    
-def unloadhook(h):
-    """
-    Converts an unload hook into an application processor.
-    
-        >>> app = auto_application()
-        >>> def f(): "something done after handling request"
-        ...
-        >>> app.add_processor(unloadhook(f))    
-    """
-    def processor(handler):
-        try:
-            result = handler()
-            is_generator = result and hasattr(result, 'next')
-        except:
-            # run the hook even when handler raises some exception
-            h()
-            raise
-
-        if is_generator:
-            return wrap(result)
-        else:
-            h()
-            return result
-            
-    def wrap(result):
-        def next():
-            try:
-                return result.next()
-            except:
-                # call the hook at the and of iterator
-                h()
-                raise
-
-        result = iter(result)
-        while True:
-            yield next()
-            
-    return processor
-
-def autodelegate(prefix=''):
-    """
-    Returns a method that takes one argument and calls the method named prefix+arg,
-    calling `notfound()` if there isn't one. Example:
-
-        urls = ('/prefs/(.*)', 'prefs')
-
-        class prefs:
-            GET = autodelegate('GET_')
-            def GET_password(self): pass
-            def GET_privacy(self): pass
-
-    `GET_password` would get called for `/prefs/password` while `GET_privacy` for 
-    `GET_privacy` gets called for `/prefs/privacy`.
-    
-    If a user visits `/prefs/password/change` then `GET_password(self, '/change')`
-    is called.
-    """
-    def internal(self, arg):
-        if '/' in arg:
-            first, rest = arg.split('/', 1)
-            func = prefix + first
-            args = ['/' + rest]
-        else:
-            func = prefix + arg
-            args = []
-        
-        if hasattr(self, func):
-            try:
-                return getattr(self, func)(*args)
-            except TypeError:
-                return web.notfound()
-        else:
-            return web.notfound()
-    return internal
-
-class Reloader:
-    """Checks to see if any loaded modules have changed on disk and, 
-    if so, reloads them.
-    """
-    def __init__(self):
-        self.mtimes = {}
-
-    def __call__(self):
-        for mod in sys.modules.values():
-            self.check(mod)
-            
-    def check(self, mod):
-        try: 
-            mtime = os.stat(mod.__file__).st_mtime
-        except (AttributeError, OSError, IOError):
-            return
-        if mod.__file__.endswith('.pyc') and os.path.exists(mod.__file__[:-1]):
-            mtime = max(os.stat(mod.__file__[:-1]).st_mtime, mtime)
-            
-        if mod not in self.mtimes:
-            self.mtimes[mod] = mtime
-        elif self.mtimes[mod] < mtime:
-            try: 
-                reload(mod)
-                self.mtimes[mod] = mtime
-            except ImportError: 
-                pass
-                
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
--- a/bundled/webpy/web/browser.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,236 +0,0 @@
-"""Browser to test web applications.
-(from web.py)
-"""
-from utils import re_compile
-from net import htmlunquote
-
-import httplib, urllib, urllib2
-import copy
-from StringIO import StringIO
-
-DEBUG = False
-
-__all__ = [
-    "BrowserError",
-    "Browser", "AppBrowser",
-    "AppHandler"
-]
-
-class BrowserError(Exception):
-    pass
-
-class Browser:
-    def __init__(self):
-        import cookielib
-        self.cookiejar = cookielib.CookieJar()
-        self._cookie_processor = urllib2.HTTPCookieProcessor(self.cookiejar)
-        self.form = None
-
-        self.url = "http://0.0.0.0:8080/"
-        self.path = "/"
-        
-        self.status = None
-        self.data = None
-        self._response = None
-        self._forms = None
-
-    def reset(self):
-        """Clears all cookies and history."""
-        self.cookiejar.clear()
-
-    def build_opener(self):
-        """Builds the opener using urllib2.build_opener. 
-        Subclasses can override this function to prodive custom openers.
-        """
-        return urllib2.build_opener()
-
-    def do_request(self, req):
-        if DEBUG:
-            print 'requesting', req.get_method(), req.get_full_url()
-        opener = self.build_opener()
-        opener.add_handler(self._cookie_processor)
-        try:
-            self._response = opener.open(req)
-        except urllib2.HTTPError, e:
-            self._response = e
-
-        self.url = self._response.geturl()
-        self.path = urllib2.Request(self.url).get_selector()
-        self.data = self._response.read()
-        self.status = self._response.code
-        self._forms = None
-        self.form = None
-        return self.get_response()
-
-    def open(self, url, data=None, headers={}):
-        """Opens the specified url."""
-        url = urllib.basejoin(self.url, url)
-        req = urllib2.Request(url, data, headers)
-        return self.do_request(req)
-
-    def show(self):
-        """Opens the current page in real web browser."""
-        f = open('page.html', 'w')
-        f.write(self.data)
-        f.close()
-
-        import webbrowser, os
-        url = 'file://' + os.path.abspath('page.html')
-        webbrowser.open(url)
-
-    def get_response(self):
-        """Returns a copy of the current response."""
-        return urllib.addinfourl(StringIO(self.data), self._response.info(), self._response.geturl())
-
-    def get_soup(self):
-        """Returns beautiful soup of the current document."""
-        import BeautifulSoup
-        return BeautifulSoup.BeautifulSoup(self.data)
-
-    def get_text(self, e=None):
-        """Returns content of e or the current document as plain text."""
-        e = e or self.get_soup()
-        return ''.join([htmlunquote(c) for c in e.recursiveChildGenerator() if isinstance(c, unicode)])
-
-    def _get_links(self):
-        soup = self.get_soup()
-        return [a for a in soup.findAll(name='a')]
-        
-    def get_links(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
-        """Returns all links in the document."""
-        return self._filter_links(self._get_links(),
-            text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
-
-    def follow_link(self, link=None, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
-        if link is None:
-            links = self._filter_links(self.get_links(),
-                text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
-            link = links and links[0]
-            
-        if link:
-            return self.open(link['href'])
-        else:
-            raise BrowserError("No link found")
-            
-    def find_link(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
-        links = self._filter_links(self.get_links(), 
-            text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
-        return links and links[0] or None
-            
-    def _filter_links(self, links, 
-            text=None, text_regex=None,
-            url=None, url_regex=None,
-            predicate=None):
-        predicates = []
-        if text is not None:
-            predicates.append(lambda link: link.string == text)
-        if text_regex is not None:
-            predicates.append(lambda link: re_compile(text_regex).search(link.string or ''))
-        if url is not None:
-            predicates.append(lambda link: link.get('href') == url)
-        if url_regex is not None:
-            predicates.append(lambda link: re_compile(url_regex).search(link.get('href', '')))
-        if predicate:
-            predicate.append(predicate)
-
-        def f(link):
-            for p in predicates:
-                if not p(link):
-                    return False
-            return True
-
-        return [link for link in links if f(link)]
-
-    def get_forms(self):
-        """Returns all forms in the current document.
-        The returned form objects implement the ClientForm.HTMLForm interface.
-        """
-        if self._forms is None:
-            import ClientForm
-            self._forms = ClientForm.ParseResponse(self.get_response(), backwards_compat=False)
-        return self._forms
-
-    def select_form(self, name=None, predicate=None, index=0):
-        """Selects the specified form."""
-        forms = self.get_forms()
-
-        if name is not None:
-            forms = [f for f in forms if f.name == name]
-        if predicate:
-            forms = [f for f in forms if predicate(f)]
-            
-        if forms:
-            self.form = forms[index]
-            return self.form
-        else:
-            raise BrowserError("No form selected.")
-        
-    def submit(self, **kw):
-        """submits the currently selected form."""
-        if self.form is None:
-            raise BrowserError("No form selected.")
-        req = self.form.click(**kw)
-        return self.do_request(req)
-
-    def __getitem__(self, key):
-        return self.form[key]
-
-    def __setitem__(self, key, value):
-        self.form[key] = value
-
-class AppBrowser(Browser):
-    """Browser interface to test web.py apps.
-    
-        b = AppBrowser(app)
-        b.open('/')
-        b.follow_link(text='Login')
-        
-        b.select_form(name='login')
-        b['username'] = 'joe'
-        b['password'] = 'secret'
-        b.submit()
-
-        assert b.path == '/'
-        assert 'Welcome joe' in b.get_text()
-    """
-    def __init__(self, app):
-        Browser.__init__(self)
-        self.app = app
-
-    def build_opener(self):
-        return urllib2.build_opener(AppHandler(self.app))
-
-class AppHandler(urllib2.HTTPHandler):
-    """urllib2 handler to handle requests using web.py application."""
-    handler_order = 100
-
-    def __init__(self, app):
-        self.app = app
-
-    def http_open(self, req):
-        result = self.app.request(
-            localpart=req.get_selector(),
-            method=req.get_method(),
-            host=req.get_host(),
-            data=req.get_data(),
-            headers=dict(req.header_items()),
-            https=req.get_type() == "https"
-        )
-        return self._make_response(result, req.get_full_url())
-
-    def https_open(self, req):
-        return self.http_open(req)
-    
-    try:
-        https_request = urllib2.HTTPHandler.do_request_
-    except AttributeError:
-        # for python 2.3
-        pass
-
-    def _make_response(self, result, url):
-        data = "\r\n".join(["%s: %s" % (k, v) for k, v in result.header_items])
-        headers = httplib.HTTPMessage(StringIO(data))
-        response = urllib.addinfourl(StringIO(result.data), headers, url)
-        code, msg = result.status.split(None, 1)
-        response.code, response.msg = int(code), msg
-        return response
--- a/bundled/webpy/web/contrib/template.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-"""
-Interface to various templating engines.
-"""
-import os.path
-
-__all__ = [
-    "render_cheetah", "render_genshi", "render_mako",
-    "cache", 
-]
-
-class render_cheetah:
-    """Rendering interface to Cheetah Templates.
-
-    Example:
-
-        render = render_cheetah('templates')
-        render.hello(name="cheetah")
-    """
-    def __init__(self, path):
-        # give error if Chetah is not installed
-        from Cheetah.Template import Template
-        self.path = path
-
-    def __getattr__(self, name):
-        from Cheetah.Template import Template
-        path = os.path.join(self.path, name + ".html")
-        
-        def template(**kw):
-            t = Template(file=path, searchList=[kw])
-            return t.respond()
-
-        return template
-    
-class render_genshi:
-    """Rendering interface genshi templates.
-    Example:
-
-    for xml/html templates.
-
-        render = render_genshi(['templates/'])
-        render.hello(name='genshi')
-
-    For text templates:
-
-        render = render_genshi(['templates/'], type='text')
-        render.hello(name='genshi')
-    """
-
-    def __init__(self, *a, **kwargs):
-        from genshi.template import TemplateLoader
-
-        self._type = kwargs.pop('type', None)
-        self._loader = TemplateLoader(*a, **kwargs)
-
-    def __getattr__(self, name):
-        # Assuming all templates are html
-        path = name + ".html"
-
-        if self._type == "text":
-            from genshi.template import TextTemplate
-            cls = TextTemplate
-            type = "text"
-        else:
-            cls = None
-            type = None
-
-        t = self._loader.load(path, cls=cls)
-        def template(**kw):
-            stream = t.generate(**kw)
-            if type:
-                return stream.render(type)
-            else:
-                return stream.render()
-        return template
-
-class render_jinja:
-    """Rendering interface to Jinja2 Templates
-    
-    Example:
-
-        render= render_jinja('templates')
-        render.hello(name='jinja2')
-    """
-    def __init__(self, *a, **kwargs):
-        extensions = kwargs.pop('extensions', [])
-        globals = kwargs.pop('globals', {})
-
-        from jinja2 import Environment,FileSystemLoader
-        self._lookup = Environment(loader=FileSystemLoader(*a, **kwargs), extensions=extensions)
-        self._lookup.globals.update(globals)
-        
-    def __getattr__(self, name):
-        # Assuming all templates end with .html
-        path = name + '.html'
-        t = self._lookup.get_template(path)
-        return t.render
-        
-class render_mako:
-    """Rendering interface to Mako Templates.
-
-    Example:
-
-        render = render_mako(directories=['templates'])
-        render.hello(name="mako")
-    """
-    def __init__(self, *a, **kwargs):
-        from mako.lookup import TemplateLookup
-        self._lookup = TemplateLookup(*a, **kwargs)
-
-    def __getattr__(self, name):
-        # Assuming all templates are html
-        path = name + ".html"
-        t = self._lookup.get_template(path)
-        return t.render
-
-class cache:
-    """Cache for any rendering interface.
-    
-    Example:
-
-        render = cache(render_cheetah("templates/"))
-        render.hello(name='cache')
-    """
-    def __init__(self, render):
-        self._render = render
-        self._cache = {}
-
-    def __getattr__(self, name):
-        if name not in self._cache:
-            self._cache[name] = getattr(self._render, name)
-        return self._cache[name]
--- a/bundled/webpy/web/db.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1137 +0,0 @@
-"""
-Database API
-(part of web.py)
-"""
-
-__all__ = [
-  "UnknownParamstyle", "UnknownDB", "TransactionError", 
-  "sqllist", "sqlors", "reparam", "sqlquote",
-  "SQLQuery", "SQLParam", "sqlparam",
-  "SQLLiteral", "sqlliteral",
-  "database", 'DB',
-]
-
-import time
-try:
-    import datetime
-except ImportError:
-    datetime = None
-
-from utils import threadeddict, storage, iters, iterbetter
-
-try:
-    # db module can work independent of web.py
-    from webapi import debug, config
-except:
-    import sys
-    debug = sys.stderr
-    config = storage()
-
-class UnknownDB(Exception):
-    """raised for unsupported dbms"""
-    pass
-
-class _ItplError(ValueError): 
-    def __init__(self, text, pos):
-        ValueError.__init__(self)
-        self.text = text
-        self.pos = pos
-    def __str__(self):
-        return "unfinished expression in %s at char %d" % (
-            repr(self.text), self.pos)
-
-class TransactionError(Exception): pass
-
-class UnknownParamstyle(Exception): 
-    """
-    raised for unsupported db paramstyles
-
-    (currently supported: qmark, numeric, format, pyformat)
-    """
-    pass
-    
-class SQLParam:
-    """
-    Parameter in SQLQuery.
-    
-        >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam("joe")])
-        >>> q
-        <sql: "SELECT * FROM test WHERE name='joe'">
-        >>> q.query()
-        'SELECT * FROM test WHERE name=%s'
-        >>> q.values()
-        ['joe']
-    """
-    def __init__(self, value):
-        self.value = value
-        
-    def get_marker(self, paramstyle='pyformat'):
-        if paramstyle == 'qmark':
-            return '?'
-        elif paramstyle == 'numeric':
-            return ':1'
-        elif paramstyle is None or paramstyle in ['format', 'pyformat']:
-            return '%s'
-        raise UnknownParamstyle, paramstyle
-        
-    def sqlquery(self): 
-        return SQLQuery([self])
-        
-    def __add__(self, other):
-        return self.sqlquery() + other
-        
-    def __radd__(self, other):
-        return other + self.sqlquery() 
-            
-    def __str__(self): 
-        return str(self.value)
-    
-    def __repr__(self):
-        return '<param: %s>' % repr(self.value)
-
-sqlparam =  SQLParam
-
-class SQLQuery:
-    """
-    You can pass this sort of thing as a clause in any db function.
-    Otherwise, you can pass a dictionary to the keyword argument `vars`
-    and the function will call reparam for you.
-
-    Internally, consists of `items`, which is a list of strings and
-    SQLParams, which get concatenated to produce the actual query.
-    """
-    # tested in sqlquote's docstring
-    def __init__(self, items=[]):
-        """Creates a new SQLQuery.
-        
-            >>> SQLQuery("x")
-            <sql: 'x'>
-            >>> q = SQLQuery(['SELECT * FROM ', 'test', ' WHERE x=', SQLParam(1)])
-            >>> q
-            <sql: 'SELECT * FROM test WHERE x=1'>
-            >>> q.query(), q.values()
-            ('SELECT * FROM test WHERE x=%s', [1])
-            >>> SQLQuery(SQLParam(1))
-            <sql: '1'>
-        """
-        if isinstance(items, list):
-            self.items = items
-        elif isinstance(items, SQLParam):
-            self.items = [items]
-        elif isinstance(items, SQLQuery):
-            self.items = list(items.items)
-        else:
-            self.items = [str(items)]
-            
-        # Take care of SQLLiterals
-        for i, item in enumerate(self.items):
-            if isinstance(item, SQLParam) and isinstance(item.value, SQLLiteral):
-                self.items[i] = item.value.v
-
-    def __add__(self, other):
-        if isinstance(other, basestring):
-            items = [other]
-        elif isinstance(other, SQLQuery):
-            items = other.items
-        else:
-            return NotImplemented
-        return SQLQuery(self.items + items)
-
-    def __radd__(self, other):
-        if isinstance(other, basestring):
-            items = [other]
-        else:
-            return NotImplemented
-            
-        return SQLQuery(items + self.items)
-
-    def __iadd__(self, other):
-        if isinstance(other, basestring):
-            items = [other]
-        elif isinstance(other, SQLQuery):
-            items = other.items
-        else:
-            return NotImplemented
-        self.items.extend(items)
-        return self
-
-    def __len__(self):
-        return len(self.query())
-        
-    def query(self, paramstyle=None):
-        """
-        Returns the query part of the sql query.
-            >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam('joe')])
-            >>> q.query()
-            'SELECT * FROM test WHERE name=%s'
-            >>> q.query(paramstyle='qmark')
-            'SELECT * FROM test WHERE name=?'
-        """
-        s = ''
-        for x in self.items:
-            if isinstance(x, SQLParam):
-                x = x.get_marker(paramstyle)
-            s += x
-        return s
-    
-    def values(self):
-        """
-        Returns the values of the parameters used in the sql query.
-            >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam('joe')])
-            >>> q.values()
-            ['joe']
-        """
-        return [i.value for i in self.items if isinstance(i, SQLParam)]
-        
-    def join(items, sep=' '):
-        """
-        Joins multiple queries.
-        
-        >>> SQLQuery.join(['a', 'b'], ', ')
-        <sql: 'a, b'>
-        """
-        if len(items) == 0:
-            return SQLQuery("")
-
-        q = SQLQuery(items[0])
-        for item in items[1:]:
-            q += sep
-            q += item
-        return q
-    
-    join = staticmethod(join)
-
-    def __str__(self):
-        try:
-            return self.query() % tuple([sqlify(x) for x in self.values()])
-        except (ValueError, TypeError):
-            return self.query()
-
-    def __repr__(self):
-        return '<sql: %s>' % repr(str(self))
-
-class SQLLiteral: 
-    """
-    Protects a string from `sqlquote`.
-
-        >>> sqlquote('NOW()')
-        <sql: "'NOW()'">
-        >>> sqlquote(SQLLiteral('NOW()'))
-        <sql: 'NOW()'>
-    """
-    def __init__(self, v): 
-        self.v = v
-
-    def __repr__(self): 
-        return self.v
-
-sqlliteral = SQLLiteral
-
-def _sqllist(values):
-    """
-        >>> _sqllist([1, 2, 3])
-        <sql: '(1, 2, 3)'>
-    """
-    items = []
-    items.append('(')
-    for i, v in enumerate(values):
-        if i != 0:
-            items.append(', ')
-        items.append(sqlparam(v))
-    items.append(')')
-    return SQLQuery(items)
-
-def reparam(string_, dictionary): 
-    """
-    Takes a string and a dictionary and interpolates the string
-    using values from the dictionary. Returns an `SQLQuery` for the result.
-
-        >>> reparam("s = $s", dict(s=True))
-        <sql: "s = 't'">
-        >>> reparam("s IN $s", dict(s=[1, 2]))
-        <sql: 's IN (1, 2)'>
-    """
-    dictionary = dictionary.copy() # eval mucks with it
-    vals = []
-    result = []
-    for live, chunk in _interpolate(string_):
-        if live:
-            v = eval(chunk, dictionary)
-            result.append(sqlquote(v))
-        else: 
-            result.append(chunk)
-    return SQLQuery.join(result, '')
-
-def sqlify(obj): 
-    """
-    converts `obj` to its proper SQL version
-
-        >>> sqlify(None)
-        'NULL'
-        >>> sqlify(True)
-        "'t'"
-        >>> sqlify(3)
-        '3'
-    """
-    # because `1 == True and hash(1) == hash(True)`
-    # we have to do this the hard way...
-
-    if obj is None:
-        return 'NULL'
-    elif obj is True:
-        return "'t'"
-    elif obj is False:
-        return "'f'"
-    elif datetime and isinstance(obj, datetime.datetime):
-        return repr(obj.isoformat())
-    else:
-        return repr(obj)
-
-def sqllist(lst): 
-    """
-    Converts the arguments for use in something like a WHERE clause.
-    
-        >>> sqllist(['a', 'b'])
-        'a, b'
-        >>> sqllist('a')
-        'a'
-        >>> sqllist(u'abc')
-        u'abc'
-    """
-    if isinstance(lst, basestring): 
-        return lst
-    else:
-        return ', '.join(lst)
-
-def sqlors(left, lst):
-    """
-    `left is a SQL clause like `tablename.arg = ` 
-    and `lst` is a list of values. Returns a reparam-style
-    pair featuring the SQL that ORs together the clause
-    for each item in the lst.
-
-        >>> sqlors('foo = ', [])
-        <sql: '1=2'>
-        >>> sqlors('foo = ', [1])
-        <sql: 'foo = 1'>
-        >>> sqlors('foo = ', 1)
-        <sql: 'foo = 1'>
-        >>> sqlors('foo = ', [1,2,3])
-        <sql: '(foo = 1 OR foo = 2 OR foo = 3 OR 1=2)'>
-    """
-    if isinstance(lst, iters):
-        lst = list(lst)
-        ln = len(lst)
-        if ln == 0:
-            return SQLQuery("1=2")
-        if ln == 1:
-            lst = lst[0]
-
-    if isinstance(lst, iters):
-        return SQLQuery(['('] + 
-          sum([[left, sqlparam(x), ' OR '] for x in lst], []) +
-          ['1=2)']
-        )
-    else:
-        return left + sqlparam(lst)
-        
-def sqlwhere(dictionary, grouping=' AND '): 
-    """
-    Converts a `dictionary` to an SQL WHERE clause `SQLQuery`.
-    
-        >>> sqlwhere({'cust_id': 2, 'order_id':3})
-        <sql: 'order_id = 3 AND cust_id = 2'>
-        >>> sqlwhere({'cust_id': 2, 'order_id':3}, grouping=', ')
-        <sql: 'order_id = 3, cust_id = 2'>
-        >>> sqlwhere({'a': 'a', 'b': 'b'}).query()
-        'a = %s AND b = %s'
-    """
-    return SQLQuery.join([k + ' = ' + sqlparam(v) for k, v in dictionary.items()], grouping)
-
-def sqlquote(a): 
-    """
-    Ensures `a` is quoted properly for use in a SQL query.
-
-        >>> 'WHERE x = ' + sqlquote(True) + ' AND y = ' + sqlquote(3)
-        <sql: "WHERE x = 't' AND y = 3">
-        >>> 'WHERE x = ' + sqlquote(True) + ' AND y IN ' + sqlquote([2, 3])
-        <sql: "WHERE x = 't' AND y IN (2, 3)">
-    """
-    if isinstance(a, list):
-        return _sqllist(a)
-    else:
-        return sqlparam(a).sqlquery()
-
-class Transaction:
-    """Database transaction."""
-    def __init__(self, ctx):
-        self.ctx = ctx
-        self.transaction_count = transaction_count = len(ctx.transactions)
-
-        class transaction_engine:
-            """Transaction Engine used in top level transactions."""
-            def do_transact(self):
-                ctx.commit(unload=False)
-
-            def do_commit(self):
-                ctx.commit()
-
-            def do_rollback(self):
-                ctx.rollback()
-
-        class subtransaction_engine:
-            """Transaction Engine used in sub transactions."""
-            def query(self, q):
-                db_cursor = ctx.db.cursor()
-                ctx.db_execute(db_cursor, SQLQuery(q % transaction_count))
-
-            def do_transact(self):
-                self.query('SAVEPOINT webpy_sp_%s')
-
-            def do_commit(self):
-                self.query('RELEASE SAVEPOINT webpy_sp_%s')
-
-            def do_rollback(self):
-                self.query('ROLLBACK TO SAVEPOINT webpy_sp_%s')
-
-        class dummy_engine:
-            """Transaction Engine used instead of subtransaction_engine 
-            when sub transactions are not supported."""
-            do_transact = do_commit = do_rollback = lambda self: None
-
-        if self.transaction_count:
-            # nested transactions are not supported in some databases
-            if self.ctx.get('ignore_nested_transactions'):
-                self.engine = dummy_engine()
-            else:
-                self.engine = subtransaction_engine()
-        else:
-            self.engine = transaction_engine()
-
-        self.engine.do_transact()
-        self.ctx.transactions.append(self)
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exctype, excvalue, traceback):
-        if exctype is not None:
-            self.rollback()
-        else:
-            self.commit()
-
-    def commit(self):
-        if len(self.ctx.transactions) > self.transaction_count:
-            self.engine.do_commit()
-            self.ctx.transactions = self.ctx.transactions[:self.transaction_count]
-
-    def rollback(self):
-        if len(self.ctx.transactions) > self.transaction_count:
-            self.engine.do_rollback()
-            self.ctx.transactions = self.ctx.transactions[:self.transaction_count]
-
-class DB: 
-    """Database"""
-    def __init__(self, db_module, keywords):
-        """Creates a database.
-        """
-        # some DB implementaions take optional paramater `driver` to use a specific driver modue
-        # but it should not be passed to connect
-        keywords.pop('driver', None)
-
-        self.db_module = db_module
-        self.keywords = keywords
-
-        
-        self._ctx = threadeddict()
-        # flag to enable/disable printing queries
-        self.printing = config.get('debug', False)
-        self.supports_multiple_insert = False
-        
-        try:
-            import DBUtils
-            # enable pooling if DBUtils module is available.
-            self.has_pooling = True
-        except ImportError:
-            self.has_pooling = False
-            
-        # Pooling can be disabled by passing pooling=False in the keywords.
-        self.has_pooling = self.keywords.pop('pooling', True) and self.has_pooling
-            
-    def _getctx(self): 
-        if not self._ctx.get('db'):
-            self._load_context(self._ctx)
-        return self._ctx
-    ctx = property(_getctx)
-    
-    def _load_context(self, ctx):
-        ctx.dbq_count = 0
-        ctx.transactions = [] # stack of transactions
-        
-        if self.has_pooling:
-            ctx.db = self._connect_with_pooling(self.keywords)
-        else:
-            ctx.db = self._connect(self.keywords)
-        ctx.db_execute = self._db_execute
-        
-        if not hasattr(ctx.db, 'commit'):
-            ctx.db.commit = lambda: None
-
-        if not hasattr(ctx.db, 'rollback'):
-            ctx.db.rollback = lambda: None
-            
-        def commit(unload=True):
-            # do db commit and release the connection if pooling is enabled.            
-            ctx.db.commit()
-            if unload and self.has_pooling:
-                self._unload_context(self._ctx)
-                
-        def rollback():
-            # do db rollback and release the connection if pooling is enabled.
-            ctx.db.rollback()
-            if self.has_pooling:
-                self._unload_context(self._ctx)
-                
-        ctx.commit = commit
-        ctx.rollback = rollback
-            
-    def _unload_context(self, ctx):
-        del ctx.db
-            
-    def _connect(self, keywords):
-        return self.db_module.connect(**keywords)
-        
-    def _connect_with_pooling(self, keywords):
-        def get_pooled_db():
-            from DBUtils import PooledDB
-
-            # In DBUtils 0.9.3, `dbapi` argument is renamed as `creator`
-            # see Bug#122112
-            
-            if PooledDB.__version__.split('.') < '0.9.3'.split('.'):
-                return PooledDB.PooledDB(dbapi=self.db_module, **keywords)
-            else:
-                return PooledDB.PooledDB(creator=self.db_module, **keywords)
-        
-        if getattr(self, '_pooleddb', None) is None:
-            self._pooleddb = get_pooled_db()
-        
-        return self._pooleddb.connection()
-        
-    def _db_cursor(self):
-        return self.ctx.db.cursor()
-
-    def _param_marker(self):
-        """Returns parameter marker based on paramstyle attribute if this database."""
-        style = getattr(self, 'paramstyle', 'pyformat')
-
-        if style == 'qmark':
-            return '?'
-        elif style == 'numeric':
-            return ':1'
-        elif style in ['format', 'pyformat']:
-            return '%s'
-        raise UnknownParamstyle, style
-
-    def _db_execute(self, cur, sql_query): 
-        """executes an sql query"""
-        self.ctx.dbq_count += 1
-        
-        try:
-            a = time.time()
-            paramstyle = getattr(self, 'paramstyle', 'pyformat')
-            out = cur.execute(sql_query.query(paramstyle), sql_query.values())
-            b = time.time()
-        except:
-            if self.printing:
-                print >> debug, 'ERR:', str(sql_query)
-            if self.ctx.transactions:
-                self.ctx.transactions[-1].rollback()
-            else:
-                self.ctx.rollback()
-            raise
-
-        if self.printing:
-            print >> debug, '%s (%s): %s' % (round(b-a, 2), self.ctx.dbq_count, str(sql_query))
-        return out
-    
-    def _where(self, where, vars): 
-        if isinstance(where, (int, long)):
-            where = "id = " + sqlparam(where)
-        #@@@ for backward-compatibility
-        elif isinstance(where, (list, tuple)) and len(where) == 2:
-            where = SQLQuery(where[0], where[1])
-        elif isinstance(where, SQLQuery):
-            pass
-        else:
-            where = reparam(where, vars)        
-        return where
-    
-    def query(self, sql_query, vars=None, processed=False, _test=False): 
-        """
-        Execute SQL query `sql_query` using dictionary `vars` to interpolate it.
-        If `processed=True`, `vars` is a `reparam`-style list to use 
-        instead of interpolating.
-        
-            >>> db = DB(None, {})
-            >>> db.query("SELECT * FROM foo", _test=True)
-            <sql: 'SELECT * FROM foo'>
-            >>> db.query("SELECT * FROM foo WHERE x = $x", vars=dict(x='f'), _test=True)
-            <sql: "SELECT * FROM foo WHERE x = 'f'">
-            >>> db.query("SELECT * FROM foo WHERE x = " + sqlquote('f'), _test=True)
-            <sql: "SELECT * FROM foo WHERE x = 'f'">
-        """
-        if vars is None: vars = {}
-        
-        if not processed and not isinstance(sql_query, SQLQuery):
-            sql_query = reparam(sql_query, vars)
-        
-        if _test: return sql_query
-        
-        db_cursor = self._db_cursor()
-        self._db_execute(db_cursor, sql_query)
-        
-        if db_cursor.description:
-            names = [x[0] for x in db_cursor.description]
-            def iterwrapper():
-                row = db_cursor.fetchone()
-                while row:
-                    yield storage(dict(zip(names, row)))
-                    row = db_cursor.fetchone()
-            out = iterbetter(iterwrapper())
-            out.__len__ = lambda: int(db_cursor.rowcount)
-            out.list = lambda: [storage(dict(zip(names, x))) \
-                               for x in db_cursor.fetchall()]
-        else:
-            out = db_cursor.rowcount
-        
-        if not self.ctx.transactions: 
-            self.ctx.commit()
-        return out
-    
-    def select(self, tables, vars=None, what='*', where=None, order=None, group=None, 
-               limit=None, offset=None, _test=False): 
-        """
-        Selects `what` from `tables` with clauses `where`, `order`, 
-        `group`, `limit`, and `offset`. Uses vars to interpolate. 
-        Otherwise, each clause can be a SQLQuery.
-        
-            >>> db = DB(None, {})
-            >>> db.select('foo', _test=True)
-            <sql: 'SELECT * FROM foo'>
-            >>> db.select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True)
-            <sql: 'SELECT * FROM foo, bar WHERE foo.bar_id = bar.id LIMIT 5'>
-        """
-        if vars is None: vars = {}
-        sql_clauses = self.sql_clauses(what, tables, where, group, order, limit, offset)
-        clauses = [self.gen_clause(sql, val, vars) for sql, val in sql_clauses if val is not None]
-        qout = SQLQuery.join(clauses)
-        if _test: return qout
-        return self.query(qout, processed=True)
-    
-    def where(self, table, what='*', order=None, group=None, limit=None, 
-              offset=None, _test=False, **kwargs):
-        """
-        Selects from `table` where keys are equal to values in `kwargs`.
-        
-            >>> db = DB(None, {})
-            >>> db.where('foo', bar_id=3, _test=True)
-            <sql: 'SELECT * FROM foo WHERE bar_id = 3'>
-            >>> db.where('foo', source=2, crust='dewey', _test=True)
-            <sql: "SELECT * FROM foo WHERE source = 2 AND crust = 'dewey'">
-        """
-        where = []
-        for k, v in kwargs.iteritems():
-            where.append(k + ' = ' + sqlquote(v))
-        return self.select(table, what=what, order=order, 
-               group=group, limit=limit, offset=offset, _test=_test, 
-               where=SQLQuery.join(where, ' AND '))
-    
-    def sql_clauses(self, what, tables, where, group, order, limit, offset): 
-        return (
-            ('SELECT', what),
-            ('FROM', sqllist(tables)),
-            ('WHERE', where),
-            ('GROUP BY', group),
-            ('ORDER BY', order),
-            ('LIMIT', limit),
-            ('OFFSET', offset))
-    
-    def gen_clause(self, sql, val, vars): 
-        if isinstance(val, (int, long)):
-            if sql == 'WHERE':
-                nout = 'id = ' + sqlquote(val)
-            else:
-                nout = SQLQuery(val)
-        #@@@
-        elif isinstance(val, (list, tuple)) and len(val) == 2:
-            nout = SQLQuery(val[0], val[1]) # backwards-compatibility
-        elif isinstance(val, SQLQuery):
-            nout = val
-        else:
-            nout = reparam(val, vars)
-
-        def xjoin(a, b):
-            if a and b: return a + ' ' + b
-            else: return a or b
-
-        return xjoin(sql, nout)
-
-    def insert(self, tablename, seqname=None, _test=False, **values): 
-        """
-        Inserts `values` into `tablename`. Returns current sequence ID.
-        Set `seqname` to the ID if it's not the default, or to `False`
-        if there isn't one.
-        
-            >>> db = DB(None, {})
-            >>> q = db.insert('foo', name='bob', age=2, created=SQLLiteral('NOW()'), _test=True)
-            >>> q
-            <sql: "INSERT INTO foo (age, name, created) VALUES (2, 'bob', NOW())">
-            >>> q.query()
-            'INSERT INTO foo (age, name, created) VALUES (%s, %s, NOW())'
-            >>> q.values()
-            [2, 'bob']
-        """
-        def q(x): return "(" + x + ")"
-        
-        if values:
-            _keys = SQLQuery.join(values.keys(), ', ')
-            _values = SQLQuery.join([sqlparam(v) for v in values.values()], ', ')
-            sql_query = "INSERT INTO %s " % tablename + q(_keys) + ' VALUES ' + q(_values)
-        else:
-            sql_query = SQLQuery("INSERT INTO %s DEFAULT VALUES" % tablename)
-
-        if _test: return sql_query
-        
-        db_cursor = self._db_cursor()
-        if seqname is not False: 
-            sql_query = self._process_insert_query(sql_query, tablename, seqname)
-
-        if isinstance(sql_query, tuple):
-            # for some databases, a separate query has to be made to find 
-            # the id of the inserted row.
-            q1, q2 = sql_query
-            self._db_execute(db_cursor, q1)
-            self._db_execute(db_cursor, q2)
-        else:
-            self._db_execute(db_cursor, sql_query)
-
-        try: 
-            out = db_cursor.fetchone()[0]
-        except Exception: 
-            out = None
-        
-        if not self.ctx.transactions: 
-            self.ctx.commit()
-        return out
-        
-    def multiple_insert(self, tablename, values, seqname=None, _test=False):
-        """
-        Inserts multiple rows into `tablename`. The `values` must be a list of dictioanries, 
-        one for each row to be inserted, each with the same set of keys.
-        Returns the list of ids of the inserted rows.        
-        Set `seqname` to the ID if it's not the default, or to `False`
-        if there isn't one.
-        
-            >>> db = DB(None, {})
-            >>> db.supports_multiple_insert = True
-            >>> values = [{"name": "foo", "email": "foo@example.com"}, {"name": "bar", "email": "bar@example.com"}]
-            >>> db.multiple_insert('person', values=values, _test=True)
-            <sql: "INSERT INTO person (name, email) VALUES ('foo', 'foo@example.com'), ('bar', 'bar@example.com')">
-        """        
-        if not values:
-            return []
-            
-        if not self.supports_multiple_insert:
-            out = [self.insert(tablename, seqname=seqname, _test=_test, **v) for v in values]
-            if seqname is False:
-                return None
-            else:
-                return out
-                
-        keys = values[0].keys()
-        #@@ make sure all keys are valid
-
-        # make sure all rows have same keys.
-        for v in values:
-            if v.keys() != keys:
-                raise ValueError, 'Bad data'
-
-        sql_query = SQLQuery('INSERT INTO %s (%s) VALUES ' % (tablename, ', '.join(keys))) 
-
-        data = []
-        for row in values:
-            d = SQLQuery.join([SQLParam(row[k]) for k in keys], ', ')
-            data.append('(' + d + ')')
-        sql_query += SQLQuery.join(data, ', ')
-
-        if _test: return sql_query
-
-        db_cursor = self._db_cursor()
-        if seqname is not False: 
-            sql_query = self._process_insert_query(sql_query, tablename, seqname)
-
-        if isinstance(sql_query, tuple):
-            # for some databases, a separate query has to be made to find 
-            # the id of the inserted row.
-            q1, q2 = sql_query
-            self._db_execute(db_cursor, q1)
-            self._db_execute(db_cursor, q2)
-        else:
-            self._db_execute(db_cursor, sql_query)
-
-        try: 
-            out = db_cursor.fetchone()[0]
-            out = range(out-len(values)+1, out+1)        
-        except Exception: 
-            out = None
-
-        if not self.ctx.transactions: 
-            self.ctx.commit()
-        return out
-
-    
-    def update(self, tables, where, vars=None, _test=False, **values): 
-        """
-        Update `tables` with clause `where` (interpolated using `vars`)
-        and setting `values`.
-
-            >>> db = DB(None, {})
-            >>> name = 'Joseph'
-            >>> q = db.update('foo', where='name = $name', name='bob', age=2,
-            ...     created=SQLLiteral('NOW()'), vars=locals(), _test=True)
-            >>> q
-            <sql: "UPDATE foo SET age = 2, name = 'bob', created = NOW() WHERE name = 'Joseph'">
-            >>> q.query()
-            'UPDATE foo SET age = %s, name = %s, created = NOW() WHERE name = %s'
-            >>> q.values()
-            [2, 'bob', 'Joseph']
-        """
-        if vars is None: vars = {}
-        where = self._where(where, vars)
-
-        query = (
-          "UPDATE " + sqllist(tables) + 
-          " SET " + sqlwhere(values, ', ') + 
-          " WHERE " + where)
-
-        if _test: return query
-        
-        db_cursor = self._db_cursor()
-        self._db_execute(db_cursor, query)
-        if not self.ctx.transactions: 
-            self.ctx.commit()
-        return db_cursor.rowcount
-    
-    def delete(self, table, where, using=None, vars=None, _test=False): 
-        """
-        Deletes from `table` with clauses `where` and `using`.
-
-            >>> db = DB(None, {})
-            >>> name = 'Joe'
-            >>> db.delete('foo', where='name = $name', vars=locals(), _test=True)
-            <sql: "DELETE FROM foo WHERE name = 'Joe'">
-        """
-        if vars is None: vars = {}
-        where = self._where(where, vars)
-
-        q = 'DELETE FROM ' + table
-        if where: q += ' WHERE ' + where
-        if using: q += ' USING ' + sqllist(using)
-
-        if _test: return q
-
-        db_cursor = self._db_cursor()
-        self._db_execute(db_cursor, q)
-        if not self.ctx.transactions: 
-            self.ctx.commit()
-        return db_cursor.rowcount
-
-    def _process_insert_query(self, query, tablename, seqname):
-        return query
-
-    def transaction(self): 
-        """Start a transaction."""
-        return Transaction(self.ctx)
-    
-class PostgresDB(DB): 
-    """Postgres driver."""
-    def __init__(self, **keywords):
-        if 'pw' in keywords:
-            keywords['password'] = keywords['pw']
-            del keywords['pw']
-            
-        db_module = import_driver(["psycopg2", "psycopg", "pgdb"], preferred=keywords.pop('driver', None))
-        if db_module.__name__ == "psycopg2":
-            import psycopg2.extensions
-            psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
-
-        keywords['database'] = keywords.pop('db')
-        self.dbname = "postgres"
-        self.paramstyle = db_module.paramstyle
-        DB.__init__(self, db_module, keywords)
-        self.supports_multiple_insert = True
-        
-    def _process_insert_query(self, query, tablename, seqname):
-        if seqname is None: 
-            seqname = tablename + "_id_seq"
-        return query + "; SELECT currval('%s')" % seqname
-
-    def _connect(self, keywords):
-        conn = DB._connect(self, keywords)
-        conn.set_client_encoding('UTF8')
-        return conn
-        
-    def _connect_with_pooling(self, keywords):
-        conn = DB._connect_with_pooling(self, keywords)
-        conn._con._con.set_client_encoding('UTF8')
-        return conn
-
-class MySQLDB(DB): 
-    def __init__(self, **keywords):
-        import MySQLdb as db
-        if 'pw' in keywords:
-            keywords['passwd'] = keywords['pw']
-            del keywords['pw']
-
-        if 'charset' not in keywords:
-            keywords['charset'] = 'utf8'
-        elif keywords['charset'] is None:
-            del keywords['charset']
-
-        self.paramstyle = db.paramstyle = 'pyformat' # it's both, like psycopg
-        self.dbname = "mysql"
-        DB.__init__(self, db, keywords)
-        self.supports_multiple_insert = True
-        
-    def _process_insert_query(self, query, tablename, seqname):
-        return query, SQLQuery('SELECT last_insert_id();')
-
-def import_driver(drivers, preferred=None):
-    """Import the first available driver or preferred driver.
-    """
-    if preferred:
-        drivers = [preferred]
-
-    for d in drivers:
-        try:
-            return __import__(d, None, None, ['x'])
-        except ImportError:
-            pass
-    raise ImportError("Unable to import " + " or ".join(drivers))
-
-class SqliteDB(DB): 
-    def __init__(self, **keywords):
-        db = import_driver(["sqlite3", "pysqlite2.dbapi2", "sqlite"], preferred=keywords.pop('driver', None))
-
-        if db.__name__ in ["sqlite3", "pysqlite2.dbapi2"]:
-            db.paramstyle = 'qmark'
-
-        self.paramstyle = db.paramstyle
-        keywords['database'] = keywords.pop('db')
-        self.dbname = "sqlite"        
-        DB.__init__(self, db, keywords)
-
-    def _process_insert_query(self, query, tablename, seqname):
-        return query, SQLQuery('SELECT last_insert_rowid();')
-    
-    def query(self, *a, **kw):
-        out = DB.query(self, *a, **kw)
-        if isinstance(out, iterbetter):
-            # rowcount is not provided by sqlite
-            del out.__len__
-        return out
-
-class FirebirdDB(DB):
-    """Firebird Database.
-    """
-    def __init__(self, **keywords):
-        try:
-            import kinterbasdb as db
-        except Exception:
-            db = None
-            pass
-        if 'pw' in keywords:
-            keywords['passwd'] = keywords['pw']
-            del keywords['pw']
-        keywords['database'] = keywords['db']
-        del keywords['db']
-        DB.__init__(self, db, keywords)
-        
-    def delete(self, table, where=None, using=None, vars=None, _test=False):
-        # firebird doesn't support using clause
-        using=None
-        return DB.delete(self, table, where, using, vars, _test)
-
-    def sql_clauses(self, what, tables, where, group, order, limit, offset):
-        return (
-            ('SELECT', ''),
-            ('FIRST', limit),
-            ('SKIP', offset),
-            ('', what),
-            ('FROM', sqllist(tables)),
-            ('WHERE', where),
-            ('GROUP BY', group),
-            ('ORDER BY', order)
-        )
-
-class MSSQLDB(DB):
-    def __init__(self, **keywords):
-        import pymssql as db    
-        if 'pw' in keywords:
-            keywords['password'] = keywords.pop('pw')
-        keywords['database'] = keywords.pop('db')
-        self.dbname = "mssql"
-        DB.__init__(self, db, keywords)
-
-    def sql_clauses(self, what, tables, where, group, order, limit, offset): 
-        return (
-            ('SELECT', what),
-            ('TOP', limit),
-            ('FROM', sqllist(tables)),
-            ('WHERE', where),
-            ('GROUP BY', group),
-            ('ORDER BY', order),
-            ('OFFSET', offset))
-            
-    def _test(self):
-        """Test LIMIT.
-
-            Fake presence of pymssql module for running tests.
-            >>> import sys
-            >>> sys.modules['pymssql'] = sys.modules['sys']
-            
-            MSSQL has TOP clause instead of LIMIT clause.
-            >>> db = MSSQLDB(db='test', user='joe', pw='secret')
-            >>> db.select('foo', limit=4, _test=True)
-            <sql: 'SELECT * TOP 4 FROM foo'>
-        """
-        pass
-
-class OracleDB(DB): 
-    def __init__(self, **keywords): 
-        import cx_Oracle as db 
-        if 'pw' in keywords: 
-            keywords['password'] = keywords.pop('pw') 
-
-        #@@ TODO: use db.makedsn if host, port is specified 
-        keywords['dsn'] = keywords.pop('db') 
-        self.dbname = 'oracle' 
-        db.paramstyle = 'numeric' 
-        self.paramstyle = db.paramstyle
-
-        # oracle doesn't support pooling 
-        keywords.pop('pooling', None) 
-        DB.__init__(self, db, keywords) 
-
-    def _process_insert_query(self, query, tablename, seqname): 
-        if seqname is None: 
-            # It is not possible to get seq name from table name in Oracle
-            return query
-        else:
-            return query + "; SELECT %s.currval FROM dual" % seqname 
-
-_databases = {}
-def database(dburl=None, **params):
-    """Creates appropriate database using params.
-    
-    Pooling will be enabled if DBUtils module is available. 
-    Pooling can be disabled by passing pooling=False in params.
-    """
-    dbn = params.pop('dbn')
-    if dbn in _databases:
-        return _databases[dbn](**params)
-    else:
-        raise UnknownDB, dbn
-
-def register_database(name, clazz):
-    """
-    Register a database.
-
-        >>> class LegacyDB(DB): 
-        ...     def __init__(self, **params): 
-        ...        pass 
-        ...
-        >>> register_database('legacy', LegacyDB)
-        >>> db = database(dbn='legacy', db='test', user='joe', passwd='secret') 
-    """
-    _databases[name] = clazz
-
-register_database('mysql', MySQLDB)
-register_database('postgres', PostgresDB)
-register_database('sqlite', SqliteDB)
-register_database('firebird', FirebirdDB)
-register_database('mssql', MSSQLDB)
-register_database('oracle', OracleDB)
-
-def _interpolate(format): 
-    """
-    Takes a format string and returns a list of 2-tuples of the form
-    (boolean, string) where boolean says whether string should be evaled
-    or not.
-
-    from <http://lfw.org/python/Itpl.py> (public domain, Ka-Ping Yee)
-    """
-    from tokenize import tokenprog
-
-    def matchorfail(text, pos):
-        match = tokenprog.match(text, pos)
-        if match is None:
-            raise _ItplError(text, pos)
-        return match, match.end()
-
-    namechars = "abcdefghijklmnopqrstuvwxyz" \
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
-    chunks = []
-    pos = 0
-
-    while 1:
-        dollar = format.find("$", pos)
-        if dollar < 0: 
-            break
-        nextchar = format[dollar + 1]
-
-        if nextchar == "{":
-            chunks.append((0, format[pos:dollar]))
-            pos, level = dollar + 2, 1
-            while level:
-                match, pos = matchorfail(format, pos)
-                tstart, tend = match.regs[3]
-                token = format[tstart:tend]
-                if token == "{": 
-                    level = level + 1
-                elif token == "}":  
-                    level = level - 1
-            chunks.append((1, format[dollar + 2:pos - 1]))
-
-        elif nextchar in namechars:
-            chunks.append((0, format[pos:dollar]))
-            match, pos = matchorfail(format, dollar + 1)
-            while pos < len(format):
-                if format[pos] == "." and \
-                    pos + 1 < len(format) and format[pos + 1] in namechars:
-                    match, pos = matchorfail(format, pos + 1)
-                elif format[pos] in "([":
-                    pos, level = pos + 1, 1
-                    while level:
-                        match, pos = matchorfail(format, pos)
-                        tstart, tend = match.regs[3]
-                        token = format[tstart:tend]
-                        if token[0] in "([": 
-                            level = level + 1
-                        elif token[0] in ")]":  
-                            level = level - 1
-                else: 
-                    break
-            chunks.append((1, format[dollar + 1:pos]))
-        else:
-            chunks.append((0, format[pos:dollar + 1]))
-            pos = dollar + 1 + (nextchar == "$")
-
-    if pos < len(format): 
-        chunks.append((0, format[pos:]))
-    return chunks
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
--- a/bundled/webpy/web/debugerror.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,356 +0,0 @@
-"""
-pretty debug errors
-(part of web.py)
-
-portions adapted from Django <djangoproject.com> 
-Copyright (c) 2005, the Lawrence Journal-World
-Used under the modified BSD license:
-http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
-"""
-
-__all__ = ["debugerror", "djangoerror", "emailerrors"]
-
-import sys, urlparse, pprint, traceback
-from net import websafe
-from template import Template
-from utils import sendmail
-import webapi as web
-
-import os, os.path
-whereami = os.path.join(os.getcwd(), __file__)
-whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1])
-djangoerror_t = """\
-$def with (exception_type, exception_value, frames)
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html lang="en">
-<head>
-  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
-  <meta name="robots" content="NONE,NOARCHIVE" />
-  <title>$exception_type at $ctx.path</title>
-  <style type="text/css">
-    html * { padding:0; margin:0; }
-    body * { padding:10px 20px; }
-    body * * { padding:0; }
-    body { font:small sans-serif; }
-    body>div { border-bottom:1px solid #ddd; }
-    h1 { font-weight:normal; }
-    h2 { margin-bottom:.8em; }
-    h2 span { font-size:80%; color:#666; font-weight:normal; }
-    h3 { margin:1em 0 .5em 0; }
-    h4 { margin:0 0 .5em 0; font-weight: normal; }
-    table { 
-        border:1px solid #ccc; border-collapse: collapse; background:white; }
-    tbody td, tbody th { vertical-align:top; padding:2px 3px; }
-    thead th { 
-        padding:1px 6px 1px 3px; background:#fefefe; text-align:left; 
-        font-weight:normal; font-size:11px; border:1px solid #ddd; }
-    tbody th { text-align:right; color:#666; padding-right:.5em; }
-    table.vars { margin:5px 0 2px 40px; }
-    table.vars td, table.req td { font-family:monospace; }
-    table td.code { width:100%;}
-    table td.code div { overflow:hidden; }
-    table.source th { color:#666; }
-    table.source td { 
-        font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
-    ul.traceback { list-style-type:none; }
-    ul.traceback li.frame { margin-bottom:1em; }
-    div.context { margin: 10px 0; }
-    div.context ol { 
-        padding-left:30px; margin:0 10px; list-style-position: inside; }
-    div.context ol li { 
-        font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
-    div.context ol.context-line li { color:black; background-color:#ccc; }
-    div.context ol.context-line li span { float: right; }
-    div.commands { margin-left: 40px; }
-    div.commands a { color:black; text-decoration:none; }
-    #summary { background: #ffc; }
-    #summary h2 { font-weight: normal; color: #666; }
-    #explanation { background:#eee; }
-    #template, #template-not-exist { background:#f6f6f6; }
-    #template-not-exist ul { margin: 0 0 0 20px; }
-    #traceback { background:#eee; }
-    #requestinfo { background:#f6f6f6; padding-left:120px; }
-    #summary table { border:none; background:transparent; }
-    #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
-    #requestinfo h3 { margin-bottom:-1em; }
-    .error { background: #ffc; }
-    .specific { color:#cc3300; font-weight:bold; }
-  </style>
-  <script type="text/javascript">
-  //<!--
-    function getElementsByClassName(oElm, strTagName, strClassName){
-        // Written by Jonathan Snook, http://www.snook.ca/jon; 
-        // Add-ons by Robert Nyman, http://www.robertnyman.com
-        var arrElements = (strTagName == "*" && document.all)? document.all :
-        oElm.getElementsByTagName(strTagName);
-        var arrReturnElements = new Array();
-        strClassName = strClassName.replace(/\-/g, "\\-");
-        var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
-        var oElement;
-        for(var i=0; i<arrElements.length; i++){
-            oElement = arrElements[i];
-            if(oRegExp.test(oElement.className)){
-                arrReturnElements.push(oElement);
-            }
-        }
-        return (arrReturnElements)
-    }
-    function hideAll(elems) {
-      for (var e = 0; e < elems.length; e++) {
-        elems[e].style.display = 'none';
-      }
-    }
-    window.onload = function() {
-      hideAll(getElementsByClassName(document, 'table', 'vars'));
-      hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
-      hideAll(getElementsByClassName(document, 'ol', 'post-context'));
-    }
-    function toggle() {
-      for (var i = 0; i < arguments.length; i++) {
-        var e = document.getElementById(arguments[i]);
-        if (e) {
-          e.style.display = e.style.display == 'none' ? 'block' : 'none';
-        }
-      }
-      return false;
-    }
-    function varToggle(link, id) {
-      toggle('v' + id);
-      var s = link.getElementsByTagName('span')[0];
-      var uarr = String.fromCharCode(0x25b6);
-      var darr = String.fromCharCode(0x25bc);
-      s.innerHTML = s.innerHTML == uarr ? darr : uarr;
-      return false;
-    }
-    //-->
-  </script>
-</head>
-<body>
-
-$def dicttable (d, kls='req', id=None):
-    $ items = d and d.items() or []
-    $items.sort()
-    $:dicttable_items(items, kls, id)
-        
-$def dicttable_items(items, kls='req', id=None):
-    $if items:
-        <table class="$kls"
-        $if id: id="$id"
-        ><thead><tr><th>Variable</th><th>Value</th></tr></thead>
-        <tbody>
-        $for k, v in items:
-            <tr><td>$k</td><td class="code"><div>$prettify(v)</div></td></tr>
-        </tbody>
-        </table>
-    $else:
-        <p>No data.</p>
-
-<div id="summary">
-  <h1>$exception_type at $ctx.path</h1>
-  <h2>$exception_value</h2>
-  <table><tr>
-    <th>Python</th>
-    <td>$frames[0].filename in $frames[0].function, line $frames[0].lineno</td>
-  </tr><tr>
-    <th>Web</th>
-    <td>$ctx.method $ctx.home$ctx.path</td>
-  </tr></table>
-</div>
-<div id="traceback">
-<h2>Traceback <span>(innermost first)</span></h2>
-<ul class="traceback">
-$for frame in frames:
-    <li class="frame">
-    <code>$frame.filename</code> in <code>$frame.function</code>
-    $if frame.context_line:
-        <div class="context" id="c$frame.id">
-        $if frame.pre_context:
-            <ol start="$frame.pre_context_lineno" class="pre-context" id="pre$frame.id">
-            $for line in frame.pre_context:
-                <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
-            </ol>
-            <ol start="$frame.lineno" class="context-line"><li onclick="toggle('pre$frame.id', 'post$frame.id')">$frame.context_line <span>...</span></li></ol>
-        $if frame.post_context:
-            <ol start='${frame.lineno + 1}' class="post-context" id="post$frame.id">
-            $for line in frame.post_context:
-                <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
-            </ol>
-      </div>
-    
-    $if frame.vars:
-        <div class="commands">
-        <a href='#' onclick="return varToggle(this, '$frame.id')"><span>&#x25b6;</span> Local vars</a>
-        $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame))
-        </div>
-        $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id)))
-      </li>
-  </ul>
-</div>
-
-<div id="requestinfo">
-$if ctx.output or ctx.headers:
-    <h2>Response so far</h2>
-    <h3>HEADERS</h3>
-    $:dicttable_items(ctx.headers)
-
-    <h3>BODY</h3>
-    <p class="req" style="padding-bottom: 2em"><code>
-    $ctx.output
-    </code></p>
-  
-<h2>Request information</h2>
-
-<h3>INPUT</h3>
-$:dicttable(web.input())
-
-<h3 id="cookie-info">COOKIES</h3>
-$:dicttable(web.cookies())
-
-<h3 id="meta-info">META</h3>
-$ newctx = [(k, v) for (k, v) in ctx.iteritems() if not k.startswith('_') and not isinstance(v, dict)]
-$:dicttable(dict(newctx))
-
-<h3 id="meta-info">ENVIRONMENT</h3>
-$:dicttable(ctx.env)
-</div>
-
-<div id="explanation">
-  <p>
-    You're seeing this error because you have <code>web.config.debug</code>
-    set to <code>True</code>. Set that to <code>False</code> if you don't to see this.
-  </p>
-</div>
-
-</body>
-</html>
-"""
-
-djangoerror_r = None
-
-def djangoerror():
-    def _get_lines_from_file(filename, lineno, context_lines):
-        """
-        Returns context_lines before and after lineno from file.
-        Returns (pre_context_lineno, pre_context, context_line, post_context).
-        """
-        try:
-            source = open(filename).readlines()
-            lower_bound = max(0, lineno - context_lines)
-            upper_bound = lineno + context_lines
-
-            pre_context = \
-                [line.strip('\n') for line in source[lower_bound:lineno]]
-            context_line = source[lineno].strip('\n')
-            post_context = \
-                [line.strip('\n') for line in source[lineno + 1:upper_bound]]
-
-            return lower_bound, pre_context, context_line, post_context
-        except (OSError, IOError):
-            return None, [], None, []    
-    
-    exception_type, exception_value, tback = sys.exc_info()
-    frames = []
-    while tback is not None:
-        filename = tback.tb_frame.f_code.co_filename
-        function = tback.tb_frame.f_code.co_name
-        lineno = tback.tb_lineno - 1
-        pre_context_lineno, pre_context, context_line, post_context = \
-            _get_lines_from_file(filename, lineno, 7)
-        frames.append(web.storage({
-            'tback': tback,
-            'filename': filename,
-            'function': function,
-            'lineno': lineno,
-            'vars': tback.tb_frame.f_locals,
-            'id': id(tback),
-            'pre_context': pre_context,
-            'context_line': context_line,
-            'post_context': post_context,
-            'pre_context_lineno': pre_context_lineno,
-        }))
-        tback = tback.tb_next
-    frames.reverse()
-    urljoin = urlparse.urljoin
-    def prettify(x):
-        try: 
-            out = pprint.pformat(x)
-        except Exception, e: 
-            out = '[could not display: <' + e.__class__.__name__ + \
-                  ': '+str(e)+'>]'
-        return out
-        
-    global djangoerror_r
-    if djangoerror_r is None:
-        djangoerror_r = Template(djangoerror_t, filename=__file__, filter=websafe)
-        
-    t = djangoerror_r
-    globals = {'ctx': web.ctx, 'web':web, 'dict':dict, 'str':str, 'prettify': prettify}
-    t.t.func_globals.update(globals)
-    return t(exception_type, exception_value, frames)
-
-def debugerror():
-    """
-    A replacement for `internalerror` that presents a nice page with lots
-    of debug information for the programmer.
-
-    (Based on the beautiful 500 page from [Django](http://djangoproject.com/), 
-    designed by [Wilson Miner](http://wilsonminer.com/).)
-    """
-    return web._InternalError(djangoerror())
-
-def emailerrors(to_address, olderror, from_address=None):
-    """
-    Wraps the old `internalerror` handler (pass as `olderror`) to 
-    additionally email all errors to `to_address`, to aid in
-    debugging production websites.
-    
-    Emails contain a normal text traceback as well as an
-    attachment containing the nice `debugerror` page.
-    """
-    from_address = from_address or to_address
-
-    def emailerrors_internal():
-        error = olderror()
-        tb = sys.exc_info()
-        error_name = tb[0]
-        error_value = tb[1]
-        tb_txt = ''.join(traceback.format_exception(*tb))
-        path = web.ctx.path
-        request = web.ctx.method + ' ' + web.ctx.home + web.ctx.fullpath
-        text = ("""\
-------here----
-Content-Type: text/plain
-Content-Disposition: inline
-
-%(request)s
-
-%(tb_txt)s
-
-------here----
-Content-Type: text/html; name="bug.html"
-Content-Disposition: attachment; filename="bug.html"
-
-""" % locals()) + str(djangoerror())
-        sendmail(
-          "your buggy site <%s>" % from_address,
-          "the bugfixer <%s>" % to_address,
-          "bug: %(error_name)s: %(error_value)s (%(path)s)" % locals(),
-          text, 
-          headers={'Content-Type': 'multipart/mixed; boundary="----here----"'})
-        return error
-    
-    return emailerrors_internal
-
-if __name__ == "__main__":
-    urls = (
-        '/', 'index'
-    )
-    from application import application
-    app = application(urls, globals())
-    app.internalerror = debugerror
-    
-    class index:
-        def GET(self):
-            thisdoesnotexist
-
-    app.run()
--- a/bundled/webpy/web/form.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-"""
-HTML forms
-(part of web.py)
-"""
-
-import copy, re
-import webapi as web
-import utils, net
-
-def attrget(obj, attr, value=None):
-    if hasattr(obj, 'has_key') and obj.has_key(attr): return obj[attr]
-    if hasattr(obj, attr): return getattr(obj, attr)
-    return value
-
-class Form:
-    r"""
-    HTML form.
-    
-        >>> f = Form(Textbox("x"))
-        >>> f.render()
-        '<table>\n    <tr><th><label for="x">x</label></th><td><input type="text" name="x" id="x" /></td></tr>\n</table>'
-    """
-    def __init__(self, *inputs, **kw):
-        self.inputs = inputs
-        self.valid = True
-        self.note = None
-        self.validators = kw.pop('validators', [])
-
-    def __call__(self, x=None):
-        o = copy.deepcopy(self)
-        if x: o.validates(x)
-        return o
-    
-    def render(self):
-        out = ''
-        out += self.rendernote(self.note)
-        out += '<table>\n'
-        for i in self.inputs:
-            out += '    <tr><th><label for="%s">%s</label></th>' % (i.id, net.websafe(i.description))
-            out += "<td>"+i.pre+i.render()+i.post+"</td></tr>\n"
-        out += "</table>"
-        return out
-        
-    def render_css(self): 
-        out = [] 
-        out.append(self.rendernote(self.note)) 
-        for i in self.inputs: 
-            out.append('<label for="%s">%s</label>' % (i.id, net.websafe(i.description))) 
-            out.append(i.pre) 
-            out.append(i.render()) 
-            out.append(i.post) 
-            out.append('\n') 
-        return ''.join(out) 
-        
-    def rendernote(self, note):
-        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
-        else: return ""
-    
-    def validates(self, source=None, _validate=True, **kw):
-        source = source or kw or web.input()
-        out = True
-        for i in self.inputs:
-            v = attrget(source, i.name)
-            if _validate:
-                out = i.validate(v) and out
-            else:
-                i.value = v
-        if _validate:
-            out = out and self._validate(source)
-            self.valid = out
-        return out
-
-    def _validate(self, value):
-        self.value = value
-        for v in self.validators:
-            if not v.valid(value):
-                self.note = v.msg
-                return False
-        return True
-
-    def fill(self, source=None, **kw):
-        return self.validates(source, _validate=False, **kw)
-    
-    def __getitem__(self, i):
-        for x in self.inputs:
-            if x.name == i: return x
-        raise KeyError, i
-
-    def __getattr__(self, name):
-        # don't interfere with deepcopy
-        inputs = self.__dict__.get('inputs') or []
-        for x in inputs:
-            if x.name == name: return x
-        raise AttributeError, name
-    
-    def get(self, i, default=None):
-        try:
-            return self[i]
-        except KeyError:
-            return default
-            
-    def _get_d(self): #@@ should really be form.attr, no?
-        return utils.storage([(i.name, i.value) for i in self.inputs])
-    d = property(_get_d)
-
-class Input(object):
-    def __init__(self, name, *validators, **attrs):
-        self.description = attrs.pop('description', name)
-        self.value = attrs.pop('value', None)
-        self.pre = attrs.pop('pre', "")
-        self.post = attrs.pop('post', "")
-        self.id = attrs.setdefault('id', name)
-        if 'class_' in attrs:
-            attrs['class'] = attrs['class_']
-            del attrs['class_']
-        self.name, self.validators, self.attrs, self.note = name, validators, attrs, None
-
-    def validate(self, value):
-        self.value = value
-        for v in self.validators:
-            if not v.valid(value):
-                self.note = v.msg
-                return False
-        return True
-
-    def render(self): raise NotImplementedError
-
-    def rendernote(self, note):
-        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
-        else: return ""
-        
-    def addatts(self):
-        str = ""
-        for (n, v) in self.attrs.items():
-            str += ' %s="%s"' % (n, net.websafe(v))
-        return str
-    
-#@@ quoting
-
-class Textbox(Input):
-    def render(self, shownote=True):
-        x = '<input type="text" name="%s"' % net.websafe(self.name)
-        if self.value: x += ' value="%s"' % net.websafe(self.value)
-        x += self.addatts()
-        x += ' />'
-        if shownote:
-            x += self.rendernote(self.note)
-        return x
-
-class Password(Input):
-    def render(self):
-        x = '<input type="password" name="%s"' % net.websafe(self.name)
-        if self.value: x += ' value="%s"' % net.websafe(self.value)
-        x += self.addatts()
-        x += ' />'
-        x += self.rendernote(self.note)
-        return x
-
-class Textarea(Input):
-    def render(self):
-        x = '<textarea name="%s"' % net.websafe(self.name)
-        x += self.addatts()
-        x += '>'
-        if self.value is not None: x += net.websafe(self.value)
-        x += '</textarea>'
-        x += self.rendernote(self.note)
-        return x
-
-class Dropdown(Input):
-    def __init__(self, name, args, *validators, **attrs):
-        self.args = args
-        super(Dropdown, self).__init__(name, *validators, **attrs)
-
-    def render(self):
-        x = '<select name="%s"%s>\n' % (net.websafe(self.name), self.addatts())
-        for arg in self.args:
-            if isinstance(arg, (tuple, list)):
-                value, desc= arg
-            else:
-                value, desc = arg, arg 
-
-            if self.value == value: select_p = ' selected="selected"'
-            else: select_p = ''
-            x += '  <option %s value="%s">%s</option>\n' % (select_p, net.websafe(value), net.websafe(desc))
-        x += '</select>\n'
-        x += self.rendernote(self.note)
-        return x
-
-class Radio(Input):
-    def __init__(self, name, args, *validators, **attrs):
-        self.args = args
-        super(Radio, self).__init__(name, *validators, **attrs)
-
-    def render(self):
-        x = '<span>'
-        for arg in self.args:
-            if self.value == arg: select_p = ' checked="checked"'
-            else: select_p = ''
-            x += '<input type="radio" name="%s" value="%s"%s%s /> %s ' % (net.websafe(self.name), net.websafe(arg), select_p, self.addatts(), net.websafe(arg))
-            x += '</span>'
-            x += self.rendernote(self.note)    
-        return x
-
-class Checkbox(Input):
-    def render(self):
-        x = '<input name="%s" type="checkbox"' % net.websafe(self.name)
-        if self.value: x += ' checked="checked"'
-        x += self.addatts()
-        x += ' />'
-        x += self.rendernote(self.note)
-        return x
-
-class Button(Input):
-    def __init__(self, name, *validators, **attrs):
-        super(Button, self).__init__(name, *validators, **attrs)
-        self.description = ""
-
-    def render(self):
-        safename = net.websafe(self.name)
-        x = '<button name="%s"%s>%s</button>' % (safename, self.addatts(), safename)
-        x += self.rendernote(self.note)
-        return x
-
-class Hidden(Input):
-    def __init__(self, name, *validators, **attrs):
-        super(Hidden, self).__init__(name, *validators, **attrs)
-        # it doesnt make sence for a hidden field to have description
-        self.description = ""
-
-    def render(self):
-        x = '<input type="hidden" name="%s"' % net.websafe(self.name)
-        if self.value: x += ' value="%s"' % net.websafe(self.value)
-        x += self.addatts()
-        x += ' />'
-        return x
-
-class File(Input):
-    def render(self):
-        x = '<input type="file" name="%s"' % net.websafe(self.name)
-        x += self.addatts()
-        x += ' />'
-        x += self.rendernote(self.note)
-        return x
-    
-class Validator:
-    def __deepcopy__(self, memo): return copy.copy(self)
-    def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
-    def valid(self, value): 
-        try: return self.test(value)
-        except: return False
-
-notnull = Validator("Required", bool)
-
-class regexp(Validator):
-    def __init__(self, rexp, msg):
-        self.rexp = re.compile(rexp)
-        self.msg = msg
-    
-    def valid(self, value):
-        return bool(self.rexp.match(value))
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
--- a/bundled/webpy/web/http.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-"""
-HTTP Utilities
-(from web.py)
-"""
-
-__all__ = [
-  "expires", "lastmodified", 
-  "prefixurl", "modified", 
-  "write",
-  "changequery", "url",
-  "profiler",
-]
-
-import sys, os, threading, urllib, urlparse
-try: import datetime
-except ImportError: pass
-import net, utils, webapi as web
-
-def prefixurl(base=''):
-    """
-    Sorry, this function is really difficult to explain.
-    Maybe some other time.
-    """
-    url = web.ctx.path.lstrip('/')
-    for i in xrange(url.count('/')): 
-        base += '../'
-    if not base: 
-        base = './'
-    return base
-
-def expires(delta):
-    """
-    Outputs an `Expires` header for `delta` from now. 
-    `delta` is a `timedelta` object or a number of seconds.
-    """
-    if isinstance(delta, (int, long)):
-        delta = datetime.timedelta(seconds=delta)
-    date_obj = datetime.datetime.utcnow() + delta
-    web.header('Expires', net.httpdate(date_obj))
-
-def lastmodified(date_obj):
-    """Outputs a `Last-Modified` header for `datetime`."""
-    web.header('Last-Modified', net.httpdate(date_obj))
-
-def modified(date=None, etag=None):
-    """
-    Checks to see if the page has been modified since the version in the
-    requester's cache.
-    
-    When you publish pages, you can include `Last-Modified` and `ETag`
-    with the date the page was last modified and an opaque token for
-    the particular version, respectively. When readers reload the page, 
-    the browser sends along the modification date and etag value for
-    the version it has in its cache. If the page hasn't changed, 
-    the server can just return `304 Not Modified` and not have to 
-    send the whole page again.
-    
-    This function takes the last-modified date `date` and the ETag `etag`
-    and checks the headers to see if they match. If they do, it returns 
-    `True` and sets the response status to `304 Not Modified`. It also
-    sets `Last-Modified and `ETag` output headers.
-    """
-    try:
-        from __builtin__ import set
-    except ImportError:
-        # for python 2.3
-        from sets import Set as set
-
-    n = set([x.strip('" ') for x in web.ctx.env.get('HTTP_IF_NONE_MATCH', '').split(',')])
-    m = net.parsehttpdate(web.ctx.env.get('HTTP_IF_MODIFIED_SINCE', '').split(';')[0])
-    validate = False
-    if etag:
-        if '*' in n or etag in n:
-            validate = True
-    if date and m:
-        # we subtract a second because 
-        # HTTP dates don't have sub-second precision
-        if date-datetime.timedelta(seconds=1) <= m:
-            validate = True
-    
-    if validate: web.ctx.status = '304 Not Modified'
-    if date: lastmodified(date)
-    if etag: web.header('ETag', '"' + etag + '"')
-    return not validate
-
-def write(cgi_response):
-    """
-    Converts a standard CGI-style string response into `header` and 
-    `output` calls.
-    """
-    cgi_response = str(cgi_response)
-    cgi_response.replace('\r\n', '\n')
-    head, body = cgi_response.split('\n\n', 1)
-    lines = head.split('\n')
-
-    for line in lines:
-        if line.isspace(): 
-            continue
-        hdr, value = line.split(":", 1)
-        value = value.strip()
-        if hdr.lower() == "status": 
-            web.ctx.status = value
-        else: 
-            web.header(hdr, value)
-
-    web.output(body)
-
-def urlencode(query):
-    """
-    Same as urllib.urlencode, but supports unicode strings.
-    
-        >>> urlencode({'text':'foo bar'})
-        'text=foo+bar'
-    """
-    query = dict([(k, utils.utf8(v)) for k, v in query.items()])
-    return urllib.urlencode(query)
-
-def changequery(query=None, **kw):
-    """
-    Imagine you're at `/foo?a=1&b=2`. Then `changequery(a=3)` will return
-    `/foo?a=3&b=2` -- the same URL but with the arguments you requested
-    changed.
-    """
-    if query is None:
-        query = web.input(_method='get')
-    for k, v in kw.iteritems():
-        if v is None:
-            query.pop(k, None)
-        else:
-            query[k] = v
-    out = web.ctx.path
-    if query:
-        out += '?' + urlencode(query)
-    return out
-
-def url(path=None, **kw):
-    """
-    Makes url by concatinating web.ctx.homepath and path and the 
-    query string created using the arguments.
-    """
-    if path is None:
-        path = web.ctx.path
-    if path.startswith("/"):
-        out = web.ctx.homepath + path
-    else:
-        out = path
-
-    if kw:
-        out += '?' + urlencode(kw)
-    
-    return out
-
-def profiler(app):
-    """Outputs basic profiling information at the bottom of each response."""
-    from utils import profile
-    def profile_internal(e, o):
-        out, result = profile(app)(e, o)
-        return list(out) + ['<pre>' + net.websafe(result) + '</pre>']
-    return profile_internal
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
--- a/bundled/webpy/web/httpserver.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-__all__ = ["runsimple"]
-
-import sys, os
-import webapi as web
-import net
-import utils
-
-def runbasic(func, server_address=("0.0.0.0", 8080)):
-    """
-    Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` 
-    is hosted statically.
-
-    Based on [WsgiServer][ws] from [Colin Stewart][cs].
-    
-  [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html
-  [cs]: http://www.owlfish.com/
-    """
-    # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
-    # Modified somewhat for simplicity
-    # Used under the modified BSD license:
-    # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
-
-    import SimpleHTTPServer, SocketServer, BaseHTTPServer, urlparse
-    import socket, errno
-    import traceback
-
-    class WSGIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
-        def run_wsgi_app(self):
-            protocol, host, path, parameters, query, fragment = \
-                urlparse.urlparse('http://dummyhost%s' % self.path)
-
-            # we only use path, query
-            env = {'wsgi.version': (1, 0)
-                   ,'wsgi.url_scheme': 'http'
-                   ,'wsgi.input': self.rfile
-                   ,'wsgi.errors': sys.stderr
-                   ,'wsgi.multithread': 1
-                   ,'wsgi.multiprocess': 0
-                   ,'wsgi.run_once': 0
-                   ,'REQUEST_METHOD': self.command
-                   ,'REQUEST_URI': self.path
-                   ,'PATH_INFO': path
-                   ,'QUERY_STRING': query
-                   ,'CONTENT_TYPE': self.headers.get('Content-Type', '')
-                   ,'CONTENT_LENGTH': self.headers.get('Content-Length', '')
-                   ,'REMOTE_ADDR': self.client_address[0]
-                   ,'SERVER_NAME': self.server.server_address[0]
-                   ,'SERVER_PORT': str(self.server.server_address[1])
-                   ,'SERVER_PROTOCOL': self.request_version
-                   }
-
-            for http_header, http_value in self.headers.items():
-                env ['HTTP_%s' % http_header.replace('-', '_').upper()] = \
-                    http_value
-
-            # Setup the state
-            self.wsgi_sent_headers = 0
-            self.wsgi_headers = []
-
-            try:
-                # We have there environment, now invoke the application
-                result = self.server.app(env, self.wsgi_start_response)
-                try:
-                    try:
-                        for data in result:
-                            if data: 
-                                self.wsgi_write_data(data)
-                    finally:
-                        if hasattr(result, 'close'): 
-                            result.close()
-                except socket.error, socket_err:
-                    # Catch common network errors and suppress them
-                    if (socket_err.args[0] in \
-                       (errno.ECONNABORTED, errno.EPIPE)): 
-                        return
-                except socket.timeout, socket_timeout: 
-                    return
-            except:
-                print >> web.debug, traceback.format_exc(),
-
-            if (not self.wsgi_sent_headers):
-                # We must write out something!
-                self.wsgi_write_data(" ")
-            return
-
-        do_POST = run_wsgi_app
-        do_PUT = run_wsgi_app
-        do_DELETE = run_wsgi_app
-
-        def do_GET(self):
-            if self.path.startswith('/static/'):
-                SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
-            else:
-                self.run_wsgi_app()
-
-        def wsgi_start_response(self, response_status, response_headers, 
-                              exc_info=None):
-            if (self.wsgi_sent_headers):
-                raise Exception \
-                      ("Headers already sent and start_response called again!")
-            # Should really take a copy to avoid changes in the application....
-            self.wsgi_headers = (response_status, response_headers)
-            return self.wsgi_write_data
-
-        def wsgi_write_data(self, data):
-            if (not self.wsgi_sent_headers):
-                status, headers = self.wsgi_headers
-                # Need to send header prior to data
-                status_code = status[:status.find(' ')]
-                status_msg = status[status.find(' ') + 1:]
-                self.send_response(int(status_code), status_msg)
-                for header, value in headers:
-                    self.send_header(header, value)
-                self.end_headers()
-                self.wsgi_sent_headers = 1
-            # Send the data
-            self.wfile.write(data)
-
-    class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
-        def __init__(self, func, server_address):
-            BaseHTTPServer.HTTPServer.__init__(self, 
-                                               server_address, 
-                                               WSGIHandler)
-            self.app = func
-            self.serverShuttingDown = 0
-
-    print "http://%s:%d/" % server_address
-    WSGIServer(func, server_address).serve_forever()
-
-def runsimple(func, server_address=("0.0.0.0", 8080)):
-    """
-    Runs [CherryPy][cp] WSGI server hosting WSGI app `func`. 
-    The directory `static/` is hosted statically.
-
-    [cp]: http://www.cherrypy.org
-    """
-    from wsgiserver import CherryPyWSGIServer
-    from SimpleHTTPServer import SimpleHTTPRequestHandler
-    from BaseHTTPServer import BaseHTTPRequestHandler
-
-    class StaticApp(SimpleHTTPRequestHandler):
-        """WSGI application for serving static files."""
-        def __init__(self, environ, start_response):
-            self.headers = []
-            self.environ = environ
-            self.start_response = start_response
-
-        def send_response(self, status, msg=""):
-            self.status = str(status) + " " + msg
-
-        def send_header(self, name, value):
-            self.headers.append((name, value))
-
-        def end_headers(self):
-            pass
-
-        def log_message(*a): pass
-
-        def __iter__(self):
-            environ = self.environ
-
-            self.path = environ.get('PATH_INFO', '')
-            self.client_address = environ.get('REMOTE_ADDR','-'), \
-                                  environ.get('REMOTE_PORT','-')
-            self.command = environ.get('REQUEST_METHOD', '-')
-
-            from cStringIO import StringIO
-            self.wfile = StringIO() # for capturing error
-
-            f = self.send_head()
-            self.start_response(self.status, self.headers)
-
-            if f:
-                block_size = 16 * 1024
-                while True:
-                    buf = f.read(block_size)
-                    if not buf:
-                        break
-                    yield buf
-                f.close()
-            else:
-                value = self.wfile.getvalue()
-                yield value
-                    
-    class WSGIWrapper(BaseHTTPRequestHandler):
-        """WSGI wrapper for logging the status and serving static files."""
-        def __init__(self, app):
-            self.app = app
-            self.format = '%s - - [%s] "%s %s %s" - %s'
-
-        def __call__(self, environ, start_response):
-            def xstart_response(status, response_headers, *args):
-                write = start_response(status, response_headers, *args)
-                self.log(status, environ)
-                return write
-
-            path = environ.get('PATH_INFO', '')
-            if path.startswith('/static/'):
-                return StaticApp(environ, xstart_response)
-            else:
-                return self.app(environ, xstart_response)
-
-        def log(self, status, environ):
-            outfile = environ.get('wsgi.errors', web.debug)
-            req = environ.get('PATH_INFO', '_')
-            protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
-            method = environ.get('REQUEST_METHOD', '-')
-            host = "%s:%s" % (environ.get('REMOTE_ADDR','-'), 
-                              environ.get('REMOTE_PORT','-'))
-
-            #@@ It is really bad to extend from 
-            #@@ BaseHTTPRequestHandler just for this method
-            time = self.log_date_time_string()
-
-            msg = self.format % (host, time, protocol, method, req, status)
-            print >> outfile, utils.safestr(msg)
-            
-    func = WSGIWrapper(func)
-    server = CherryPyWSGIServer(server_address, func, server_name="localhost")
-
-    print "http://%s:%d/" % server_address
-    try:
-        server.start()
-    except KeyboardInterrupt:
-        server.stop()
--- a/bundled/webpy/web/net.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-"""
-Network Utilities
-(from web.py)
-"""
-
-__all__ = [
-  "validipaddr", "validipport", "validip", "validaddr", 
-  "urlquote",
-  "httpdate", "parsehttpdate", 
-  "htmlquote", "htmlunquote", "websafe",
-]
-
-import urllib, time
-try: import datetime
-except ImportError: pass
-
-def validipaddr(address):
-    """
-    Returns True if `address` is a valid IPv4 address.
-    
-        >>> validipaddr('192.168.1.1')
-        True
-        >>> validipaddr('192.168.1.800')
-        False
-        >>> validipaddr('192.168.1')
-        False
-    """
-    try:
-        octets = address.split('.')
-        if len(octets) != 4:
-            return False
-        for x in octets:
-            if not (0 <= int(x) <= 255):
-                return False
-    except ValueError:
-        return False
-    return True
-
-def validipport(port):
-    """
-    Returns True if `port` is a valid IPv4 port.
-    
-        >>> validipport('9000')
-        True
-        >>> validipport('foo')
-        False
-        >>> validipport('1000000')
-        False
-    """
-    try:
-        if not (0 <= int(port) <= 65535):
-            return False
-    except ValueError:
-        return False
-    return True
-
-def validip(ip, defaultaddr="0.0.0.0", defaultport=8080):
-    """Returns `(ip_address, port)` from string `ip_addr_port`"""
-    addr = defaultaddr
-    port = defaultport
-    
-    ip = ip.split(":", 1)
-    if len(ip) == 1:
-        if not ip[0]:
-            pass
-        elif validipaddr(ip[0]):
-            addr = ip[0]
-        elif validipport(ip[0]):
-            port = int(ip[0])
-        else:
-            raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
-    elif len(ip) == 2:
-        addr, port = ip
-        if not validipaddr(addr) and validipport(port):
-            raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
-        port = int(port)
-    else:
-        raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
-    return (addr, port)
-
-def validaddr(string_):
-    """
-    Returns either (ip_address, port) or "/path/to/socket" from string_
-    
-        >>> validaddr('/path/to/socket')
-        '/path/to/socket'
-        >>> validaddr('8000')
-        ('0.0.0.0', 8000)
-        >>> validaddr('127.0.0.1')
-        ('127.0.0.1', 8080)
-        >>> validaddr('127.0.0.1:8000')
-        ('127.0.0.1', 8000)
-        >>> validaddr('fff')
-        Traceback (most recent call last):
-            ...
-        ValueError: fff is not a valid IP address/port
-    """
-    if '/' in string_:
-        return string_
-    else:
-        return validip(string_)
-
-def urlquote(val):
-    """
-    Quotes a string for use in a URL.
-    
-        >>> urlquote('://?f=1&j=1')
-        '%3A//%3Ff%3D1%26j%3D1'
-        >>> urlquote(None)
-        ''
-        >>> urlquote(u'\u203d')
-        '%E2%80%BD'
-    """
-    if val is None: return ''
-    if not isinstance(val, unicode): val = str(val)
-    else: val = val.encode('utf-8')
-    return urllib.quote(val)
-
-def httpdate(date_obj):
-    """
-    Formats a datetime object for use in HTTP headers.
-    
-        >>> import datetime
-        >>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1))
-        'Thu, 01 Jan 1970 01:01:01 GMT'
-    """
-    return date_obj.strftime("%a, %d %b %Y %H:%M:%S GMT")
-
-def parsehttpdate(string_):
-    """
-    Parses an HTTP date into a datetime object.
-
-        >>> parsehttpdate('Thu, 01 Jan 1970 01:01:01 GMT')
-        datetime.datetime(1970, 1, 1, 1, 1, 1)
-    """
-    try:
-        t = time.strptime(string_, "%a, %d %b %Y %H:%M:%S %Z")
-    except ValueError:
-        return None
-    return datetime.datetime(*t[:6])
-
-def htmlquote(text):
-    """
-    Encodes `text` for raw use in HTML.
-    
-        >>> htmlquote("<'&\\">")
-        '&lt;&#39;&amp;&quot;&gt;'
-    """
-    text = text.replace("&", "&amp;") # Must be done first!
-    text = text.replace("<", "&lt;")
-    text = text.replace(">", "&gt;")
-    text = text.replace("'", "&#39;")
-    text = text.replace('"', "&quot;")
-    return text
-
-def htmlunquote(text):
-    """
-    Decodes `text` that's HTML quoted.
-
-        >>> htmlunquote('&lt;&#39;&amp;&quot;&gt;')
-        '<\\'&">'
-    """
-    text = text.replace("&quot;", '"')
-    text = text.replace("&#39;", "'")
-    text = text.replace("&gt;", ">")
-    text = text.replace("&lt;", "<")
-    text = text.replace("&amp;", "&") # Must be done last!
-    return text
-
-def websafe(val):
-    """
-    Converts `val` so that it's safe for use in UTF-8 HTML.
-    
-        >>> websafe("<'&\\">")
-        '&lt;&#39;&amp;&quot;&gt;'
-        >>> websafe(None)
-        ''
-        >>> websafe(u'\u203d')
-        '\\xe2\\x80\\xbd'
-    """
-    if val is None:
-        return ''
-    if isinstance(val, unicode):
-        val = val.encode('utf-8')
-    val = str(val)
-    return htmlquote(val)
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
--- a/bundled/webpy/web/session.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,319 +0,0 @@
-"""
-Session Management
-(from web.py)
-"""
-
-import os, time, datetime, random, base64
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-try:
-    import hashlib
-    sha1 = hashlib.sha1
-except ImportError:
-    import sha
-    sha1 = sha.new
-
-import utils
-import webapi as web
-
-__all__ = [
-    'Session', 'SessionExpired',
-    'Store', 'DiskStore', 'DBStore',
-]
-
-web.config.session_parameters = utils.storage({
-    'cookie_name': 'webpy_session_id',
-    'cookie_domain': None,
-    'timeout': 86400, #24 * 60 * 60, # 24 hours in seconds
-    'ignore_expiry': True,
-    'ignore_change_ip': True,
-    'secret_key': 'fLjUfxqXtfNoIldA0A0J',
-    'expired_message': 'Session expired',
-})
-
-class SessionExpired(web.HTTPError): 
-    def __init__(self, message):
-        web.HTTPError.__init__(self, '200 OK', {}, data=message)
-
-class Session(utils.ThreadedDict):
-    """Session management for web.py
-    """
-
-    def __init__(self, app, store, initializer=None):
-        self.__dict__['store'] = store
-        self.__dict__['_initializer'] = initializer
-        self.__dict__['_last_cleanup_time'] = 0
-        self.__dict__['_config'] = utils.storage(web.config.session_parameters)
-
-        if app:
-            app.add_processor(self._processor)
-
-    def _processor(self, handler):
-        """Application processor to setup session for every request"""
-        self._cleanup()
-        self._load()
-
-        try:
-            return handler()
-        finally:
-            self._save()
-
-    def _load(self):
-        """Load the session from the store, by the id from cookie"""
-        cookie_name = self._config.cookie_name
-        cookie_domain = self._config.cookie_domain
-        self.session_id = web.cookies().get(cookie_name)
-
-        # protection against session_id tampering
-        if self.session_id and not self._valid_session_id(self.session_id):
-            self.session_id = None
-
-        self._check_expiry()
-        if self.session_id:
-            d = self.store[self.session_id]
-            self.update(d)
-            self._validate_ip()
-        
-        if not self.session_id:
-            self.session_id = self._generate_session_id()
-
-            if self._initializer:
-                if isinstance(self._initializer, dict):
-                    self.update(self._initializer)
-                elif hasattr(self._initializer, '__call__'):
-                    self._initializer()
- 
-        self.ip = web.ctx.ip
-
-    def _check_expiry(self):
-        # check for expiry
-        if self.session_id and self.session_id not in self.store:
-            if self._config.ignore_expiry:
-                self.session_id = None
-            else:
-                return self.expired()
-
-    def _validate_ip(self):
-        # check for change of IP
-        if self.session_id and self.get('ip', None) != web.ctx.ip:
-            if not self._config.ignore_change_ip:
-               return self.expired() 
-    
-    def _save(self):
-        cookie_name = self._config.cookie_name
-        cookie_domain = self._config.cookie_domain
-        if not self.get('_killed'):
-            web.setcookie(cookie_name, self.session_id, domain=cookie_domain)
-            self.store[self.session_id] = dict(self)
-        else:
-            web.setcookie(cookie_name, self.session_id, expires=-1, domain=cookie_domain)
-    
-    def _generate_session_id(self):
-        """Generate a random id for session"""
-
-        while True:
-            rand = os.urandom(16)
-            now = time.time()
-            secret_key = self._config.secret_key
-            session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key))
-            session_id = session_id.hexdigest()
-            if session_id not in self.store:
-                break
-        return session_id
-
-    def _valid_session_id(self, session_id):
-        rx = utils.re_compile('^[0-9a-fA-F]+$')
-        return rx.match(session_id)
-        
-    def _cleanup(self):
-        """Cleanup the stored sessions"""
-        current_time = time.time()
-        timeout = self._config.timeout
-        if current_time - self._last_cleanup_time > timeout:
-            self.store.cleanup(timeout)
-            self.__dict__['_last_cleanup_time'] = current_time
-
-    def expired(self):
-        """Called when an expired session is atime"""
-        self._killed = True
-        self._save()
-        raise SessionExpired(self._config.expired_message)
- 
-    def kill(self):
-        """Kill the session, make it no longer available"""
-        del self.store[self.session_id]
-        self._killed = True
-
-class Store:
-    """Base class for session stores"""
-
-    def __contains__(self, key):
-        raise NotImplementedError
-
-    def __getitem__(self, key):
-        raise NotImplementedError
-
-    def __setitem__(self, key, value):
-        raise NotImplementedError
-
-    def cleanup(self, timeout):
-        """removes all the expired sessions"""
-        raise NotImplementedError
-
-    def encode(self, session_dict):
-        """encodes session dict as a string"""
-        pickled = pickle.dumps(session_dict)
-        return base64.encodestring(pickled)
-
-    def decode(self, session_data):
-        """decodes the data to get back the session dict """
-        pickled = base64.decodestring(session_data)
-        return pickle.loads(pickled)
-
-class DiskStore(Store):
-    """
-    Store for saving a session on disk.
-
-        >>> import tempfile
-        >>> root = tempfile.mkdtemp()
-        >>> s = DiskStore(root)
-        >>> s['a'] = 'foo'
-        >>> s['a']
-        'foo'
-        >>> time.sleep(0.01)
-        >>> s.cleanup(0.01)
-        >>> s['a']
-        Traceback (most recent call last):
-            ...
-        KeyError: 'a'
-    """
-    def __init__(self, root):
-        # if the storage root doesn't exists, create it.
-        if not os.path.exists(root):
-            os.mkdir(root)
-        self.root = root
-
-    def _get_path(self, key):
-        if os.path.sep in key: 
-            raise ValueError, "Bad key: %s" % repr(key)
-        return os.path.join(self.root, key)
-    
-    def __contains__(self, key):
-        path = self._get_path(key)
-        return os.path.exists(path)
-
-    def __getitem__(self, key):
-        path = self._get_path(key)
-        if os.path.exists(path): 
-            pickled = open(path).read()
-            return self.decode(pickled)
-        else:
-            raise KeyError, key
-
-    def __setitem__(self, key, value):
-        path = self._get_path(key)
-        pickled = self.encode(value)    
-        try:
-            f = open(path, 'w')
-            try:
-                f.write(pickled)
-            finally: 
-                f.close()
-        except IOError:
-            pass
-
-    def __delitem__(self, key):
-        path = self._get_path(key)
-        if os.path.exists(path):
-            os.remove(path)
-    
-    def cleanup(self, timeout):
-        now = time.time()
-        for f in os.listdir(self.root):
-            path = self._get_path(f)
-            atime = os.stat(path).st_atime
-            if now - atime > timeout :
-                os.remove(path)
-
-class DBStore(Store):
-    """Store for saving a session in database
-    Needs a table with the following columns:
-
-        session_id CHAR(128) UNIQUE NOT NULL,
-        atime DATETIME NOT NULL default current_timestamp,
-        data TEXT
-    """
-    def __init__(self, db, table_name):
-        self.db = db
-        self.table = table_name
-    
-    def __contains__(self, key):
-        data = self.db.select(self.table, where="session_id=$key", vars=locals())
-        return bool(list(data)) 
-
-    def __getitem__(self, key):
-        now = datetime.datetime.now()
-        try:
-            s = self.db.select(self.table, where="session_id=$key", vars=locals())[0]
-            self.db.update(self.table, where="session_id=$key", atime=now, vars=locals())
-        except IndexError:
-            raise KeyError
-        else:
-            return self.decode(s.data)
-
-    def __setitem__(self, key, value):
-        pickled = self.encode(value)
-        now = datetime.datetime.now()
-        if key in self:
-            self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals())
-        else:
-            self.db.insert(self.table, False, session_id=key, data=pickled )
-                
-    def __delitem__(self, key):
-        self.db.delete(self.table, where="session_id=$key", vars=locals())
-
-    def cleanup(self, timeout):
-        timeout = datetime.timedelta(timeout/(24.0*60*60)) #timedelta takes numdays as arg
-        last_allowed_time = datetime.datetime.now() - timeout
-        self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals())
-
-class ShelfStore:
-    """Store for saving session using `shelve` module.
-
-        import shelve
-        store = ShelfStore(shelve.open('session.shelf'))
-
-    XXX: is shelve thread-safe?
-    """
-    def __init__(self, shelf):
-        self.shelf = shelf
-
-    def __contains__(self, key):
-        return key in self.shelf
-
-    def __getitem__(self, key):
-        atime, v = self.shelf[key]
-        self[key] = v # update atime
-        return v
-
-    def __setitem__(self, key, value):
-        self.shelf[key] = time.time(), value
-        
-    def __delitem__(self, key):
-        try:
-            del self.shelf[key]
-        except KeyError:
-            pass
-
-    def cleanup(self, timeout):
-        now = time.time()
-        for k in self.shelf.keys():
-            atime, v = self.shelf[k]
-            if now - atime > timeout :
-                del self[k]
-
-if __name__ == '__main__' :
-    import doctest
-    doctest.testmod()
--- a/bundled/webpy/web/template.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1412 +0,0 @@
-"""
-simple, elegant templating
-(part of web.py)
-
-Template design:
-
-Template string is split into tokens and the tokens are combined into nodes. 
-Parse tree is a nodelist. TextNode and ExpressionNode are simple nodes and 
-for-loop, if-loop etc are block nodes, which contain multiple child nodes. 
-
-Each node can emit some python string. python string emitted by the 
-root node is validated for safeeval and executed using python in the given environment.
-
-Enough care is taken to make sure the generated code and the template has line to line match, 
-so that the error messages can point to exact line number in template. (It doesn't work in some cases still.)
-
-Grammar:
-
-    template -> defwith sections 
-    defwith -> '$def with (' arguments ')' | ''
-    sections -> section*
-    section -> block | assignment | line
-
-    assignment -> '$ ' <assignment expression>
-    line -> (text|expr)*
-    text -> <any characters other than $>
-    expr -> '$' pyexpr | '$(' pyexpr ')' | '${' pyexpr '}'
-    pyexpr -> <python expression>
-
-"""
-
-__all__ = [
-    "Template",
-    "Render", "render", "frender",
-    "ParseError", "SecurityError",
-    "test"
-]
-
-import tokenize
-import os
-import glob
-import re
-
-from utils import storage, safeunicode, safestr, re_compile
-from webapi import config
-from net import websafe
-
-def splitline(text):
-    r"""
-    Splits the given text at newline.
-    
-        >>> splitline('foo\nbar')
-        ('foo\n', 'bar')
-        >>> splitline('foo')
-        ('foo', '')
-        >>> splitline('')
-        ('', '')
-    """
-    index = text.find('\n') + 1
-    if index:
-        return text[:index], text[index:]
-    else:
-        return text, ''
-
-class Parser:
-    """Parser Base.
-    """
-    def __init__(self, text, name="<template>"):
-        self.text = text
-        self.name = name
-
-    def parse(self):
-        text = self.text
-        defwith, text = self.read_defwith(text)
-        suite = self.read_suite(text)
-        return DefwithNode(defwith, suite)
-
-    def read_defwith(self, text):
-        if text.startswith('$def with'):
-            defwith, text = splitline(text)
-            defwith = defwith[1:].strip() # strip $ and spaces
-            return defwith, text
-        else:
-            return '', text
-    
-    def read_section(self, text):
-        r"""Reads one section from the given text.
-        
-        section -> block | assignment | line
-        
-            >>> read_section = Parser('').read_section
-            >>> read_section('foo\nbar\n')
-            (<line: [t'foo\n']>, 'bar\n')
-            >>> read_section('$ a = b + 1\nfoo\n')
-            (<assignment: 'a = b + 1'>, 'foo\n')
-            
-        read_section('$for in range(10):\n    hello $i\nfoo)
-        """
-        if text.lstrip(' ').startswith('$'):
-            index = text.index('$')
-            begin_indent, text2 = text[:index], text[index+1:]
-            ahead = self.python_lookahead(text2)
-            
-            if ahead == 'var':
-                return self.read_var(text2)
-            elif ahead in STATEMENT_NODES:
-                return self.read_block_section(text2, begin_indent)
-            elif ahead in KEYWORDS:
-                return self.read_keyword(text2)
-            elif ahead.strip() == '':
-                # assignments starts with a space after $
-                # ex: $ a = b + 2
-                return self.read_assignment(text2)
-        return self.readline(text)
-        
-    def read_var(self, text):
-        r"""Reads a var statement.
-        
-            >>> read_var = Parser('').read_var
-            >>> read_var('var x=10\nfoo')
-            (<var: x = 10>, 'foo')
-            >>> read_var('var x: hello $name\nfoo')
-            (<var: x = join_('hello ', escape_(name, True))>, 'foo')
-        """
-        line, text = splitline(text)
-        tokens = self.python_tokens(line)
-        if len(tokens) < 4:
-            raise SyntaxError('Invalid var statement')
-            
-        name = tokens[1]
-        sep = tokens[2]
-        value = line.split(sep, 1)[1].strip()
-        
-        if sep == '=':
-            pass # no need to process value
-        elif sep == ':': 
-            #@@ Hack for backward-compatability
-            if tokens[3] == '\n': # multi-line var statement
-                block, text = self.read_indented_block(text, '    ')
-                lines = [self.readline(x)[0] for x in block.splitlines()]
-                nodes = []
-                for x in lines:
-                    nodes.extend(x.nodes)
-                    nodes.append(TextNode('\n'))         
-            else: # single-line var statement
-                linenode, _ = self.readline(value)
-                nodes = linenode.nodes                
-            parts = [node.emit('') for node in nodes]
-            value = "join_(%s)" % ", ".join(parts)
-        else:
-            raise SyntaxError('Invalid var statement')
-        return VarNode(name, value), text
-                    
-    def read_suite(self, text):
-        r"""Reads section by section till end of text.
-        
-            >>> read_suite = Parser('').read_suite
-            >>> read_suite('hello $name\nfoo\n')
-            [<line: [t'hello ', $name, t'\n']>, <line: [t'foo\n']>]
-        """
-        sections = []
-        while text:
-            section, text = self.read_section(text)
-            sections.append(section)
-        return SuiteNode(sections)
-    
-    def readline(self, text):
-        r"""Reads one line from the text. Newline is supressed if the line ends with \.
-        
-            >>> readline = Parser('').readline
-            >>> readline('hello $name!\nbye!')
-            (<line: [t'hello ', $name, t'!\n']>, 'bye!')
-            >>> readline('hello $name!\\\nbye!')
-            (<line: [t'hello ', $name, t'!']>, 'bye!')
-            >>> readline('$f()\n\n')
-            (<line: [$f(), t'\n']>, '\n')
-        """
-        line, text = splitline(text)
-
-        # supress new line if line ends with \
-        if line.endswith('\\\n'):
-            line = line[:-2]
-                
-        nodes = []
-        while line:
-            node, line = self.read_node(line)
-            nodes.append(node)
-            
-        return LineNode(nodes), text
-
-    def read_node(self, text):
-        r"""Reads a node from the given text and returns the node and remaining text.
-
-            >>> read_node = Parser('').read_node
-            >>> read_node('hello $name')
-            (t'hello ', '$name')
-            >>> read_node('$name')
-            ($name, '')
-        """
-        if text.startswith('$$'):
-            return TextNode('$'), text[2:]
-        elif text.startswith('$#'): # comment
-            line, text = splitline(text)
-            return TextNode('\n'), text
-        elif text.startswith('$'):
-            text = text[1:] # strip $
-            if text.startswith(':'):
-                escape = False
-                text = text[1:] # strip :
-            else:
-                escape = True
-            return self.read_expr(text, escape=escape)
-        else:
-            return self.read_text(text)
-    
-    def read_text(self, text):
-        r"""Reads a text node from the given text.
-        
-            >>> read_text = Parser('').read_text
-            >>> read_text('hello $name')
-            (t'hello ', '$name')
-        """
-        index = text.find('$')
-        if index < 0:
-            return TextNode(text), ''
-        else:
-            return TextNode(text[:index]), text[index:]
-            
-    def read_keyword(self, text):
-        line, text = splitline(text)
-        return CodeNode(None, line.strip() + "\n"), text
-
-    def read_expr(self, text, escape=True):
-        """Reads a python expression from the text and returns the expression and remaining text.
-
-        expr -> simple_expr | paren_expr
-        simple_expr -> id extended_expr
-        extended_expr -> attr_access | paren_expr extended_expr | ''
-        attr_access -> dot id extended_expr
-        paren_expr -> [ tokens ] | ( tokens ) | { tokens }
-     
-            >>> read_expr = Parser('').read_expr
-            >>> read_expr("name")
-            ($name, '')
-            >>> read_expr("a.b and c")
-            ($a.b, ' and c')
-            >>> read_expr("a. b")
-            ($a, '. b')
-            >>> read_expr("name</h1>")
-            ($name, '</h1>')
-            >>> read_expr("(limit)ing")
-            ($(limit), 'ing')
-            >>> read_expr('a[1, 2][:3].f(1+2, "weird string[).", 3 + 4) done.')
-            ($a[1, 2][:3].f(1+2, "weird string[).", 3 + 4), ' done.')
-        """
-        def simple_expr():
-            identifier()
-            extended_expr()
-        
-        def identifier():
-            tokens.next()
-        
-        def extended_expr():
-            lookahead = tokens.lookahead()
-            if lookahead is None:
-                return
-            elif lookahead.value == '.':
-                attr_access()
-            elif lookahead.value in parens:
-                paren_expr()
-                extended_expr()
-            else:
-                return
-        
-        def attr_access():
-            from token import NAME # python token constants
-            dot = tokens.lookahead()
-            if tokens.lookahead2().type == NAME:
-                tokens.next() # consume dot
-                identifier()
-                extended_expr()
-        
-        def paren_expr():
-            begin = tokens.next().value
-            end = parens[begin]
-            while True:
-                if tokens.lookahead().value in parens:
-                    paren_expr()
-                else:
-                    t = tokens.next()
-                    if t.value == end:
-                        break
-            return
-
-        parens = {
-            "(": ")",
-            "[": "]",
-            "{": "}"
-        }
-        
-        def get_tokens(text):
-            """tokenize text using python tokenizer.
-            Python tokenizer ignores spaces, but they might be important in some cases. 
-            This function introduces dummy space tokens when it identifies any ignored space.
-            Each token is a storage object containing type, value, begin and end.
-            """
-            readline = iter([text]).next
-            end = None
-            for t in tokenize.generate_tokens(readline):
-                t = storage(type=t[0], value=t[1], begin=t[2], end=t[3])
-                if end is not None and end != t.begin:
-                    _, x1 = end
-                    _, x2 = t.begin
-                    yield storage(type=-1, value=text[x1:x2], begin=end, end=t.begin)
-                end = t.end
-                yield t
-                
-        class BetterIter:
-            """Iterator like object with 2 support for 2 look aheads."""
-            def __init__(self, items):
-                self.iteritems = iter(items)
-                self.items = []
-                self.position = 0
-                self.current_item = None
-            
-            def lookahead(self):
-                if len(self.items) <= self.position:
-                    self.items.append(self._next())
-                return self.items[self.position]
-
-            def _next(self):
-                try:
-                    return self.iteritems.next()
-                except StopIteration:
-                    return None
-                
-            def lookahead2(self):
-                if len(self.items) <= self.position+1:
-                    self.items.append(self._next())
-                return self.items[self.position+1]
-                    
-            def next(self):
-                self.current_item = self.lookahead()
-                self.position += 1
-                return self.current_item
-
-        tokens = BetterIter(get_tokens(text))
-                
-        if tokens.lookahead().value in parens:
-            paren_expr()
-        else:
-            simple_expr()
-        row, col = tokens.current_item.end
-        return ExpressionNode(text[:col], escape=escape), text[col:]    
-
-    def read_assignment(self, text):
-        r"""Reads assignment statement from text.
-    
-            >>> read_assignment = Parser('').read_assignment
-            >>> read_assignment('a = b + 1\nfoo')
-            (<assignment: 'a = b + 1'>, 'foo')
-        """
-        line, text = splitline(text)
-        return AssignmentNode(line.strip()), text
-    
-    def python_lookahead(self, text):
-        """Returns the first python token from the given text.
-        
-            >>> python_lookahead = Parser('').python_lookahead
-            >>> python_lookahead('for i in range(10):')
-            'for'
-            >>> python_lookahead('else:')
-            'else'
-            >>> python_lookahead(' x = 1')
-            ' '
-        """
-        readline = iter([text]).next
-        tokens = tokenize.generate_tokens(readline)
-        return tokens.next()[1]
-        
-    def python_tokens(self, text):
-        readline = iter([text]).next
-        tokens = tokenize.generate_tokens(readline)
-        return [t[1] for t in tokens]
-        
-    def read_indented_block(self, text, indent):
-        r"""Read a block of text. A block is what typically follows a for or it statement.
-        It can be in the same line as that of the statement or an indented block.
-
-            >>> read_indented_block = Parser('').read_indented_block
-            >>> read_indented_block('  a\n  b\nc', '  ')
-            ('a\nb\n', 'c')
-            >>> read_indented_block('  a\n    b\n  c\nd', '  ')
-            ('a\n  b\nc\n', 'd')
-        """
-        if indent == '':
-            return '', text
-            
-        block = ""
-        while True:
-            if text.startswith(indent):
-                line, text = splitline(text)
-                block += line[len(indent):]
-            else:
-                break
-        return block, text
-
-    def read_statement(self, text):
-        r"""Reads a python statement.
-        
-            >>> read_statement = Parser('').read_statement
-            >>> read_statement('for i in range(10): hello $name')
-            ('for i in range(10):', ' hello $name')
-        """
-        tok = PythonTokenizer(text)
-        tok.consume_till(':')
-        return text[:tok.index], text[tok.index:]
-        
-    def read_block_section(self, text, begin_indent=''):
-        r"""
-            >>> read_block_section = Parser('').read_block_section
-            >>> read_block_section('for i in range(10): hello $i\nfoo')
-            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, 'foo')
-            >>> read_block_section('for i in range(10):\n        hello $i\n    foo', begin_indent='    ')
-            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, '    foo')
-            >>> read_block_section('for i in range(10):\n  hello $i\nfoo')
-            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, 'foo')
-        """
-        line, text = splitline(text)
-        stmt, line = self.read_statement(line)
-        keyword = self.python_lookahead(stmt)
-        
-        # if there is some thing left in the line
-        if line.strip():
-            block = line.lstrip()
-        else:
-            def find_indent(text):
-                rx = re_compile('  +')
-                match = rx.match(text)    
-                first_indent = match and match.group(0)
-                return first_indent or ""
-
-            # find the indentation of the block by looking at the first line
-            first_indent = find_indent(text)[len(begin_indent):]
-            indent = begin_indent + min(first_indent, INDENT)
-            
-            block, text = self.read_indented_block(text, indent)
-            
-        return self.create_block_node(keyword, stmt, block, begin_indent), text
-        
-    def create_block_node(self, keyword, stmt, block, begin_indent):
-        if keyword in STATEMENT_NODES:
-            return STATEMENT_NODES[keyword](stmt, block, begin_indent)
-        else:
-            raise ParseError, 'Unknown statement: %s' % repr(keyword)
-        
-class PythonTokenizer:
-    """Utility wrapper over python tokenizer."""
-    def __init__(self, text):
-        self.text = text
-        readline = iter([text]).next
-        self.tokens = tokenize.generate_tokens(readline)
-        self.index = 0
-        
-    def consume_till(self, delim):        
-        """Consumes tokens till colon.
-        
-            >>> tok = PythonTokenizer('for i in range(10): hello $i')
-            >>> tok.consume_till(':')
-            >>> tok.text[:tok.index]
-            'for i in range(10):'
-            >>> tok.text[tok.index:]
-            ' hello $i'
-        """
-        try:
-            while True:
-                t = self.next()
-                if t.value == delim:
-                    break
-                elif t.value == '(':
-                    self.consume_till(')')
-                elif t.value == '[':
-                    self.consume_till(']')
-                elif t.value == '{':
-                    self.consume_till('}')
-
-                # if end of line is found, it is an exception.
-                # Since there is no easy way to report the line number,
-                # leave the error reporting to the python parser later  
-                #@@ This should be fixed.
-                if t.value == '\n':
-                    break
-        except:
-            #raise ParseError, "Expected %s, found end of line." % repr(delim)
-
-            # raising ParseError doesn't show the line number. 
-            # if this error is ignored, then it will be caught when compiling the python code.
-            return
-    
-    def next(self):
-        type, t, begin, end, line = self.tokens.next()
-        row, col = end
-        self.index = col
-        return storage(type=type, value=t, begin=begin, end=end)
-        
-class DefwithNode:
-    def __init__(self, defwith, suite):
-        if defwith:
-            self.defwith = defwith.replace('with', '__template__') + ':'
-        else:
-            self.defwith = 'def __template__():'
-        self.suite = suite
-
-    def emit(self, indent):
-        return self.defwith + self.suite.emit(indent + INDENT)
-
-    def __repr__(self):
-        return "<defwith: %s, %s>" % (self.defwith, self.nodes)
-
-class TextNode:
-    def __init__(self, value):
-        self.value = value
-
-    def emit(self, indent):
-        return repr(self.value)
-        
-    def __repr__(self):
-        return 't' + repr(self.value)
-        
-class ExpressionNode:
-    def __init__(self, value, escape=True):
-        self.value = value.strip()
-        
-        # convert ${...} to $(...)
-        if value.startswith('{') and value.endswith('}'):
-            self.value = '(' + self.value[1:-1] + ')'
-            
-        self.escape = escape
-
-    def emit(self, indent):
-        return 'escape_(%s, %s)' % (self.value, bool(self.escape))
-        
-    def __repr__(self):
-        if self.escape:
-            escape = ''
-        else:
-            escape = ':'
-        return "$%s%s" % (escape, self.value)
-        
-class AssignmentNode:
-    def __init__(self, code):
-        self.code = code
-        
-    def emit(self, indent, begin_indent=''):
-        return indent + self.code + "\n"
-        
-    def __repr__(self):
-        return "<assignment: %s>" % repr(self.code)
-        
-class LineNode:
-    def __init__(self, nodes):
-        self.nodes = nodes
-        
-    def emit(self, indent, text_indent='', name=''):
-        text = [node.emit('') for node in self.nodes]
-        if text_indent:
-            text = [repr(text_indent)] + text
-        return indent + 'yield %s, join_(%s)\n' % (repr(name), ', '.join(text))
-    
-    def __repr__(self):
-        return "<line: %s>" % repr(self.nodes)
-
-INDENT = '    ' # 4 spaces
-        
-class BlockNode:
-    def __init__(self, stmt, block, begin_indent=''):
-        self.stmt = stmt
-        self.suite = Parser('').read_suite(block)
-        self.begin_indent = begin_indent
-
-    def emit(self, indent, text_indent=''):
-        text_indent = self.begin_indent + text_indent
-        out = indent + self.stmt + self.suite.emit(indent + INDENT, text_indent)
-        return out
-        
-    def text(self):
-        return '${' + self.stmt + '}' + "".join([node.text(indent) for node in self.nodes])
-        
-    def __repr__(self):
-        return "<block: %s, %s>" % (repr(self.stmt), repr(self.nodelist))
-
-class ForNode(BlockNode):
-    def __init__(self, stmt, block, begin_indent=''):
-        self.original_stmt = stmt
-        tok = PythonTokenizer(stmt)
-        tok.consume_till('in')
-        a = stmt[:tok.index] # for i in
-        b = stmt[tok.index:-1] # rest of for stmt excluding :
-        stmt = a + ' loop.setup(' + b.strip() + '):'
-        BlockNode.__init__(self, stmt, block, begin_indent)
-        
-    def __repr__(self):
-        return "<block: %s, %s>" % (repr(self.original_stmt), repr(self.suite))
-
-class CodeNode:
-    def __init__(self, stmt, block, begin_indent=''):
-        self.code = block
-        
-    def emit(self, indent, text_indent=''):
-        import re
-        rx = re.compile('^', re.M)
-        return rx.sub(indent, self.code).rstrip(' ')
-        
-    def __repr__(self):
-        return "<code: %s>" % repr(self.code)
-        
-class IfNode(BlockNode):
-    pass
-
-class ElseNode(BlockNode):
-    pass
-
-class ElifNode(BlockNode):
-    pass
-
-class DefNode(BlockNode):
-    pass
-
-class VarNode:
-    def __init__(self, name, value):
-        self.name = name
-        self.value = value
-        
-    def emit(self, indent, text_indent):
-        return indent + 'yield %s, %s\n' % (repr(self.name), self.value)
-        
-    def __repr__(self):
-        return "<var: %s = %s>" % (self.name, self.value)
-
-class SuiteNode:
-    """Suite is a list of sections."""
-    def __init__(self, sections):
-        self.sections = sections
-        
-    def emit(self, indent, text_indent=''):
-        return "\n" + "".join([s.emit(indent, text_indent) for s in self.sections])
-        
-    def __repr__(self):
-        return repr(self.sections)
-
-STATEMENT_NODES = {
-    'for': ForNode,
-    'while': BlockNode,
-    'if': IfNode,
-    'elif': ElifNode,
-    'else': ElseNode,
-    'def': DefNode,
-    'code': CodeNode
-}
-
-KEYWORDS = [
-    "pass",
-    "break",
-    "continue",
-    "return"
-]
-
-TEMPLATE_BUILTIN_NAMES = [
-    "dict", "enumerate", "float", "int", "bool", "list", "long", "reversed", 
-    "set", "slice", "tuple", "xrange",
-    "abs", "all", "any", "callable", "chr", "cmp", "divmod", "filter", "hex", 
-    "id", "isinstance", "iter", "len", "max", "min", "oct", "ord", "pow", "range",
-    "True", "False",
-    "None",
-    "__import__", # some c-libraries like datetime requires __import__ to present in the namespace
-]
-
-import __builtin__
-TEMPLATE_BUILTINS = dict([(name, getattr(__builtin__, name)) for name in TEMPLATE_BUILTIN_NAMES if name in __builtin__.__dict__])
-
-class ForLoop:
-    """
-    Wrapper for expression in for stament to support loop.xxx helpers.
-    
-        >>> loop = ForLoop()
-        >>> for x in loop.setup(['a', 'b', 'c']):
-        ...     print loop.index, loop.revindex, loop.parity, x
-        ...
-        1 3 odd a
-        2 2 even b
-        3 1 odd c
-        >>> loop.index
-        Traceback (most recent call last):
-            ...
-        AttributeError: index
-    """
-    def __init__(self):
-        self._ctx = None
-        
-    def __getattr__(self, name):
-        if self._ctx is None:
-            raise AttributeError, name
-        else:
-            return getattr(self._ctx, name)
-        
-    def setup(self, seq):        
-        self._push()
-        return self._ctx.setup(seq)
-        
-    def _push(self):
-        self._ctx = ForLoopContext(self, self._ctx)
-        
-    def _pop(self):
-        self._ctx = self._ctx.parent
-                
-class ForLoopContext:
-    """Stackable context for ForLoop to support nested for loops.
-    """
-    def __init__(self, forloop, parent):
-        self._forloop = forloop
-        self.parent = parent
-        
-    def setup(self, seq):
-        if hasattr(seq, '__len__'):
-            n = len(seq)
-        else:
-            n = 0
-            
-        self.index = 0
-        seq = iter(seq)
-        
-        # Pre python-2.5 does not support yield in try-except.
-        # This is a work-around to overcome that limitation.
-        def next(seq):
-            try:
-                return seq.next()
-            except:
-                self._forloop._pop()
-                raise
-        
-        while True:
-            self._next(self.index + 1, n)
-            yield next(seq)
-            
-    def _next(self, i, n):
-        self.index = i
-        self.index0 = i - 1
-        self.first = (i == 1)
-        self.last = (i == n)
-        self.odd = (i % 2 == 1)
-        self.even = (i % 2 == 0)
-        self.parity = ['odd', 'even'][self.even]
-        if n:
-            self.length = n
-            self.revindex0 = n - i
-            self.revindex = self.revindex0 + 1
-        
-class BaseTemplate:
-    def __init__(self, code, filename, filter, globals, builtins):
-        self.filename = filename
-        self.filter = filter
-        self._globals = globals
-        self._builtins = builtins
-        if code:
-            self.t = self._compile(code)
-        else:
-            self.t = lambda: ''
-        
-    def _compile(self, code):
-        env = self.make_env(self._globals or {}, self._builtins)
-        exec(code, env)
-        return env['__template__']
-
-    def __call__(self, *a, **kw):
-        out = self.t(*a, **kw)
-        return self._join_output(out)
-        
-    def _join_output(self, out):
-        d = TemplateResult()
-        data = []
-        
-        for name, value in out:
-            if name:
-                d[name] = value
-            else:
-                data.append(value)
-                            
-        d.__body__ = u"".join(data)
-        return d       
-
-    def make_env(self, globals, builtins):
-        return dict(globals,
-            __builtins__=builtins, 
-            loop=ForLoop(),
-            escape_=self._escape,
-            join_=self._join
-        )
-    
-    def _join(self, *items):
-        return u"".join([safeunicode(item) for item in items])
-        
-    def _escape(self, value, escape=False):
-        import types
-        if value is None: 
-            value = ''
-        elif isinstance(value, types.GeneratorType):
-            value = self._join_output(value)
-            
-        value = safeunicode(value)
-        if escape and self.filter:
-            value = self.filter(value)
-        return value
-
-class Template(BaseTemplate):
-    CONTENT_TYPES = {
-        '.html' : 'text/html; charset=utf-8',
-        '.xhtml' : 'application/xhtml+xml; charset=utf-8',
-        '.txt' : 'text/plain',
-    }
-    FILTERS = {
-        '.html': websafe,
-        '.xhtml': websafe,
-        '.xml': websafe
-    }
-    globals = {}
-    
-    def __init__(self, text, filename='<template>', filter=None, globals=None, builtins=None):
-        text = Template.normalize_text(text)
-        code = self.compile_template(text, filename)
-                
-        _, ext = os.path.splitext(filename)
-        filter = filter or self.FILTERS.get(ext, None)
-        self.content_type = self.CONTENT_TYPES.get(ext, None)
-
-        if globals is None:
-            globals = self.globals
-        if builtins is None:
-            builtins = TEMPLATE_BUILTINS
-                
-        BaseTemplate.__init__(self, code=code, filename=filename, filter=filter, globals=globals, builtins=builtins)
-        
-    def normalize_text(text):
-        """Normalizes template text by correcting \r\n, tabs and BOM chars."""
-        text = text.replace('\r\n', '\n').replace('\r', '\n').expandtabs()
-        if not text.endswith('\n'):
-            text += '\n'
-
-        # ignore BOM chars at the begining of template
-        BOM = '\xef\xbb\xbf'
-        if isinstance(text, str) and text.startswith(BOM):
-            text = text[len(BOM):]
-        
-        # support fort \$ for backward-compatibility 
-        text = text.replace(r'\$', '$$')
-        return text
-    normalize_text = staticmethod(normalize_text)
-                
-    def __call__(self, *a, **kw):
-        import webapi as web
-        if 'headers' in web.ctx and self.content_type:
-            web.header('Content-Type', self.content_type, unique=True)
-            
-        return BaseTemplate.__call__(self, *a, **kw)
-        
-    def generate_code(text, filename):
-        # parse the text
-        rootnode = Parser(text, filename).parse()
-                
-        # generate python code from the parse tree
-        code = rootnode.emit(indent="").strip()
-        return safestr(code)
-        
-    generate_code = staticmethod(generate_code)
-        
-    def compile_template(self, template_string, filename):
-        code = Template.generate_code(template_string, filename)
-    
-        def get_source_line(filename, lineno):
-            try:
-                lines = open(filename).read().splitlines()
-                return lines[lineno]
-            except:
-                return None
-        
-        try:
-            # compile the code first to report the errors, if any, with the filename
-            compiled_code = compile(code, filename, 'exec')
-        except SyntaxError, e:
-            # display template line that caused the error along with the traceback.
-            try:
-                e.msg += '\n\nTemplate traceback:\n    File %s, line %s\n        %s' % \
-                    (repr(e.filename), e.lineno, get_source_line(e.filename, e.lineno-1))
-            except: 
-                pass
-            raise
-        
-        # make sure code is safe
-        import compiler
-        ast = compiler.parse(code)
-        SafeVisitor().walk(ast, filename)
-
-        return compiled_code
-        
-class CompiledTemplate(Template):
-    def __init__(self, f, filename):
-        Template.__init__(self, '', filename)
-        self.t = f
-        
-    def compile_template(self, *a):
-        return None
-    
-    def _compile(self, *a):
-        return None
-                
-class Render:
-    """The most preferred way of using templates.
-    
-        render = web.template.render('templates')
-        print render.foo()
-        
-    Optional parameter can be `base` can be used to pass output of 
-    every template through the base template.
-    
-        render = web.template.render('templates', base='layout')
-    """
-    def __init__(self, loc='templates', cache=None, base=None, **keywords):
-        self._loc = loc
-        self._keywords = keywords
-
-        if cache is None:
-            cache = not config.get('debug', False)
-        
-        if cache:
-            self._cache = {}
-        else:
-            self._cache = None
-        
-        if base and not hasattr(base, '__call__'):
-            # make base a function, so that it can be passed to sub-renders
-            self._base = lambda page: self._template(base)(page)
-        else:
-            self._base = base
-            
-    def _lookup(self, name):
-        path = os.path.join(self._loc, name)
-        if os.path.isdir(path):
-            return 'dir', path
-        else:
-            path = self._findfile(path)
-            if path:
-                return 'file', path
-            else:
-                return 'none', None
-        
-    def _load_template(self, name):
-        kind, path = self._lookup(name)
-        
-        if kind == 'dir':
-            return Render(path, cache=self._cache is not None, base=self._base, **self._keywords)
-        elif kind == 'file':
-            return Template(open(path).read(), filename=path, **self._keywords)
-        else:
-            raise AttributeError, "No template named " + name            
-
-    def _findfile(self, path_prefix): 
-        p = [f for f in glob.glob(path_prefix + '.*') if not f.endswith('~')] # skip backup files
-        return p and p[0]
-            
-    def _template(self, name):
-        if self._cache is not None:
-            if name not in self._cache:
-                self._cache[name] = self._load_template(name)
-            return self._cache[name]
-        else:
-            return self._load_template(name)
-        
-    def __getattr__(self, name):
-        t = self._template(name)
-        if self._base and isinstance(t, Template):
-            def template(*a, **kw):
-                return self._base(t(*a, **kw))
-            return template
-        else:
-            return self._template(name)
-
-class GAE_Render(Render):
-    # Render gets over-written. make a copy here.
-    super = Render
-    def __init__(self, loc, *a, **kw):
-        GAE_Render.super.__init__(self, loc, *a, **kw)
-        
-        import types
-        if isinstance(loc, types.ModuleType):
-            self.mod = loc
-        else:
-            name = loc.rstrip('/').replace('/', '.')
-            self.mod = __import__(name, None, None, ['x'])
-
-        self.mod.__dict__.update(kw.get('builtins', TEMPLATE_BUILTINS))
-        self.mod.__dict__.update(Template.globals)
-        self.mod.__dict__.update(kw.get('globals', {}))
-
-    def _load_template(self, name):
-        t = getattr(self.mod, name)
-        import types
-        if isinstance(t, types.ModuleType):
-            return GAE_Render(t, cache=self._cache is not None, base=self._base, **self._keywords)
-        else:
-            return t
-
-render = Render
-# setup render for Google App Engine.
-try:
-    from google import appengine
-    render = Render = GAE_Render
-except ImportError:
-    pass
-        
-def frender(path, **keywords):
-    """Creates a template from the given file path.
-    """
-    return Template(open(path).read(), filename=path, **keywords)
-    
-def compile_templates(root):
-    """Compiles templates to python code."""
-    re_start = re_compile('^', re.M)
-    
-    for dirpath, dirnames, filenames in os.walk(root):
-        filenames = [f for f in filenames if not f.startswith('.') and not f.endswith('~') and not f.startswith('__init__.py')]
-
-        for d in dirnames[:]:
-            if d.startswith('.'):
-                dirnames.remove(d) # don't visit this dir
-
-        out = open(os.path.join(dirpath, '__init__.py'), 'w')
-        out.write('from web.template import CompiledTemplate, ForLoop\n\n')
-        if dirnames:
-            out.write("import " + ", ".join(dirnames))
-
-        for f in filenames:
-            path = os.path.join(dirpath, f)
-
-            if '.' in f:
-                name, _ = f.split('.', 1)
-            else:
-                name = f
-                
-            text = open(path).read()
-            text = Template.normalize_text(text)
-            code = Template.generate_code(text, path)
-            code = re_start.sub('    ', code)
-                        
-            _gen = '' + \
-            '\ndef %s():' + \
-            '\n    loop = ForLoop()' + \
-            '\n    _dummy  = CompiledTemplate(lambda: None, "dummy")' + \
-            '\n    join_ = _dummy._join' + \
-            '\n    escape_ = _dummy._escape' + \
-            '\n' + \
-            '\n%s' + \
-            '\n    return __template__'
-            
-            gen_code = _gen % (name, code)
-            out.write(gen_code)
-            out.write('\n\n')
-            out.write('%s = CompiledTemplate(%s(), %s)\n\n' % (name, name, repr(path)))
-
-            # create template to make sure it compiles
-            t = Template(open(path).read(), path)
-        out.close()
-                
-class ParseError(Exception):
-    pass
-    
-class SecurityError(Exception):
-    """The template seems to be trying to do something naughty."""
-    pass
-
-# Enumerate all the allowed AST nodes
-ALLOWED_AST_NODES = [
-    "Add", "And",
-#   "AssAttr",
-    "AssList", "AssName", "AssTuple",
-#   "Assert",
-    "Assign", "AugAssign",
-#   "Backquote",
-    "Bitand", "Bitor", "Bitxor", "Break",
-    "CallFunc","Class", "Compare", "Const", "Continue",
-    "Decorators", "Dict", "Discard", "Div",
-    "Ellipsis", "EmptyNode",
-#   "Exec",
-    "Expression", "FloorDiv", "For",
-#   "From",
-    "Function", 
-    "GenExpr", "GenExprFor", "GenExprIf", "GenExprInner",
-    "Getattr", 
-#   "Global", 
-    "If", "IfExp",
-#   "Import",
-    "Invert", "Keyword", "Lambda", "LeftShift",
-    "List", "ListComp", "ListCompFor", "ListCompIf", "Mod",
-    "Module",
-    "Mul", "Name", "Not", "Or", "Pass", "Power",
-#   "Print", "Printnl", "Raise",
-    "Return", "RightShift", "Slice", "Sliceobj",
-    "Stmt", "Sub", "Subscript",
-#   "TryExcept", "TryFinally",
-    "Tuple", "UnaryAdd", "UnarySub",
-    "While", "With", "Yield",
-]
-
-class SafeVisitor(object):
-    """
-    Make sure code is safe by walking through the AST.
-    
-    Code considered unsafe if:
-        * it has restricted AST nodes
-        * it is trying to access resricted attributes   
-        
-    Adopted from http://www.zafar.se/bkz/uploads/safe.txt (public domain, Babar K. Zafar)
-    """
-    def __init__(self):
-        "Initialize visitor by generating callbacks for all AST node types."
-        self.errors = []
-
-    def walk(self, ast, filename):
-        "Validate each node in AST and raise SecurityError if the code is not safe."
-        self.filename = filename
-        self.visit(ast)
-        
-        if self.errors:        
-            raise SecurityError, '\n'.join([str(err) for err in self.errors])
-        
-    def visit(self, node, *args):
-        "Recursively validate node and all of its children."
-        def classname(obj):
-            return obj.__class__.__name__
-        nodename = classname(node)
-        fn = getattr(self, 'visit' + nodename, None)
-        
-        if fn:
-            fn(node, *args)
-        else:
-            if nodename not in ALLOWED_AST_NODES:
-                self.fail(node, *args)
-            
-        for child in node.getChildNodes():
-            self.visit(child, *args)
-
-    def visitName(self, node, *args):
-        "Disallow any attempts to access a restricted attr."
-        #self.assert_attr(node.getChildren()[0], node)
-        pass
-        
-    def visitGetattr(self, node, *args):
-        "Disallow any attempts to access a restricted attribute."
-        self.assert_attr(node.attrname, node)
-            
-    def assert_attr(self, attrname, node):
-        if self.is_unallowed_attr(attrname):
-            lineno = self.get_node_lineno(node)
-            e = SecurityError("%s:%d - access to attribute '%s' is denied" % (self.filename, lineno, attrname))
-            self.errors.append(e)
-
-    def is_unallowed_attr(self, name):
-        return name.startswith('_') \
-            or name.startswith('func_') \
-            or name.startswith('im_')
-            
-    def get_node_lineno(self, node):
-        return (node.lineno) and node.lineno or 0
-        
-    def fail(self, node, *args):
-        "Default callback for unallowed AST nodes."
-        lineno = self.get_node_lineno(node)
-        nodename = node.__class__.__name__
-        e = SecurityError("%s:%d - execution of '%s' statements is denied" % (self.filename, lineno, nodename))
-        self.errors.append(e)
-
-class TemplateResult(storage):
-    """Dictionary like object for storing template output.
-    
-    A template can specify key-value pairs in the output using 
-    `var` statements. Each `var` statement adds a new key to the 
-    template output and the main output is stored with key 
-    __body__.
-    
-        >>> d = TemplateResult(__body__='hello, world', x='foo')
-        >>> d
-        <TemplateResult: {'__body__': 'hello, world', 'x': 'foo'}>
-        >>> print d
-        hello, world
-    """
-    def __unicode__(self): 
-        return safeunicode(self.get('__body__', ''))
-    
-    def __str__(self):
-        return safestr(self.get('__body__', ''))
-        
-    def __repr__(self):
-        return "<TemplateResult: %s>" % dict.__repr__(self)
-    
-def test():
-    r"""Doctest for testing template module.
-
-    Define a utility function to run template test.
-    
-        >>> class TestResult(TemplateResult):
-        ...     def __repr__(self): return repr(unicode(self))
-        ...
-        >>> def t(code, **keywords):
-        ...     tmpl = Template(code, **keywords)
-        ...     return lambda *a, **kw: TestResult(tmpl(*a, **kw))
-        ...
-    
-    Simple tests.
-    
-        >>> t('1')()
-        u'1\n'
-        >>> t('$def with ()\n1')()
-        u'1\n'
-        >>> t('$def with (a)\n$a')(1)
-        u'1\n'
-        >>> t('$def with (a=0)\n$a')(1)
-        u'1\n'
-        >>> t('$def with (a=0)\n$a')(a=1)
-        u'1\n'
-    
-    Test complicated expressions.
-        
-        >>> t('$def with (x)\n$x.upper()')('hello')
-        u'HELLO\n'
-        >>> t('$(2 * 3 + 4 * 5)')()
-        u'26\n'
-        >>> t('${2 * 3 + 4 * 5}')()
-        u'26\n'
-        >>> t('$def with (limit)\nkeep $(limit)ing.')('go')
-        u'keep going.\n'
-        >>> t('$def with (a)\n$a.b[0]')(storage(b=[1]))
-        u'1\n'
-        
-    Test html escaping.
-    
-        >>> t('$def with (x)\n$x', filename='a.html')('<html>')
-        u'&lt;html&gt;\n'
-        >>> t('$def with (x)\n$x', filename='a.txt')('<html>')
-        u'<html>\n'
-                
-    Test if, for and while.
-    
-        >>> t('$if 1: 1')()
-        u'1\n'
-        >>> t('$if 1:\n    1')()
-        u'1\n'
-        >>> t('$if 1:\n    1\\')()
-        u'1'
-        >>> t('$if 0: 0\n$elif 1: 1')()
-        u'1\n'
-        >>> t('$if 0: 0\n$elif None: 0\n$else: 1')()
-        u'1\n'
-        >>> t('$if 0 < 1 and 1 < 2: 1')()
-        u'1\n'
-        >>> t('$for x in [1, 2, 3]: $x')()
-        u'1\n2\n3\n'
-        >>> t('$def with (d)\n$for k, v in d.iteritems(): $k')({1: 1})
-        u'1\n'
-        >>> t('$for x in [1, 2, 3]:\n\t$x')()
-        u'    1\n    2\n    3\n'
-        >>> t('$def with (a)\n$while a and a.pop():1')([1, 2, 3])
-        u'1\n1\n1\n'
-
-    The space after : must be ignored.
-    
-        >>> t('$if True: foo')()
-        u'foo\n'
-    
-    Test loop.xxx.
-
-        >>> t("$for i in range(5):$loop.index, $loop.parity")()
-        u'1, odd\n2, even\n3, odd\n4, even\n5, odd\n'
-        >>> t("$for i in range(2):\n    $for j in range(2):$loop.parent.parity $loop.parity")()
-        u'odd odd\nodd even\neven odd\neven even\n'
-        
-    Test assignment.
-    
-        >>> t('$ a = 1\n$a')()
-        u'1\n'
-        >>> t('$ a = [1]\n$a[0]')()
-        u'1\n'
-        >>> t('$ a = {1: 1}\n$a.keys()[0]')()
-        u'1\n'
-        >>> t('$ a = []\n$if not a: 1')()
-        u'1\n'
-        >>> t('$ a = {}\n$if not a: 1')()
-        u'1\n'
-        >>> t('$ a = -1\n$a')()
-        u'-1\n'
-        >>> t('$ a = "1"\n$a')()
-        u'1\n'
-
-    Test comments.
-    
-        >>> t('$# 0')()
-        u'\n'
-        >>> t('hello$#comment1\nhello$#comment2')()
-        u'hello\nhello\n'
-        >>> t('$#comment0\nhello$#comment1\nhello$#comment2')()
-        u'\nhello\nhello\n'
-        
-    Test unicode.
-    
-        >>> t('$def with (a)\n$a')(u'\u203d')
-        u'\u203d\n'
-        >>> t('$def with (a)\n$a')(u'\u203d'.encode('utf-8'))
-        u'\u203d\n'
-        >>> t(u'$def with (a)\n$a $:a')(u'\u203d')
-        u'\u203d \u203d\n'
-        >>> t(u'$def with ()\nfoo')()
-        u'foo\n'
-        >>> def f(x): return x
-        ...
-        >>> t(u'$def with (f)\n$:f("x")')(f)
-        u'x\n'
-        >>> t('$def with (f)\n$:f("x")')(f)
-        u'x\n'
-    
-    Test dollar escaping.
-    
-        >>> t("Stop, $$money isn't evaluated.")()
-        u"Stop, $money isn't evaluated.\n"
-        >>> t("Stop, \$money isn't evaluated.")()
-        u"Stop, $money isn't evaluated.\n"
-        
-    Test space sensitivity.
-    
-        >>> t('$def with (x)\n$x')(1)
-        u'1\n'
-        >>> t('$def with(x ,y)\n$x')(1, 1)
-        u'1\n'
-        >>> t('$(1 + 2*3 + 4)')()
-        u'11\n'
-        
-    Make sure globals are working.
-            
-        >>> t('$x')()
-        Traceback (most recent call last):
-            ...
-        NameError: global name 'x' is not defined
-        >>> t('$x', globals={'x': 1})()
-        u'1\n'
-        
-    Can't change globals.
-    
-        >>> t('$ x = 2\n$x', globals={'x': 1})()
-        u'2\n'
-        >>> t('$ x = x + 1\n$x', globals={'x': 1})()
-        Traceback (most recent call last):
-            ...
-        UnboundLocalError: local variable 'x' referenced before assignment
-    
-    Make sure builtins are customizable.
-    
-        >>> t('$min(1, 2)')()
-        u'1\n'
-        >>> t('$min(1, 2)', builtins={})()
-        Traceback (most recent call last):
-            ...
-        NameError: global name 'min' is not defined
-        
-    Test vars.
-    
-        >>> x = t('$var x: 1')()
-        >>> x.x
-        u'1'
-        >>> x = t('$var x = 1')()
-        >>> x.x
-        1
-        >>> x = t('$var x:  \n    foo\n    bar')()
-        >>> x.x
-        u'foo\nbar\n'
-
-    Test BOM chars.
-
-        >>> t('\xef\xbb\xbf$def with(x)\n$x')('foo')
-        u'foo\n'
-
-    Test for with weird cases.
-
-        >>> t('$for i in range(10)[1:5]:\n    $i')()
-        u'1\n2\n3\n4\n'
-        >>> t("$for k, v in {'a': 1, 'b': 2}.items():\n    $k $v")()
-        u'a 1\nb 2\n'
-        >>> t("$for k, v in ({'a': 1, 'b': 2}.items():\n    $k $v")()
-        Traceback (most recent call last):
-            ...
-        SyntaxError: invalid syntax
-
-    Test datetime.
-
-        >>> import datetime
-        >>> t("$def with (date)\n$date.strftime('%m %Y')")(datetime.datetime(2009, 1, 1))
-        u'01 2009\n'
-    """
-    pass
-            
-if __name__ == "__main__":
-    import sys
-    if '--compile' in sys.argv:
-        compile_templates(sys.argv[2])
-    else:
-        import doctest
-        doctest.testmod()
--- a/bundled/webpy/web/test.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-"""test utilities
-(part of web.py)
-"""
-import unittest
-import sys, os
-import web
-
-TestCase = unittest.TestCase
-TestSuite = unittest.TestSuite
-
-def load_modules(names):
-    return [__import__(name, None, None, "x") for name in names]
-
-def module_suite(module, classnames=None):
-    """Makes a suite from a module."""
-    if classnames:
-        return unittest.TestLoader().loadTestsFromNames(classnames, module)
-    elif hasattr(module, 'suite'):
-        return module.suite()
-    else:
-        return unittest.TestLoader().loadTestsFromModule(module)
-
-def doctest_suite(module_names):
-    """Makes a test suite from doctests."""
-    import doctest
-    suite = TestSuite()
-    for mod in load_modules(module_names):
-        suite.addTest(doctest.DocTestSuite(mod))
-    return suite
-    
-def suite(module_names):
-    """Creates a suite from multiple modules."""
-    suite = TestSuite()
-    for mod in load_modules(module_names):
-        suite.addTest(module_suite(mod))
-    return suite
-
-def runTests(suite):
-    runner = unittest.TextTestRunner()
-    return runner.run(suite)
-
-def main(suite=None):
-    if not suite:
-        main_module = __import__('__main__')
-        # allow command line switches
-        args = [a for a in sys.argv[1:] if not a.startswith('-')]
-        suite = module_suite(main_module, args or None)
-
-    result = runTests(suite)
-    sys.exit(not result.wasSuccessful())
-
--- a/bundled/webpy/web/utils.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1112 +0,0 @@
-
-#!/usr/bin/env python
-"""
-General Utilities
-(part of web.py)
-"""
-
-__all__ = [
-  "Storage", "storage", "storify", 
-  "iters", 
-  "rstrips", "lstrips", "strips", 
-  "safeunicode", "safestr", "utf8",
-  "TimeoutError", "timelimit",
-  "Memoize", "memoize",
-  "re_compile", "re_subm",
-  "group", "uniq", "iterview",
-  "IterBetter", "iterbetter",
-  "dictreverse", "dictfind", "dictfindall", "dictincr", "dictadd",
-  "listget", "intget", "datestr",
-  "numify", "denumify", "commify", "dateify",
-  "nthstr",
-  "CaptureStdout", "capturestdout", "Profile", "profile",
-  "tryall",
-  "ThreadedDict", "threadeddict",
-  "autoassign",
-  "to36",
-  "safemarkdown",
-  "sendmail"
-]
-
-import re, sys, time, threading, itertools
-
-try:
-    import subprocess
-except ImportError: 
-    subprocess = None
-
-try: import datetime
-except ImportError: pass
-
-try: set
-except NameError:
-    from sets import Set as set
-
-class Storage(dict):
-    """
-    A Storage object is like a dictionary except `obj.foo` can be used
-    in addition to `obj['foo']`.
-    
-        >>> o = storage(a=1)
-        >>> o.a
-        1
-        >>> o['a']
-        1
-        >>> o.a = 2
-        >>> o['a']
-        2
-        >>> del o.a
-        >>> o.a
-        Traceback (most recent call last):
-            ...
-        AttributeError: 'a'
-    
-    """
-    def __getattr__(self, key): 
-        try:
-            return self[key]
-        except KeyError, k:
-            raise AttributeError, k
-    
-    def __setattr__(self, key, value): 
-        self[key] = value
-    
-    def __delattr__(self, key):
-        try:
-            del self[key]
-        except KeyError, k:
-            raise AttributeError, k
-    
-    def __repr__(self):     
-        return '<Storage ' + dict.__repr__(self) + '>'
-
-storage = Storage
-
-def storify(mapping, *requireds, **defaults):
-    """
-    Creates a `storage` object from dictionary `mapping`, raising `KeyError` if
-    d doesn't have all of the keys in `requireds` and using the default 
-    values for keys found in `defaults`.
-
-    For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of
-    `storage({'a':1, 'b':2, 'c':3})`.
-    
-    If a `storify` value is a list (e.g. multiple values in a form submission), 
-    `storify` returns the last element of the list, unless the key appears in 
-    `defaults` as a list. Thus:
-    
-        >>> storify({'a':[1, 2]}).a
-        2
-        >>> storify({'a':[1, 2]}, a=[]).a
-        [1, 2]
-        >>> storify({'a':1}, a=[]).a
-        [1]
-        >>> storify({}, a=[]).a
-        []
-    
-    Similarly, if the value has a `value` attribute, `storify will return _its_
-    value, unless the key appears in `defaults` as a dictionary.
-    
-        >>> storify({'a':storage(value=1)}).a
-        1
-        >>> storify({'a':storage(value=1)}, a={}).a
-        <Storage {'value': 1}>
-        >>> storify({}, a={}).a
-        {}
-        
-    Optionally, keyword parameter `_unicode` can be passed to convert all values to unicode.
-    
-        >>> storify({'x': 'a'}, _unicode=True)
-        <Storage {'x': u'a'}>
-        >>> storify({'x': storage(value='a')}, x={}, _unicode=True)
-        <Storage {'x': <Storage {'value': 'a'}>}>
-        >>> storify({'x': storage(value='a')}, _unicode=True)
-        <Storage {'x': u'a'}>
-    """
-    _unicode = defaults.pop('_unicode', False)
-    def unicodify(s):
-        if _unicode and isinstance(s, str): return safeunicode(s)
-        else: return s
-        
-    def getvalue(x):
-        if hasattr(x, 'value'):
-            return unicodify(x.value)
-        else:
-            return unicodify(x)
-    
-    stor = Storage()
-    for key in requireds + tuple(mapping.keys()):
-        value = mapping[key]
-        if isinstance(value, list):
-            if isinstance(defaults.get(key), list):
-                value = [getvalue(x) for x in value]
-            else:
-                value = value[-1]
-        if not isinstance(defaults.get(key), dict):
-            value = getvalue(value)
-        if isinstance(defaults.get(key), list) and not isinstance(value, list):
-            value = [value]
-        setattr(stor, key, value)
-
-    for (key, value) in defaults.iteritems():
-        result = value
-        if hasattr(stor, key): 
-            result = stor[key]
-        if value == () and not isinstance(result, tuple): 
-            result = (result,)
-        setattr(stor, key, result)
-    
-    return stor
-
-iters = [list, tuple]
-import __builtin__
-if hasattr(__builtin__, 'set'):
-    iters.append(set)
-if hasattr(__builtin__, 'frozenset'):
-    iters.append(set)
-if sys.version_info < (2,6): # sets module deprecated in 2.6
-    try:
-        from sets import Set
-        iters.append(Set)
-    except ImportError: 
-        pass
-    
-class _hack(tuple): pass
-iters = _hack(iters)
-iters.__doc__ = """
-A list of iterable items (like lists, but not strings). Includes whichever
-of lists, tuples, sets, and Sets are available in this version of Python.
-"""
-
-def _strips(direction, text, remove):
-    if direction == 'l': 
-        if text.startswith(remove): 
-            return text[len(remove):]
-    elif direction == 'r':
-        if text.endswith(remove):   
-            return text[:-len(remove)]
-    else: 
-        raise ValueError, "Direction needs to be r or l."
-    return text
-
-def rstrips(text, remove):
-    """
-    removes the string `remove` from the right of `text`
-
-        >>> rstrips("foobar", "bar")
-        'foo'
-    
-    """
-    return _strips('r', text, remove)
-
-def lstrips(text, remove):
-    """
-    removes the string `remove` from the left of `text`
-    
-        >>> lstrips("foobar", "foo")
-        'bar'
-    
-    """
-    return _strips('l', text, remove)
-
-def strips(text, remove):
-    """
-    removes the string `remove` from the both sides of `text`
-
-        >>> strips("foobarfoo", "foo")
-        'bar'
-    
-    """
-    return rstrips(lstrips(text, remove), remove)
-
-def safeunicode(obj, encoding='utf-8'):
-    r"""
-    Converts any given object to unicode string.
-    
-        >>> safeunicode('hello')
-        u'hello'
-        >>> safeunicode(2)
-        u'2'
-        >>> safeunicode('\xe1\x88\xb4')
-        u'\u1234'
-    """
-    if isinstance(obj, unicode):
-        return obj
-    elif isinstance(obj, str):
-        return obj.decode(encoding)
-    else:
-        if hasattr(obj, '__unicode__'):
-            return unicode(obj)
-        else:
-            return str(obj).decode(encoding)
-    
-def safestr(obj, encoding='utf-8'):
-    r"""
-    Converts any given object to utf-8 encoded string. 
-    
-        >>> safestr('hello')
-        'hello'
-        >>> safestr(u'\u1234')
-        '\xe1\x88\xb4'
-        >>> safestr(2)
-        '2'
-    """
-    if isinstance(obj, unicode):
-        return obj.encode('utf-8')
-    elif isinstance(obj, str):
-        return obj
-    elif hasattr(obj, 'next') and hasattr(obj, '__iter__'): # iterator
-        return itertools.imap(safestr, obj)
-    else:
-        return str(obj)
-
-# for backward-compatibility
-utf8 = safestr
-    
-class TimeoutError(Exception): pass
-def timelimit(timeout):
-    """
-    A decorator to limit a function to `timeout` seconds, raising `TimeoutError`
-    if it takes longer.
-    
-        >>> import time
-        >>> def meaningoflife():
-        ...     time.sleep(.2)
-        ...     return 42
-        >>> 
-        >>> timelimit(.1)(meaningoflife)()
-        Traceback (most recent call last):
-            ...
-        TimeoutError: took too long
-        >>> timelimit(1)(meaningoflife)()
-        42
-
-    _Caveat:_ The function isn't stopped after `timeout` seconds but continues 
-    executing in a separate thread. (There seems to be no way to kill a thread.)
-
-    inspired by <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878>
-    """
-    def _1(function):
-        def _2(*args, **kw):
-            class Dispatch(threading.Thread):
-                def __init__(self):
-                    threading.Thread.__init__(self)
-                    self.result = None
-                    self.error = None
-
-                    self.setDaemon(True)
-                    self.start()
-
-                def run(self):
-                    try:
-                        self.result = function(*args, **kw)
-                    except:
-                        self.error = sys.exc_info()
-
-            c = Dispatch()
-            c.join(timeout)
-            if c.isAlive():
-                raise TimeoutError, 'took too long'
-            if c.error:
-                raise c.error[0], c.error[1]
-            return c.result
-        return _2
-    return _1
-
-class Memoize:
-    """
-    'Memoizes' a function, caching its return values for each input.
-    
-        >>> import time
-        >>> def meaningoflife():
-        ...     time.sleep(.2)
-        ...     return 42
-        >>> fastlife = memoize(meaningoflife)
-        >>> meaningoflife()
-        42
-        >>> timelimit(.1)(meaningoflife)()
-        Traceback (most recent call last):
-            ...
-        TimeoutError: took too long
-        >>> fastlife()
-        42
-        >>> timelimit(.1)(fastlife)()
-        42
-    
-    """
-    def __init__(self, func): 
-        self.func = func
-        self.cache = {}
-    def __call__(self, *args, **keywords):
-        key = (args, tuple(keywords.items()))
-        if key not in self.cache: 
-            self.cache[key] = self.func(*args, **keywords)
-        return self.cache[key]
-
-memoize = Memoize
-
-re_compile = memoize(re.compile) #@@ threadsafe?
-re_compile.__doc__ = """
-A memoized version of re.compile.
-"""
-
-class _re_subm_proxy:
-    def __init__(self): 
-        self.match = None
-    def __call__(self, match): 
-        self.match = match
-        return ''
-
-def re_subm(pat, repl, string):
-    """
-    Like re.sub, but returns the replacement _and_ the match object.
-    
-        >>> t, m = re_subm('g(oo+)fball', r'f\\1lish', 'goooooofball')
-        >>> t
-        'foooooolish'
-        >>> m.groups()
-        ('oooooo',)
-    """
-    compiled_pat = re_compile(pat)
-    proxy = _re_subm_proxy()
-    compiled_pat.sub(proxy.__call__, string)
-    return compiled_pat.sub(repl, string), proxy.match
-
-def group(seq, size): 
-    """
-    Returns an iterator over a series of lists of length size from iterable.
-
-        >>> list(group([1,2,3,4], 2))
-        [[1, 2], [3, 4]]
-    """
-    if not hasattr(seq, 'next'):  
-        seq = iter(seq)
-    while True: 
-        yield [seq.next() for i in xrange(size)]
-
-def uniq(seq):
-   """
-   Removes duplicate elements from a list.
-
-       >>> uniq([1,2,3,1,4,5,6])
-       [1, 2, 3, 4, 5, 6]
-   """
-   seen = set()
-   result = []
-   for item in seq:
-       if item in seen: continue
-       seen.add(item)
-       result.append(item)
-   return result
-
-def iterview(x):
-   """
-   Takes an iterable `x` and returns an iterator over it
-   which prints its progress to stderr as it iterates through.
-   """
-   WIDTH = 70
-
-   def plainformat(n, lenx):
-       return '%5.1f%% (%*d/%d)' % ((float(n)/lenx)*100, len(str(lenx)), n, lenx)
-
-   def bars(size, n, lenx):
-       val = int((float(n)*size)/lenx + 0.5)
-       if size - val:
-           spacing = ">" + (" "*(size-val))[1:]
-       else:
-           spacing = ""
-       return "[%s%s]" % ("="*val, spacing)
-
-   def eta(elapsed, n, lenx):
-       if n == 0:
-           return '--:--:--'
-       if n == lenx:
-           secs = int(elapsed)
-       else:
-           secs = int((elapsed/n) * (lenx-n))
-       mins, secs = divmod(secs, 60)
-       hrs, mins = divmod(mins, 60)
-
-       return '%02d:%02d:%02d' % (hrs, mins, secs)
-
-   def format(starttime, n, lenx):
-       out = plainformat(n, lenx) + ' '
-       if n == lenx:
-           end = '     '
-       else:
-           end = ' ETA '
-       end += eta(time.time() - starttime, n, lenx)
-       out += bars(WIDTH - len(out) - len(end), n, lenx)
-       out += end
-       return out
-
-   starttime = time.time()
-   lenx = len(x)
-   for n, y in enumerate(x):
-       sys.stderr.write('\r' + format(starttime, n, lenx))
-       yield y
-   sys.stderr.write('\r' + format(starttime, n+1, lenx) + '\n')
-
-class IterBetter:
-    """
-    Returns an object that can be used as an iterator 
-    but can also be used via __getitem__ (although it 
-    cannot go backwards -- that is, you cannot request 
-    `iterbetter[0]` after requesting `iterbetter[1]`).
-    
-        >>> import itertools
-        >>> c = iterbetter(itertools.count())
-        >>> c[1]
-        1
-        >>> c[5]
-        5
-        >>> c[3]
-        Traceback (most recent call last):
-            ...
-        IndexError: already passed 3
-    """
-    def __init__(self, iterator): 
-        self.i, self.c = iterator, 0
-    def __iter__(self): 
-        while 1:    
-            yield self.i.next()
-            self.c += 1
-    def __getitem__(self, i):
-        #todo: slices
-        if i < self.c: 
-            raise IndexError, "already passed "+str(i)
-        try:
-            while i > self.c: 
-                self.i.next()
-                self.c += 1
-            # now self.c == i
-            self.c += 1
-            return self.i.next()
-        except StopIteration: 
-            raise IndexError, str(i)
-iterbetter = IterBetter
-
-def dictreverse(mapping):
-    """
-    Returns a new dictionary with keys and values swapped.
-    
-        >>> dictreverse({1: 2, 3: 4})
-        {2: 1, 4: 3}
-    """
-    return dict([(value, key) for (key, value) in mapping.iteritems()])
-
-def dictfind(dictionary, element):
-    """
-    Returns a key whose value in `dictionary` is `element` 
-    or, if none exists, None.
-    
-        >>> d = {1:2, 3:4}
-        >>> dictfind(d, 4)
-        3
-        >>> dictfind(d, 5)
-    """
-    for (key, value) in dictionary.iteritems():
-        if element is value: 
-            return key
-
-def dictfindall(dictionary, element):
-    """
-    Returns the keys whose values in `dictionary` are `element`
-    or, if none exists, [].
-    
-        >>> d = {1:4, 3:4}
-        >>> dictfindall(d, 4)
-        [1, 3]
-        >>> dictfindall(d, 5)
-        []
-    """
-    res = []
-    for (key, value) in dictionary.iteritems():
-        if element is value:
-            res.append(key)
-    return res
-
-def dictincr(dictionary, element):
-    """
-    Increments `element` in `dictionary`, 
-    setting it to one if it doesn't exist.
-    
-        >>> d = {1:2, 3:4}
-        >>> dictincr(d, 1)
-        3
-        >>> d[1]
-        3
-        >>> dictincr(d, 5)
-        1
-        >>> d[5]
-        1
-    """
-    dictionary.setdefault(element, 0)
-    dictionary[element] += 1
-    return dictionary[element]
-
-def dictadd(*dicts):
-    """
-    Returns a dictionary consisting of the keys in the argument dictionaries.
-    If they share a key, the value from the last argument is used.
-    
-        >>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1})
-        {1: 0, 2: 1, 3: 1}
-    """
-    result = {}
-    for dct in dicts:
-        result.update(dct)
-    return result
-
-def listget(lst, ind, default=None):
-    """
-    Returns `lst[ind]` if it exists, `default` otherwise.
-    
-        >>> listget(['a'], 0)
-        'a'
-        >>> listget(['a'], 1)
-        >>> listget(['a'], 1, 'b')
-        'b'
-    """
-    if len(lst)-1 < ind: 
-        return default
-    return lst[ind]
-
-def intget(integer, default=None):
-    """
-    Returns `integer` as an int or `default` if it can't.
-    
-        >>> intget('3')
-        3
-        >>> intget('3a')
-        >>> intget('3a', 0)
-        0
-    """
-    try:
-        return int(integer)
-    except (TypeError, ValueError):
-        return default
-
-def datestr(then, now=None):
-    """
-    Converts a (UTC) datetime object to a nice string representation.
-    
-        >>> from datetime import datetime, timedelta
-        >>> d = datetime(1970, 5, 1)
-        >>> datestr(d, now=d)
-        '0 microseconds ago'
-        >>> for t, v in {
-        ...   timedelta(microseconds=1): '1 microsecond ago',
-        ...   timedelta(microseconds=2): '2 microseconds ago',
-        ...   -timedelta(microseconds=1): '1 microsecond from now',
-        ...   -timedelta(microseconds=2): '2 microseconds from now',
-        ...   timedelta(microseconds=2000): '2 milliseconds ago',
-        ...   timedelta(seconds=2): '2 seconds ago',
-        ...   timedelta(seconds=2*60): '2 minutes ago',
-        ...   timedelta(seconds=2*60*60): '2 hours ago',
-        ...   timedelta(days=2): '2 days ago',
-        ... }.iteritems():
-        ...     assert datestr(d, now=d+t) == v
-        >>> datestr(datetime(1970, 1, 1), now=d)
-        'January  1'
-        >>> datestr(datetime(1969, 1, 1), now=d)
-        'January  1, 1969'
-        >>> datestr(datetime(1970, 6, 1), now=d)
-        'June  1, 1970'
-        >>> datestr(None)
-        ''
-    """
-    def agohence(n, what, divisor=None):
-        if divisor: n = n // divisor
-
-        out = str(abs(n)) + ' ' + what       # '2 day'
-        if abs(n) != 1: out += 's'           # '2 days'
-        out += ' '                           # '2 days '
-        if n < 0:
-            out += 'from now'
-        else:
-            out += 'ago'
-        return out                           # '2 days ago'
-
-    oneday = 24 * 60 * 60
-
-    if not then: return ""
-    if not now: now = datetime.datetime.utcnow()
-    if type(now).__name__ == "DateTime":
-        now = datetime.datetime.fromtimestamp(now)
-    if type(then).__name__ == "DateTime":
-        then = datetime.datetime.fromtimestamp(then)
-    elif type(then).__name__ == "date":
-        then = datetime.datetime(then.year, then.month, then.day)
-
-    delta = now - then
-    deltaseconds = int(delta.days * oneday + delta.seconds + delta.microseconds * 1e-06)
-    deltadays = abs(deltaseconds) // oneday
-    if deltaseconds < 0: deltadays *= -1 # fix for oddity of floor
-
-    if deltadays:
-        if abs(deltadays) < 4:
-            return agohence(deltadays, 'day')
-
-        out = then.strftime('%B %e') # e.g. 'June 13'
-        if then.year != now.year or deltadays < 0:
-            out += ', %s' % then.year
-        return out
-
-    if int(deltaseconds):
-        if abs(deltaseconds) > (60 * 60):
-            return agohence(deltaseconds, 'hour', 60 * 60)
-        elif abs(deltaseconds) > 60:
-            return agohence(deltaseconds, 'minute', 60)
-        else:
-            return agohence(deltaseconds, 'second')
-
-    deltamicroseconds = delta.microseconds
-    if delta.days: deltamicroseconds = int(delta.microseconds - 1e6) # datetime oddity
-    if abs(deltamicroseconds) > 1000:
-        return agohence(deltamicroseconds, 'millisecond', 1000)
-
-    return agohence(deltamicroseconds, 'microsecond')
-
-def numify(string):
-    """
-    Removes all non-digit characters from `string`.
-    
-        >>> numify('800-555-1212')
-        '8005551212'
-        >>> numify('800.555.1212')
-        '8005551212'
-    
-    """
-    return ''.join([c for c in str(string) if c.isdigit()])
-
-def denumify(string, pattern):
-    """
-    Formats `string` according to `pattern`, where the letter X gets replaced
-    by characters from `string`.
-    
-        >>> denumify("8005551212", "(XXX) XXX-XXXX")
-        '(800) 555-1212'
-    
-    """
-    out = []
-    for c in pattern:
-        if c == "X":
-            out.append(string[0])
-            string = string[1:]
-        else:
-            out.append(c)
-    return ''.join(out)
-
-def commify(n):
-    """
-    Add commas to an integer `n`.
-
-        >>> commify(1)
-        '1'
-        >>> commify(123)
-        '123'
-        >>> commify(1234)
-        '1,234'
-        >>> commify(1234567890)
-        '1,234,567,890'
-        >>> commify(123.0)
-        '123.0'
-        >>> commify(1234.5)
-        '1,234.5'
-        >>> commify(1234.56789)
-        '1,234.56789'
-        >>> commify('%.2f' % 1234.5)
-        '1,234.50'
-        >>> commify(None)
-        >>>
-
-    """
-    if n is None: return None
-    n = str(n)
-    if '.' in n:
-        dollars, cents = n.split('.')
-    else:
-        dollars, cents = n, None
-
-    r = []
-    for i, c in enumerate(str(dollars)[::-1]):
-        if i and (not (i % 3)):
-            r.insert(0, ',')
-        r.insert(0, c)
-    out = ''.join(r)
-    if cents:
-        out += '.' + cents
-    return out
-
-def dateify(datestring):
-    """
-    Formats a numified `datestring` properly.
-    """
-    return denumify(datestring, "XXXX-XX-XX XX:XX:XX")
-
-
-def nthstr(n):
-    """
-    Formats an ordinal.
-    Doesn't handle negative numbers.
-
-        >>> nthstr(1)
-        '1st'
-        >>> nthstr(0)
-        '0th'
-        >>> [nthstr(x) for x in [2, 3, 4, 5, 10, 11, 12, 13, 14, 15]]
-        ['2nd', '3rd', '4th', '5th', '10th', '11th', '12th', '13th', '14th', '15th']
-        >>> [nthstr(x) for x in [91, 92, 93, 94, 99, 100, 101, 102]]
-        ['91st', '92nd', '93rd', '94th', '99th', '100th', '101st', '102nd']
-        >>> [nthstr(x) for x in [111, 112, 113, 114, 115]]
-        ['111th', '112th', '113th', '114th', '115th']
-
-    """
-    
-    assert n >= 0
-    if n % 100 in [11, 12, 13]: return '%sth' % n
-    return {1: '%sst', 2: '%snd', 3: '%srd'}.get(n % 10, '%sth') % n
-
-def cond(predicate, consequence, alternative=None):
-    """
-    Function replacement for if-else to use in expressions.
-        
-        >>> x = 2
-        >>> cond(x % 2 == 0, "even", "odd")
-        'even'
-        >>> cond(x % 2 == 0, "even", "odd") + '_row'
-        'even_row'
-    """
-    if predicate:
-        return consequence
-    else:
-        return alternative
-
-class CaptureStdout:
-    """
-    Captures everything `func` prints to stdout and returns it instead.
-    
-        >>> def idiot():
-        ...     print "foo"
-        >>> capturestdout(idiot)()
-        'foo\\n'
-    
-    **WARNING:** Not threadsafe!
-    """
-    def __init__(self, func): 
-        self.func = func
-    def __call__(self, *args, **keywords):
-        from cStringIO import StringIO
-        # Not threadsafe!
-        out = StringIO()
-        oldstdout = sys.stdout
-        sys.stdout = out
-        try: 
-            self.func(*args, **keywords)
-        finally: 
-            sys.stdout = oldstdout
-        return out.getvalue()
-
-capturestdout = CaptureStdout
-
-class Profile:
-    """
-    Profiles `func` and returns a tuple containing its output
-    and a string with human-readable profiling information.
-        
-        >>> import time
-        >>> out, inf = profile(time.sleep)(.001)
-        >>> out
-        >>> inf[:10].strip()
-        'took 0.0'
-    """
-    def __init__(self, func): 
-        self.func = func
-    def __call__(self, *args): ##, **kw):   kw unused
-        import hotshot, hotshot.stats, tempfile ##, time already imported
-        temp = tempfile.NamedTemporaryFile()
-        prof = hotshot.Profile(temp.name)
-
-        stime = time.time()
-        result = prof.runcall(self.func, *args)
-        stime = time.time() - stime
-        prof.close()
-
-        import cStringIO
-        out = cStringIO.StringIO()
-        stats = hotshot.stats.load(temp.name)
-        stats.stream = out
-        stats.strip_dirs()
-        stats.sort_stats('time', 'calls')
-        stats.print_stats(40)
-        stats.print_callers()
-
-        x =  '\n\ntook '+ str(stime) + ' seconds\n'
-        x += out.getvalue()
-
-        return result, x
-
-profile = Profile
-
-
-import traceback
-# hack for compatibility with Python 2.3:
-if not hasattr(traceback, 'format_exc'):
-    from cStringIO import StringIO
-    def format_exc(limit=None):
-        strbuf = StringIO()
-        traceback.print_exc(limit, strbuf)
-        return strbuf.getvalue()
-    traceback.format_exc = format_exc
-
-def tryall(context, prefix=None):
-    """
-    Tries a series of functions and prints their results. 
-    `context` is a dictionary mapping names to values; 
-    the value will only be tried if it's callable.
-    
-        >>> tryall(dict(j=lambda: True))
-        j: True
-        ----------------------------------------
-        results:
-           True: 1
-
-    For example, you might have a file `test/stuff.py` 
-    with a series of functions testing various things in it. 
-    At the bottom, have a line:
-
-        if __name__ == "__main__": tryall(globals())
-
-    Then you can run `python test/stuff.py` and get the results of 
-    all the tests.
-    """
-    context = context.copy() # vars() would update
-    results = {}
-    for (key, value) in context.iteritems():
-        if not hasattr(value, '__call__'): 
-            continue
-        if prefix and not key.startswith(prefix): 
-            continue
-        print key + ':',
-        try:
-            r = value()
-            dictincr(results, r)
-            print r
-        except:
-            print 'ERROR'
-            dictincr(results, 'ERROR')
-            print '   ' + '\n   '.join(traceback.format_exc().split('\n'))
-        
-    print '-'*40
-    print 'results:'
-    for (key, value) in results.iteritems():
-        print ' '*2, str(key)+':', value
-        
-class ThreadedDict:
-    """
-    Thread local storage.
-    
-        >>> d = ThreadedDict()
-        >>> d.x = 1
-        >>> d.x
-        1
-        >>> import threading
-        >>> def f(): d.x = 2
-        ...
-        >>> t = threading.Thread(target=f)
-        >>> t.start()
-        >>> t.join()
-        >>> d.x
-        1
-    """
-    def __getattr__(self, key):
-        return getattr(self._getd(), key)
-
-    def __setattr__(self, key, value):
-        return setattr(self._getd(), key, value)
-
-    def __delattr__(self, key):
-        return delattr(self._getd(), key)
-
-    def __hash__(self): 
-        return id(self)
-
-    def _getd(self):
-        t = threading.currentThread()
-        if not hasattr(t, '_d'):
-            # using __dict__ of thread as thread local storage
-            t._d = {}
-
-        # there could be multiple instances of ThreadedDict.
-        # use self as key
-        if self not in t._d:
-            t._d[self] = storage()
-        return t._d[self]
-
-threadeddict = ThreadedDict
-
-def autoassign(self, locals):
-    """
-    Automatically assigns local variables to `self`.
-    
-        >>> self = storage()
-        >>> autoassign(self, dict(a=1, b=2))
-        >>> self
-        <Storage {'a': 1, 'b': 2}>
-    
-    Generally used in `__init__` methods, as in:
-
-        def __init__(self, foo, bar, baz=1): autoassign(self, locals())
-    """
-    for (key, value) in locals.iteritems():
-        if key == 'self': 
-            continue
-        setattr(self, key, value)
-
-def to36(q):
-    """
-    Converts an integer to base 36 (a useful scheme for human-sayable IDs).
-    
-        >>> to36(35)
-        'z'
-        >>> to36(119292)
-        '2k1o'
-        >>> int(to36(939387374), 36)
-        939387374
-        >>> to36(0)
-        '0'
-        >>> to36(-393)
-        Traceback (most recent call last):
-            ... 
-        ValueError: must supply a positive integer
-    
-    """
-    if q < 0: raise ValueError, "must supply a positive integer"
-    letters = "0123456789abcdefghijklmnopqrstuvwxyz"
-    converted = []
-    while q != 0:
-        q, r = divmod(q, 36)
-        converted.insert(0, letters[r])
-    return "".join(converted) or '0'
-
-
-r_url = re_compile('(?<!\()(http://(\S+))')
-def safemarkdown(text):
-    """
-    Converts text to HTML following the rules of Markdown, but blocking any
-    outside HTML input, so that only the things supported by Markdown
-    can be used. Also converts raw URLs to links.
-
-    (requires [markdown.py](http://webpy.org/markdown.py))
-    """
-    from markdown import markdown
-    if text:
-        text = text.replace('<', '&lt;')
-        # TODO: automatically get page title?
-        text = r_url.sub(r'<\1>', text)
-        text = markdown(text)
-        return text
-
-def sendmail(from_address, to_address, subject, message, headers=None, **kw):
-    """
-    Sends the email message `message` with mail and envelope headers
-    for from `from_address_` to `to_address` with `subject`. 
-    Additional email headers can be specified with the dictionary 
-    `headers.
-
-    If `web.config.smtp_server` is set, it will send the message
-    to that SMTP server. Otherwise it will look for 
-    `/usr/sbin/sendmail`, the typical location for the sendmail-style
-    binary. To use sendmail from a different path, set `web.config.sendmail_path`.
-    """
-    try:
-        import webapi
-    except ImportError:
-        webapi = Storage(config=Storage())
-    
-    if headers is None: headers = {}
-    
-    cc = kw.get('cc', [])
-    bcc = kw.get('bcc', [])
-    
-    def listify(x):
-        if not isinstance(x, list):
-            return [safestr(x)]
-        else:
-            return [safestr(a) for a in x]
-
-    from_address = safestr(from_address)
-
-    to_address = listify(to_address)
-    cc = listify(cc)
-    bcc = listify(bcc)
-
-    recipients = to_address + cc + bcc
-    
-    headers = dictadd({
-      'MIME-Version': '1.0',
-      'Content-Type': 'text/plain; charset=UTF-8',
-      'Content-Disposition': 'inline',
-      'From': from_address,
-      'To': ", ".join(to_address),
-      'Subject': subject
-    }, headers)
-
-    if cc:
-        headers['Cc'] = ", ".join(cc)
-    
-    import email.Utils
-    from_address = email.Utils.parseaddr(from_address)[1]
-    recipients = [email.Utils.parseaddr(r)[1] for r in recipients]
-    message = ('\n'.join([safestr('%s: %s' % x) for x in headers.iteritems()])
-      + "\n\n" +  safestr(message))
-
-    if webapi.config.get('smtp_server'):
-        server = webapi.config.get('smtp_server')
-        port = webapi.config.get('smtp_port', 0)
-        username = webapi.config.get('smtp_username') 
-        password = webapi.config.get('smtp_password')
-        debug_level = webapi.config.get('smtp_debuglevel', None)
-        starttls = webapi.config.get('smtp_starttls', False)
-
-        import smtplib
-        smtpserver = smtplib.SMTP(server, port)
-
-        if debug_level:
-            smtpserver.set_debuglevel(debug_level)
-
-        if starttls:
-            smtpserver.ehlo()
-            smtpserver.starttls()
-            smtpserver.ehlo()
-
-        if username and password:
-            smtpserver.login(username, password)
-
-        smtpserver.sendmail(from_address, recipients, message)
-        smtpserver.quit()
-    else:
-        sendmail = webapi.config.get('sendmail_path', '/usr/sbin/sendmail')
-        
-        assert not from_address.startswith('-'), 'security'
-        for r in recipients:
-            assert not r.startswith('-'), 'security'
-                
-
-        if subprocess:
-            p = subprocess.Popen(['/usr/sbin/sendmail', '-f', from_address] + recipients, stdin=subprocess.PIPE)
-            p.stdin.write(message)
-            p.stdin.close()
-            p.wait()
-        else:
-            import os
-            i, o = os.popen2(["/usr/lib/sendmail", '-f', from_address] + recipients)
-            i.write(message)
-            i.close()
-            o.close()
-            del i, o
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
--- a/bundled/webpy/web/webapi.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,368 +0,0 @@
-"""
-Web API (wrapper around WSGI)
-(from web.py)
-"""
-
-__all__ = [
-    "config",
-    "header", "debug",
-    "input", "data",
-    "setcookie", "cookies",
-    "ctx", 
-    "HTTPError", 
-
-    # 200, 201, 202
-    "OK", "Created", "Accepted",    
-    "ok", "created", "accepted",
-    
-    # 301, 302, 303, 304, 407
-    "Redirect", "Found", "SeeOther", "NotModified", "TempRedirect", 
-    "redirect", "found", "seeother", "notmodified", "tempredirect",
-
-    # 400, 401, 403, 404, 405, 406, 409, 410, 412
-    "BadRequest", "Unauthorized", "Forbidden", "NoMethod", "NotFound", "NotAcceptable", "Conflict", "Gone", "PreconditionFailed",
-    "badrequest", "unauthorized", "forbidden", "nomethod", "notfound", "notacceptable", "conflict", "gone", "preconditionfailed",
-
-    # 500
-    "InternalError", 
-    "internalerror",
-]
-
-import sys, cgi, Cookie, pprint, urlparse, urllib
-from utils import storage, storify, threadeddict, dictadd, intget, utf8
-
-config = storage()
-config.__doc__ = """
-A configuration object for various aspects of web.py.
-
-`debug`
-   : when True, enables reloading, disabled template caching and sets internalerror to debugerror.
-"""
-
-class HTTPError(Exception):
-    def __init__(self, status, headers={}, data=""):
-        ctx.status = status
-        for k, v in headers.items():
-            header(k, v)
-        self.data = data
-        Exception.__init__(self, status)
-        
-def _status_code(status, data=None, classname=None, docstring=None):
-    if data is None:
-        data = status.split(" ", 1)[1]
-    classname = status.split(" ", 1)[1].replace(' ', '') # 304 Not Modified -> NotModified    
-    docstring = docstring or '`%s` status' % status
-
-    def __init__(self, data=data, headers={}):
-        HTTPError.__init__(self, status, headers, data)
-        
-    # trick to create class dynamically with dynamic docstring.
-    return type(classname, (HTTPError, object), {
-        '__doc__': docstring,
-        '__init__': __init__
-    })
-
-ok = OK = _status_code("200 OK", data="")
-created = Created = _status_code("201 Created")
-accepted = Accepted = _status_code("202 Accepted")
-
-class Redirect(HTTPError):
-    """A `301 Moved Permanently` redirect."""
-    def __init__(self, url, status='301 Moved Permanently', absolute=False):
-        """
-        Returns a `status` redirect to the new URL. 
-        `url` is joined with the base URL so that things like 
-        `redirect("about") will work properly.
-        """
-        newloc = urlparse.urljoin(ctx.path, url)
-
-        if newloc.startswith('/'):
-            if absolute:
-                home = ctx.realhome
-            else:
-                home = ctx.home
-            newloc = home + newloc
-
-        headers = {
-            'Content-Type': 'text/html',
-            'Location': newloc
-        }
-        HTTPError.__init__(self, status, headers, "")
-
-redirect = Redirect
-
-class Found(Redirect):
-    """A `302 Found` redirect."""
-    def __init__(self, url, absolute=False):
-        Redirect.__init__(self, url, '302 Found', absolute=absolute)
-
-found = Found
-
-class SeeOther(Redirect):
-    """A `303 See Other` redirect."""
-    def __init__(self, url, absolute=False):
-        Redirect.__init__(self, url, '303 See Other', absolute=absolute)
-    
-seeother = SeeOther
-
-class NotModified(HTTPError):
-    """A `304 Not Modified` status."""
-    def __init__(self):
-        HTTPError.__init__(self, "304 Not Modified")
-
-notmodified = NotModified
-
-class TempRedirect(Redirect):
-    """A `307 Temporary Redirect` redirect."""
-    def __init__(self, url, absolute=False):
-        Redirect.__init__(self, url, '307 Temporary Redirect', absolute=absolute)
-
-tempredirect = TempRedirect
-
-class BadRequest(HTTPError):
-    """`400 Bad Request` error."""
-    message = "bad request"
-    def __init__(self):
-        status = "400 Bad Request"
-        headers = {'Content-Type': 'text/html'}
-        HTTPError.__init__(self, status, headers, self.message)
-
-badrequest = BadRequest
-
-class _NotFound(HTTPError):
-    """`404 Not Found` error."""
-    message = "not found"
-    def __init__(self, message=None):
-        status = '404 Not Found'
-        headers = {'Content-Type': 'text/html'}
-        HTTPError.__init__(self, status, headers, message or self.message)
-
-def NotFound(message=None):
-    """Returns HTTPError with '404 Not Found' error from the active application.
-    """
-    if message:
-        return _NotFound(message)
-    elif ctx.get('app_stack'):
-        return ctx.app_stack[-1].notfound()
-    else:
-        return _NotFound()
-
-notfound = NotFound
-
-unauthorized = Unauthorized = _status_code("401 Unauthorized")
-forbidden = Forbidden = _status_code("403 Forbidden")
-notacceptable = NotAcceptable = _status_code("406 Not Acceptable")
-conflict = Conflict = _status_code("409 Conflict")
-preconditionfailed = PreconditionFailed = _status_code("412 Precondition Failed")
-
-class NoMethod(HTTPError):
-    """A `405 Method Not Allowed` error."""
-    def __init__(self, cls=None):
-        status = '405 Method Not Allowed'
-        headers = {}
-        headers['Content-Type'] = 'text/html'
-        
-        methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE']
-        if cls:
-            methods = [method for method in methods if hasattr(cls, method)]
-
-        headers['Allow'] = ', '.join(methods)
-        data = None
-        HTTPError.__init__(self, status, headers, data)
-        
-nomethod = NoMethod
-
-class Gone(HTTPError):
-    """`410 Gone` error."""
-    message = "gone"
-    def __init__(self):
-        status = '410 Gone'
-        headers = {'Content-Type': 'text/html'}
-        HTTPError.__init__(self, status, headers, self.message)
-
-gone = Gone
-
-class _InternalError(HTTPError):
-    """500 Internal Server Error`."""
-    message = "internal server error"
-    
-    def __init__(self, message=None):
-        status = '500 Internal Server Error'
-        headers = {'Content-Type': 'text/html'}
-        HTTPError.__init__(self, status, headers, message or self.message)
-
-def InternalError(message=None):
-    """Returns HTTPError with '500 internal error' error from the active application.
-    """
-    if message:
-        return _InternalError(message)
-    elif ctx.get('app_stack'):
-        return ctx.app_stack[-1].internalerror()
-    else:
-        return _InternalError()
-
-internalerror = InternalError
-
-def header(hdr, value, unique=False):
-    """
-    Adds the header `hdr: value` with the response.
-    
-    If `unique` is True and a header with that name already exists,
-    it doesn't add a new one. 
-    """
-    hdr, value = utf8(hdr), utf8(value)
-    # protection against HTTP response splitting attack
-    if '\n' in hdr or '\r' in hdr or '\n' in value or '\r' in value:
-        raise ValueError, 'invalid characters in header'
-        
-    if unique is True:
-        for h, v in ctx.headers:
-            if h.lower() == hdr.lower(): return
-    
-    ctx.headers.append((hdr, value))
-
-def input(*requireds, **defaults):
-    """
-    Returns a `storage` object with the GET and POST arguments. 
-    See `storify` for how `requireds` and `defaults` work.
-    """
-    from cStringIO import StringIO
-    def dictify(fs): 
-        # hack to make web.input work with enctype='text/plain.
-        if fs.list is None:
-            fs.list = [] 
-
-        return dict([(k, fs[k]) for k in fs.keys()])
-    
-    _method = defaults.pop('_method', 'both')
-    
-    e = ctx.env.copy()
-    a = b = {}
-    
-    if _method.lower() in ['both', 'post', 'put']:
-        if e['REQUEST_METHOD'] in ['POST', 'PUT']:
-            if e.get('CONTENT_TYPE', '').lower().startswith('multipart/'):
-                # since wsgi.input is directly passed to cgi.FieldStorage, 
-                # it can not be called multiple times. Saving the FieldStorage
-                # object in ctx to allow calling web.input multiple times.
-                a = ctx.get('_fieldstorage')
-                if not a:
-                    fp = e['wsgi.input']
-                    a = cgi.FieldStorage(fp=fp, environ=e, keep_blank_values=1)
-                    ctx._fieldstorage = a
-            else:
-                fp = StringIO(data())
-                a = cgi.FieldStorage(fp=fp, environ=e, keep_blank_values=1)
-            a = dictify(a)
-
-    if _method.lower() in ['both', 'get']:
-        e['REQUEST_METHOD'] = 'GET'
-        b = dictify(cgi.FieldStorage(environ=e, keep_blank_values=1))
-
-    out = dictadd(b, a)
-    try:
-        defaults.setdefault('_unicode', True) # force unicode conversion by default.
-        return storify(out, *requireds, **defaults)
-    except KeyError:
-        raise badrequest()
-
-def data():
-    """Returns the data sent with the request."""
-    if 'data' not in ctx:
-        cl = intget(ctx.env.get('CONTENT_LENGTH'), 0)
-        ctx.data = ctx.env['wsgi.input'].read(cl)
-    return ctx.data
-
-def setcookie(name, value, expires="", domain=None, secure=False):
-    """Sets a cookie."""
-    if expires < 0: 
-        expires = -1000000000 
-    kargs = {'expires': expires, 'path':'/'}
-    if domain: 
-        kargs['domain'] = domain
-    if secure:
-        kargs['secure'] = secure
-    # @@ should we limit cookies to a different path?
-    cookie = Cookie.SimpleCookie()
-    cookie[name] = urllib.quote(utf8(value))
-    for key, val in kargs.iteritems(): 
-        cookie[name][key] = val
-    header('Set-Cookie', cookie.items()[0][1].OutputString())
-
-def cookies(*requireds, **defaults):
-    """
-    Returns a `storage` object with all the cookies in it.
-    See `storify` for how `requireds` and `defaults` work.
-    """
-    cookie = Cookie.SimpleCookie()
-    cookie.load(ctx.env.get('HTTP_COOKIE', ''))
-    try:
-        d = storify(cookie, *requireds, **defaults)
-        for k, v in d.items():
-            d[k] = v and urllib.unquote(v)
-        return d
-    except KeyError:
-        badrequest()
-        raise StopIteration
-
-def debug(*args):
-    """
-    Prints a prettyprinted version of `args` to stderr.
-    """
-    try: 
-        out = ctx.environ['wsgi.errors']
-    except: 
-        out = sys.stderr
-    for arg in args:
-        print >> out, pprint.pformat(arg)
-    return ''
-
-def _debugwrite(x):
-    try: 
-        out = ctx.environ['wsgi.errors']
-    except: 
-        out = sys.stderr
-    out.write(x)
-debug.write = _debugwrite
-
-ctx = context = threadeddict()
-
-ctx.__doc__ = """
-A `storage` object containing various information about the request:
-  
-`environ` (aka `env`)
-   : A dictionary containing the standard WSGI environment variables.
-
-`host`
-   : The domain (`Host` header) requested by the user.
-
-`home`
-   : The base path for the application.
-
-`ip`
-   : The IP address of the requester.
-
-`method`
-   : The HTTP method used.
-
-`path`
-   : The path request.
-   
-`query`
-   : If there are no query arguments, the empty string. Otherwise, a `?` followed
-     by the query string.
-
-`fullpath`
-   : The full path requested, including query arguments (`== path + query`).
-
-### Response Data
-
-`status` (default: "200 OK")
-   : The status code to be used in the response.
-
-`headers`
-   : A list of 2-tuples to be used in the response.
-
-`output`
-   : A string to be used as the response.
-"""
--- a/bundled/webpy/web/webopenid.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-"""openid.py: an openid library for web.py
-
-Notes:
-
- - This will create a file called .openid_secret_key in the 
-   current directory with your secret key in it. If someone 
-   has access to this file they can log in as any user. And 
-   if the app can't find this file for any reason (e.g. you 
-   moved the app somewhere else) then each currently logged 
-   in user will get logged out.
-
- - State must be maintained through the entire auth process 
-   -- this means that if you have multiple web.py processes 
-   serving one set of URLs or if you restart your app often 
-   then log ins will fail. You have to replace sessions and 
-   store for things to work.
-
- - We set cookies starting with "openid_".
-
-"""
-
-import os
-import random
-import hmac
-import __init__ as web
-import openid.consumer.consumer
-import openid.store.memstore
-
-sessions = {}
-store = openid.store.memstore.MemoryStore()
-
-def _secret():
-    try:
-        secret = file('.openid_secret_key').read()
-    except IOError:
-        # file doesn't exist
-        secret = os.urandom(20)
-        file('.openid_secret_key', 'w').write(secret)
-    return secret
-
-def _hmac(identity_url):
-    return hmac.new(_secret(), identity_url).hexdigest()
-
-def _random_session():
-    n = random.random()
-    while n in sessions:
-        n = random.random()
-    n = str(n)
-    return n
-
-def status():
-    oid_hash = web.cookies().get('openid_identity_hash', '').split(',', 1)
-    if len(oid_hash) > 1:
-        oid_hash, identity_url = oid_hash
-        if oid_hash == _hmac(identity_url):
-            return identity_url
-    return None
-
-def form(openid_loc):
-    oid = status()
-    if oid:
-        return '''
-        <form method="post" action="%s">
-          <img src="http://openid.net/login-bg.gif" alt="OpenID" />
-          <strong>%s</strong>
-          <input type="hidden" name="action" value="logout" />
-          <input type="hidden" name="return_to" value="%s" />
-          <button type="submit">log out</button>
-        </form>''' % (openid_loc, oid, web.ctx.fullpath)
-    else:
-        return '''
-        <form method="post" action="%s">
-          <input type="text" name="openid" value="" 
-            style="background: url(http://openid.net/login-bg.gif) no-repeat; padding-left: 18px; background-position: 0 50%%;" />
-          <input type="hidden" name="return_to" value="%s" />
-          <button type="submit">log in</button>
-        </form>''' % (openid_loc, web.ctx.fullpath)
-
-def logout():
-    web.setcookie('openid_identity_hash', '', expires=-1)
-
-class host:
-    def POST(self):
-        # unlike the usual scheme of things, the POST is actually called
-        # first here
-        i = web.input(return_to='/')
-        if i.get('action') == 'logout':
-            logout()
-            return web.redirect(i.return_to)
-
-        i = web.input('openid', return_to='/')
-
-        n = _random_session()
-        sessions[n] = {'webpy_return_to': i.return_to}
-        
-        c = openid.consumer.consumer.Consumer(sessions[n], store)
-        a = c.begin(i.openid)
-        f = a.redirectURL(web.ctx.home, web.ctx.home + web.ctx.fullpath)
-
-        web.setcookie('openid_session_id', n)
-        return web.redirect(f)
-
-    def GET(self):
-        n = web.cookies('openid_session_id').openid_session_id
-        web.setcookie('openid_session_id', '', expires=-1)
-        return_to = sessions[n]['webpy_return_to']
-
-        c = openid.consumer.consumer.Consumer(sessions[n], store)
-        a = c.complete(web.input(), web.ctx.home + web.ctx.fullpath)
-
-        if a.status.lower() == 'success':
-            web.setcookie('openid_identity_hash', _hmac(a.identity_url) + ',' + a.identity_url)
-
-        del sessions[n]
-        return web.redirect(return_to)
--- a/bundled/webpy/web/wsgi.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-"""
-WSGI Utilities
-(from web.py)
-"""
-
-import os, sys
-
-import http
-import webapi as web
-from utils import listget
-from net import validaddr, validip
-import httpserver
-    
-def runfcgi(func, addr=('localhost', 8000)):
-    """Runs a WSGI function as a FastCGI server."""
-    import flup.server.fcgi as flups
-    return flups.WSGIServer(func, multiplexed=True, bindAddress=addr).run()
-
-def runscgi(func, addr=('localhost', 4000)):
-    """Runs a WSGI function as an SCGI server."""
-    import flup.server.scgi as flups
-    return flups.WSGIServer(func, bindAddress=addr).run()
-
-def runwsgi(func):
-    """
-    Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server,
-    as appropriate based on context and `sys.argv`.
-    """
-    
-    if os.environ.has_key('SERVER_SOFTWARE'): # cgi
-        os.environ['FCGI_FORCE_CGI'] = 'Y'
-
-    if (os.environ.has_key('PHP_FCGI_CHILDREN') #lighttpd fastcgi
-      or os.environ.has_key('SERVER_SOFTWARE')):
-        return runfcgi(func, None)
-    
-    if 'fcgi' in sys.argv or 'fastcgi' in sys.argv:
-        args = sys.argv[1:]
-        if 'fastcgi' in args: args.remove('fastcgi')
-        elif 'fcgi' in args: args.remove('fcgi')
-        if args:
-            return runfcgi(func, validaddr(args[0]))
-        else:
-            return runfcgi(func, None)
-    
-    if 'scgi' in sys.argv:
-        args = sys.argv[1:]
-        args.remove('scgi')
-        if args:
-            return runscgi(func, validaddr(args[0]))
-        else:
-            return runscgi(func)
-    
-    return httpserver.runsimple(func, validip(listget(sys.argv, 1, '')))
-    
-def _is_dev_mode():
-    # quick hack to check if the program is running in dev mode.
-    if os.environ.has_key('SERVER_SOFTWARE') \
-        or os.environ.has_key('PHP_FCGI_CHILDREN') \
-        or 'fcgi' in sys.argv or 'fastcgi' in sys.argv \
-        or 'mod_wsgi' in sys.argv:
-            return False
-    return True
-
-# When running the builtin-server, enable debug mode if not already set.
-web.config.setdefault('debug', _is_dev_mode())
--- a/bundled/webpy/web/wsgiserver/LICENSE.txt	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-Copyright (c) 2004-2007, CherryPy Team (team@cherrypy.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice,
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice,
-      this list of conditions and the following disclaimer in the documentation
-      and/or other materials provided with the distribution.
-    * Neither the name of the CherryPy Team nor the names of its contributors
-      may be used to endorse or promote products derived from this software
-      without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- a/bundled/webpy/web/wsgiserver/__init__.py	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1794 +0,0 @@
-"""A high-speed, production ready, thread pooled, generic WSGI server.
-
-Simplest example on how to use this module directly
-(without using CherryPy's application machinery):
-
-    from cherrypy import wsgiserver
-    
-    def my_crazy_app(environ, start_response):
-        status = '200 OK'
-        response_headers = [('Content-type','text/plain')]
-        start_response(status, response_headers)
-        return ['Hello world!\n']
-    
-    server = wsgiserver.CherryPyWSGIServer(
-                ('0.0.0.0', 8070), my_crazy_app,
-                server_name='www.cherrypy.example')
-    
-The CherryPy WSGI server can serve as many WSGI applications 
-as you want in one instance by using a WSGIPathInfoDispatcher:
-    
-    d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
-    server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
-    
-Want SSL support? Just set these attributes:
-    
-    server.ssl_certificate = <filename>
-    server.ssl_private_key = <filename>
-    
-    if __name__ == '__main__':
-        try:
-            server.start()
-        except KeyboardInterrupt:
-            server.stop()
-
-This won't call the CherryPy engine (application side) at all, only the
-WSGI server, which is independant from the rest of CherryPy. Don't
-let the name "CherryPyWSGIServer" throw you; the name merely reflects
-its origin, not its coupling.
-
-For those of you wanting to understand internals of this module, here's the
-basic call flow. The server's listening thread runs a very tight loop,
-sticking incoming connections onto a Queue:
-
-    server = CherryPyWSGIServer(...)
-    server.start()
-    while True:
-        tick()
-        # This blocks until a request comes in:
-        child = socket.accept()
-        conn = HTTPConnection(child, ...)
-        server.requests.put(conn)
-
-Worker threads are kept in a pool and poll the Queue, popping off and then
-handling each connection in turn. Each connection can consist of an arbitrary
-number of requests and their responses, so we run a nested loop:
-
-    while True:
-        conn = server.requests.get()
-        conn.communicate()
-        ->  while True:
-                req = HTTPRequest(...)
-                req.parse_request()
-                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
-                    req.rfile.readline()
-                    req.read_headers()
-                req.respond()
-                ->  response = wsgi_app(...)
-                    try:
-                        for chunk in response:
-                            if chunk:
-                                req.write(chunk)
-                    finally:
-                        if hasattr(response, "close"):
-                            response.close()
-                if req.close_connection:
-                    return
-"""
-
-
-import base64
-import os
-import Queue
-import re
-quoted_slash = re.compile("(?i)%2F")
-import rfc822
-import socket
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
-
-_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
-
-import sys
-import threading
-import time
-import traceback
-from urllib import unquote
-from urlparse import urlparse
-import warnings
-
-try:
-    from OpenSSL import SSL
-    from OpenSSL import crypto
-except ImportError:
-    SSL = None
-
-import errno
-
-def plat_specific_errors(*errnames):
-    """Return error numbers for all errors in errnames on this platform.
-    
-    The 'errno' module contains different global constants depending on
-    the specific platform (OS). This function will return the list of
-    numeric values for a given list of potential names.
-    """
-    errno_names = dir(errno)
-    nums = [getattr(errno, k) for k in errnames if k in errno_names]
-    # de-dupe the list
-    return dict.fromkeys(nums).keys()
-
-socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
-
-socket_errors_to_ignore = plat_specific_errors(
-    "EPIPE",
-    "EBADF", "WSAEBADF",
-    "ENOTSOCK", "WSAENOTSOCK",
-    "ETIMEDOUT", "WSAETIMEDOUT",
-    "ECONNREFUSED", "WSAECONNREFUSED",
-    "ECONNRESET", "WSAECONNRESET",
-    "ECONNABORTED", "WSAECONNABORTED",
-    "ENETRESET", "WSAENETRESET",
-    "EHOSTDOWN", "EHOSTUNREACH",
-    )
-socket_errors_to_ignore.append("timed out")
-
-socket_errors_nonblocking = plat_specific_errors(
-    'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
-
-comma_separated_headers = ['ACCEPT', 'ACCEPT-CHARSET', 'ACCEPT-ENCODING',
-    'ACCEPT-LANGUAGE', 'ACCEPT-RANGES', 'ALLOW', 'CACHE-CONTROL',
-    'CONNECTION', 'CONTENT-ENCODING', 'CONTENT-LANGUAGE', 'EXPECT',
-    'IF-MATCH', 'IF-NONE-MATCH', 'PRAGMA', 'PROXY-AUTHENTICATE', 'TE',
-    'TRAILER', 'TRANSFER-ENCODING', 'UPGRADE', 'VARY', 'VIA', 'WARNING',
-    'WWW-AUTHENTICATE']
-
-
-class WSGIPathInfoDispatcher(object):
-    """A WSGI dispatcher for dispatch based on the PATH_INFO.
-    
-    apps: a dict or list of (path_prefix, app) pairs.
-    """
-    
-    def __init__(self, apps):
-        try:
-            apps = apps.items()
-        except AttributeError:
-            pass
-        
-        # Sort the apps by len(path), descending
-        apps.sort()
-        apps.reverse()
-        
-        # The path_prefix strings must start, but not end, with a slash.
-        # Use "" instead of "/".
-        self.apps = [(p.rstrip("/"), a) for p, a in apps]
-    
-    def __call__(self, environ, start_response):
-        path = environ["PATH_INFO"] or "/"
-        for p, app in self.apps:
-            # The apps list should be sorted by length, descending.
-            if path.startswith(p + "/") or path == p:
-                environ = environ.copy()
-                environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
-                environ["PATH_INFO"] = path[len(p):]
-                return app(environ, start_response)
-        
-        start_response('404 Not Found', [('Content-Type', 'text/plain'),
-                                         ('Content-Length', '0')])
-        return ['']
-
-
-class MaxSizeExceeded(Exception):
-    pass
-
-class SizeCheckWrapper(object):
-    """Wraps a file-like object, raising MaxSizeExceeded if too large."""
-    
-    def __init__(self, rfile, maxlen):
-        self.rfile = rfile
-        self.maxlen = maxlen
-        self.bytes_read = 0
-    
-    def _check_length(self):
-        if self.maxlen and self.bytes_read > self.maxlen:
-            raise MaxSizeExceeded()
-    
-    def read(self, size=None):
-        data = self.rfile.read(size)
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-    
-    def readline(self, size=None):
-        if size is not None:
-            data = self.rfile.readline(size)
-            self.bytes_read += len(data)
-            self._check_length()
-            return data
-        
-        # User didn't specify a size ...
-        # We read the line in chunks to make sure it's not a 100MB line !
-        res = []
-        while True:
-            data = self.rfile.readline(256)
-            self.bytes_read += len(data)
-            self._check_length()
-            res.append(data)
-            # See http://www.cherrypy.org/ticket/421
-            if len(data) < 256 or data[-1:] == "\n":
-                return ''.join(res)
-    
-    def readlines(self, sizehint=0):
-        # Shamelessly stolen from StringIO
-        total = 0
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline()
-        return lines
-    
-    def close(self):
-        self.rfile.close()
-    
-    def __iter__(self):
-        return self
-    
-    def next(self):
-        data = self.rfile.next()
-        self.bytes_read += len(data)
-        self._check_length()
-        return data
-
-
-class HTTPRequest(object):
-    """An HTTP Request (and response).
-    
-    A single HTTP connection may consist of multiple request/response pairs.
-    
-    send: the 'send' method from the connection's socket object.
-    wsgi_app: the WSGI application to call.
-    environ: a partial WSGI environ (server and connection entries).
-        The caller MUST set the following entries:
-        * All wsgi.* entries, including .input
-        * SERVER_NAME and SERVER_PORT
-        * Any SSL_* entries
-        * Any custom entries like REMOTE_ADDR and REMOTE_PORT
-        * SERVER_SOFTWARE: the value to write in the "Server" response header.
-        * ACTUAL_SERVER_PROTOCOL: the value to write in the Status-Line of
-            the response. From RFC 2145: "An HTTP server SHOULD send a
-            response version equal to the highest version for which the
-            server is at least conditionally compliant, and whose major
-            version is less than or equal to the one received in the
-            request.  An HTTP server MUST NOT send a version for which
-            it is not at least conditionally compliant."
-    
-    outheaders: a list of header tuples to write in the response.
-    ready: when True, the request has been parsed and is ready to begin
-        generating the response. When False, signals the calling Connection
-        that the response should not be generated and the connection should
-        close.
-    close_connection: signals the calling Connection that the request
-        should close. This does not imply an error! The client and/or
-        server may each request that the connection be closed.
-    chunked_write: if True, output will be encoded with the "chunked"
-        transfer-coding. This value is set automatically inside
-        send_headers.
-    """
-    
-    max_request_header_size = 0
-    max_request_body_size = 0
-    
-    def __init__(self, wfile, environ, wsgi_app):
-        self.rfile = environ['wsgi.input']
-        self.wfile = wfile
-        self.environ = environ.copy()
-        self.wsgi_app = wsgi_app
-        
-        self.ready = False
-        self.started_response = False
-        self.status = ""
-        self.outheaders = []
-        self.sent_headers = False
-        self.close_connection = False
-        self.chunked_write = False
-    
-    def parse_request(self):
-        """Parse the next HTTP request start-line and message-headers."""
-        self.rfile.maxlen = self.max_request_header_size
-        self.rfile.bytes_read = 0
-        
-        try:
-            self._parse_request()
-        except MaxSizeExceeded:
-            self.simple_response("413 Request Entity Too Large")
-            return
-    
-    def _parse_request(self):
-        # HTTP/1.1 connections are persistent by default. If a client
-        # requests a page, then idles (leaves the connection open),
-        # then rfile.readline() will raise socket.error("timed out").
-        # Note that it does this based on the value given to settimeout(),
-        # and doesn't need the client to request or acknowledge the close
-        # (although your TCP stack might suffer for it: cf Apache's history
-        # with FIN_WAIT_2).
-        request_line = self.rfile.readline()
-        if not request_line:
-            # Force self.ready = False so the connection will close.
-            self.ready = False
-            return
-        
-        if request_line == "\r\n":
-            # RFC 2616 sec 4.1: "...if the server is reading the protocol
-            # stream at the beginning of a message and receives a CRLF
-            # first, it should ignore the CRLF."
-            # But only ignore one leading line! else we enable a DoS.
-            request_line = self.rfile.readline()
-            if not request_line:
-                self.ready = False
-                return
-        
-        environ = self.environ
-        
-        try:
-            method, path, req_protocol = request_line.strip().split(" ", 2)
-        except ValueError:
-            self.simple_response(400, "Malformed Request-Line")
-            return
-        
-        environ["REQUEST_METHOD"] = method
-        
-        # path may be an abs_path (including "http://host.domain.tld");
-        scheme, location, path, params, qs, frag = urlparse(path)
-        
-        if frag:
-            self.simple_response("400 Bad Request",
-                                 "Illegal #fragment in Request-URI.")
-            return
-        
-        if scheme:
-            environ["wsgi.url_scheme"] = scheme
-        if params:
-            path = path + ";" + params
-        
-        environ["SCRIPT_NAME"] = ""
-        
-        # Unquote the path+params (e.g. "/this%20path" -> "this path").
-        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
-        #
-        # But note that "...a URI must be separated into its components
-        # before the escaped characters within those components can be
-        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
-        atoms = [unquote(x) for x in quoted_slash.split(path)]
-        path = "%2F".join(atoms)
-        environ["PATH_INFO"] = path
-        
-        # Note that, like wsgiref and most other WSGI servers,
-        # we unquote the path but not the query string.
-        environ["QUERY_STRING"] = qs
-        
-        # Compare request and server HTTP protocol versions, in case our
-        # server does not support the requested protocol. Limit our output
-        # to min(req, server). We want the following output:
-        #     request    server     actual written   supported response
-        #     protocol   protocol  response protocol    feature set
-        # a     1.0        1.0           1.0                1.0
-        # b     1.0        1.1           1.1                1.0
-        # c     1.1        1.0           1.0                1.0
-        # d     1.1        1.1           1.1                1.1
-        # Notice that, in (b), the response will be "HTTP/1.1" even though
-        # the client only understands 1.0. RFC 2616 10.5.6 says we should
-        # only return 505 if the _major_ version is different.
-        rp = int(req_protocol[5]), int(req_protocol[7])
-        server_protocol = environ["ACTUAL_SERVER_PROTOCOL"]
-        sp = int(server_protocol[5]), int(server_protocol[7])
-        if sp[0] != rp[0]:
-            self.simple_response("505 HTTP Version Not Supported")
-            return
-        # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
-        environ["SERVER_PROTOCOL"] = req_protocol
-        self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
-        
-        # If the Request-URI was an absoluteURI, use its location atom.
-        if location:
-            environ["SERVER_NAME"] = location
-        
-        # then all the http headers
-        try:
-            self.read_headers()
-        except ValueError, ex:
-            self.simple_response("400 Bad Request", repr(ex.args))
-            return
-        
-        mrbs = self.max_request_body_size
-        if mrbs and int(environ.get("CONTENT_LENGTH", 0)) > mrbs:
-            self.simple_response("413 Request Entity Too Large")
-            return
-        
-        # Persistent connection support
-        if self.response_protocol == "HTTP/1.1":
-            # Both server and client are HTTP/1.1
-            if environ.get("HTTP_CONNECTION", "") == "close":
-                self.close_connection = True
-        else:
-            # Either the server or client (or both) are HTTP/1.0
-            if environ.get("HTTP_CONNECTION", "") != "Keep-Alive":
-                self.close_connection = True
-        
-        # Transfer-Encoding support
-        te = None
-        if self.response_protocol == "HTTP/1.1":
-            te = environ.get("HTTP_TRANSFER_ENCODING")
-            if te:
-                te = [x.strip().lower() for x in te.split(",") if x.strip()]
-        
-        self.chunked_read = False
-        
-        if te:
-            for enc in te:
-                if enc == "chunked":
-                    self.chunked_read = True
-                else:
-                    # Note that, even if we see "chunked", we must reject
-                    # if there is an extension we don't recognize.
-                    self.simple_response("501 Unimplemented")
-                    self.close_connection = True
-                    return
-        
-        # From PEP 333:
-        # "Servers and gateways that implement HTTP 1.1 must provide
-        # transparent support for HTTP 1.1's "expect/continue" mechanism.
-        # This may be done in any of several ways:
-        #   1. Respond to requests containing an Expect: 100-continue request
-        #      with an immediate "100 Continue" response, and proceed normally.
-        #   2. Proceed with the request normally, but provide the application
-        #      with a wsgi.input stream that will send the "100 Continue"
-        #      response if/when the application first attempts to read from
-        #      the input stream. The read request must then remain blocked
-        #      until the client responds.
-        #   3. Wait until the client decides that the server does not support
-        #      expect/continue, and sends the request body on its own.
-        #      (This is suboptimal, and is not recommended.)
-        #
-        # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
-        # but it seems like it would be a big slowdown for such a rare case.
-        if environ.get("HTTP_EXPECT", "") == "100-continue":
-            self.simple_response(100)
-        
-        self.ready = True
-    
-    def read_headers(self):
-        """Read header lines from the incoming stream."""
-        environ = self.environ
-        
-        while True:
-            line = self.rfile.readline()
-            if not line:
-                # No more data--illegal end of headers
-                raise ValueError("Illegal end of headers.")
-            
-            if line == '\r\n':
-                # Normal end of headers
-                break
-            
-            if line[0] in ' \t':
-                # It's a continuation line.
-                v = line.strip()
-            else:
-                k, v = line.split(":", 1)
-                k, v = k.strip().upper(), v.strip()
-                envname = "HTTP_" + k.replace("-", "_")
-            
-            if k in comma_separated_headers:
-                existing = environ.get(envname)
-                if existing:
-                    v = ", ".join((existing, v))
-            environ[envname] = v
-        
-        ct = environ.pop("HTTP_CONTENT_TYPE", None)
-        if ct is not None:
-            environ["CONTENT_TYPE"] = ct
-        cl = environ.pop("HTTP_CONTENT_LENGTH", None)
-        if cl is not None:
-            environ["CONTENT_LENGTH"] = cl
-    
-    def decode_chunked(self):
-        """Decode the 'chunked' transfer coding."""
-        cl = 0
-        data = StringIO.StringIO()
-        while True:
-            line = self.rfile.readline().strip().split(";", 1)
-            chunk_size = int(line.pop(0), 16)
-            if chunk_size <= 0:
-                break
-##            if line: chunk_extension = line[0]
-            cl += chunk_size
-            data.write(self.rfile.read(chunk_size))
-            crlf = self.rfile.read(2)
-            if crlf != "\r\n":
-                self.simple_response("400 Bad Request",
-                                     "Bad chunked transfer coding "
-                                     "(expected '\\r\\n', got %r)" % crlf)
-                return
-        
-        # Grab any trailer headers
-        self.read_headers()
-        
-        data.seek(0)
-        self.environ["wsgi.input"] = data
-        self.environ["CONTENT_LENGTH"] = str(cl) or ""
-        return True
-    
-    def respond(self):
-        """Call the appropriate WSGI app and write its iterable output."""
-        # Set rfile.maxlen to ensure we don't read past Content-Length.
-        # This will also be used to read the entire request body if errors
-        # are raised before the app can read the body.
-        if self.chunked_read:
-            # If chunked, Content-Length will be 0.
-            self.rfile.maxlen = self.max_request_body_size
-        else:
-            cl = int(self.environ.get("CONTENT_LENGTH", 0))
-            if self.max_request_body_size:
-                self.rfile.maxlen = min(cl, self.max_request_body_size)
-            else:
-                self.rfile.maxlen = cl
-        self.rfile.bytes_read = 0
-        
-        try:
-            self._respond()
-        except MaxSizeExceeded:
-            if not self.sent_headers:
-                self.simple_response("413 Request Entity Too Large")
-            return
-    
-    def _respond(self):
-        if self.chunked_read:
-            if not self.decode_chunked():
-                self.close_connection = True
-                return
-        
-        response = self.wsgi_app(self.environ, self.start_response)
-        try:
-            for chunk in response:
-                # "The start_response callable must not actually transmit
-                # the response headers. Instead, it must store them for the
-                # server or gateway to transmit only after the first
-                # iteration of the application return value that yields
-                # a NON-EMPTY string, or upon the application's first
-                # invocation of the write() callable." (PEP 333)
-                if chunk:
-                    self.write(chunk)
-        finally:
-            if hasattr(response, "close"):
-                response.close()
-        
-        if (self.ready and not self.sent_headers):
-            self.sent_headers = True
-            self.send_headers()
-        if self.chunked_write:
-            self.wfile.sendall("0\r\n\r\n")
-    
-    def simple_response(self, status, msg=""):
-        """Write a simple response back to the client."""
-        status = str(status)
-        buf = ["%s %s\r\n" % (self.environ['ACTUAL_SERVER_PROTOCOL'], status),
-               "Content-Length: %s\r\n" % len(msg),
-               "Content-Type: text/plain\r\n"]
-        
-        if status[:3] == "413" and self.response_protocol == 'HTTP/1.1':
-            # Request Entity Too Large
-            self.close_connection = True
-            buf.append("Connection: close\r\n")
-        
-        buf.append("\r\n")
-        if msg:
-            buf.append(msg)
-        
-        try:
-            self.wfile.sendall("".join(buf))
-        except socket.error, x:
-            if x.args[0] not in socket_errors_to_ignore:
-                raise
-    
-    def start_response(self, status, headers, exc_info = None):
-        """WSGI callable to begin the HTTP response."""
-        # "The application may call start_response more than once,
-        # if and only if the exc_info argument is provided."
-        if self.started_response and not exc_info:
-            raise AssertionError("WSGI start_response called a second "
-                                 "time with no exc_info.")
-        
-        # "if exc_info is provided, and the HTTP headers have already been
-        # sent, start_response must raise an error, and should raise the
-        # exc_info tuple."
-        if self.sent_headers:
-            try:
-                raise exc_info[0], exc_info[1], exc_info[2]
-            finally:
-                exc_info = None
-        
-        self.started_response = True
-        self.status = status
-        self.outheaders.extend(headers)
-        return self.write
-    
-    def write(self, chunk):
-        """WSGI callable to write unbuffered data to the client.
-        
-        This method is also used internally by start_response (to write
-        data from the iterable returned by the WSGI application).
-        """
-        if not self.started_response:
-            raise AssertionError("WSGI write called before start_response.")
-        
-        if not self.sent_headers:
-            self.sent_headers = True
-            self.send_headers()
-        
-        if self.chunked_write and chunk:
-            buf = [hex(len(chunk))[2:], "\r\n", chunk, "\r\n"]
-            self.wfile.sendall("".join(buf))
-        else:
-            self.wfile.sendall(chunk)
-    
-    def send_headers(self):
-        """Assert, process, and send the HTTP response message-headers."""
-        hkeys = [key.lower() for key, value in self.outheaders]
-        status = int(self.status[:3])
-        
-        if status == 413:
-            # Request Entity Too Large. Close conn to avoid garbage.
-            self.close_connection = True
-        elif "content-length" not in hkeys:
-            # "All 1xx (informational), 204 (no content),
-            # and 304 (not modified) responses MUST NOT
-            # include a message-body." So no point chunking.
-            if status < 200 or status in (204, 205, 304):
-                pass
-            else:
-                if (self.response_protocol == 'HTTP/1.1'
-                    and self.environ["REQUEST_METHOD"] != 'HEAD'):
-                    # Use the chunked transfer-coding
-                    self.chunked_write = True
-                    self.outheaders.append(("Transfer-Encoding", "chunked"))
-                else:
-                    # Closing the conn is the only way to determine len.
-                    self.close_connection = True
-        
-        if "connection" not in hkeys:
-            if self.response_protocol == 'HTTP/1.1':
-                # Both server and client are HTTP/1.1 or better
-                if self.close_connection:
-                    self.outheaders.append(("Connection", "close"))
-            else:
-                # Server and/or client are HTTP/1.0
-                if not self.close_connection:
-                    self.outheaders.append(("Connection", "Keep-Alive"))
-        
-        if (not self.close_connection) and (not self.chunked_read):
-            # Read any remaining request body data on the socket.
-            # "If an origin server receives a request that does not include an
-            # Expect request-header field with the "100-continue" expectation,
-            # the request includes a request body, and the server responds
-            # with a final status code before reading the entire request body
-            # from the transport connection, then the server SHOULD NOT close
-            # the transport connection until it has read the entire request,
-            # or until the client closes the connection. Otherwise, the client
-            # might not reliably receive the response message. However, this
-            # requirement is not be construed as preventing a server from
-            # defending itself against denial-of-service attacks, or from
-            # badly broken client implementations."
-            size = self.rfile.maxlen - self.rfile.bytes_read
-            if size > 0:
-                self.rfile.read(size)
-        
-        if "date" not in hkeys:
-            self.outheaders.append(("Date", rfc822.formatdate()))
-        
-        if "server" not in hkeys:
-            self.outheaders.append(("Server", self.environ['SERVER_SOFTWARE']))
-        
-        buf = [self.environ['ACTUAL_SERVER_PROTOCOL'], " ", self.status, "\r\n"]
-        try:
-            buf += [k + ": " + v + "\r\n" for k, v in self.outheaders]
-        except TypeError:
-            if not isinstance(k, str):
-                raise TypeError("WSGI response header key %r is not a string.")
-            if not isinstance(v, str):
-                raise TypeError("WSGI response header value %r is not a string.")
-            else:
-                raise
-        buf.append("\r\n")
-        self.wfile.sendall("".join(buf))
-
-
-class NoSSLError(Exception):
-    """Exception raised when a client speaks HTTP to an HTTPS socket."""
-    pass
-
-
-class FatalSSLAlert(Exception):
-    """Exception raised when the SSL implementation signals a fatal alert."""
-    pass
-
-
-if not _fileobject_uses_str_type:
-    class CP_fileobject(socket._fileobject):
-        """Faux file object attached to a socket object."""
-
-        def sendall(self, data):
-            """Sendall for non-blocking sockets."""
-            while data:
-                try:
-                    bytes_sent = self.send(data)
-                    data = data[bytes_sent:]
-                except socket.error, e:
-                    if e.args[0] not in socket_errors_nonblocking:
-                        raise
-
-        def send(self, data):
-            return self._sock.send(data)
-
-        def flush(self):
-            if self._wbuf:
-                buffer = "".join(self._wbuf)
-                self._wbuf = []
-                self.sendall(buffer)
-
-        def recv(self, size):
-            while True:
-                try:
-                    return self._sock.recv(size)
-                except socket.error, e:
-                    if (e.args[0] not in socket_errors_nonblocking
-                        and e.args[0] not in socket_error_eintr):
-                        raise
-
-        def read(self, size=-1):
-            # Use max, disallow tiny reads in a loop as they are very inefficient.
-            # We never leave read() with any leftover data from a new recv() call
-            # in our internal buffer.
-            rbufsize = max(self._rbufsize, self.default_bufsize)
-            # Our use of StringIO rather than lists of string objects returned by
-            # recv() minimizes memory usage and fragmentation that occurs when
-            # rbufsize is large compared to the typical return value of recv().
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if size < 0:
-                # Read until EOF
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(rbufsize)
-                    if not data:
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    # Already have size bytes in our buffer?  Extract and return.
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    left = size - buf_len
-                    # recv() will malloc the amount of memory given as its
-                    # parameter even though it often returns much less data
-                    # than that.  The returned data string is short lived
-                    # as we copy it into a StringIO and free it.  This avoids
-                    # fragmentation issues on many platforms.
-                    data = self.recv(left)
-                    if not data:
-                        break
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid buffer data copies when:
-                        # - We have no data in our buffer.
-                        # AND
-                        # - Our call to recv returned exactly the
-                        #   number of bytes we were asked to read.
-                        return data
-                    if n == left:
-                        buf.write(data)
-                        del data  # explicit free
-                        break
-                    assert n <= left, "recv(%d) returned %d bytes" % (left, n)
-                    buf.write(data)
-                    buf_len += n
-                    del data  # explicit free
-                    #assert buf_len == buf.tell()
-                return buf.getvalue()
-
-        def readline(self, size=-1):
-            buf = self._rbuf
-            buf.seek(0, 2)  # seek end
-            if buf.tell() > 0:
-                # check if we already have it in our buffer
-                buf.seek(0)
-                bline = buf.readline(size)
-                if bline.endswith('\n') or len(bline) == size:
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return bline
-                del bline
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    buf.seek(0)
-                    buffers = [buf.read()]
-                    self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                    data = None
-                    recv = self.recv
-                    while data != "\n":
-                        data = recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return "".join(buffers)
-
-                buf.seek(0, 2)  # seek end
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        buf.write(data[:nl])
-                        self._rbuf.write(data[nl:])
-                        del data
-                        break
-                    buf.write(data)
-                return buf.getvalue()
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes first
-                buf.seek(0, 2)  # seek end
-                buf_len = buf.tell()
-                if buf_len >= size:
-                    buf.seek(0)
-                    rv = buf.read(size)
-                    self._rbuf = StringIO.StringIO()
-                    self._rbuf.write(buf.read())
-                    return rv
-                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    left = size - buf_len
-                    # did we just receive a newline?
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        # save the excess data to _rbuf
-                        self._rbuf.write(data[nl:])
-                        if buf_len:
-                            buf.write(data[:nl])
-                            break
-                        else:
-                            # Shortcut.  Avoid data copy through buf when returning
-                            # a substring of our first recv().
-                            return data[:nl]
-                    n = len(data)
-                    if n == size and not buf_len:
-                        # Shortcut.  Avoid data copy through buf when
-                        # returning exactly all of our first recv().
-                        return data
-                    if n >= left:
-                        buf.write(data[:left])
-                        self._rbuf.write(data[left:])
-                        break
-                    buf.write(data)
-                    buf_len += n
-                    #assert buf_len == buf.tell()
-                return buf.getvalue()
-
-else:
-    class CP_fileobject(socket._fileobject):
-        """Faux file object attached to a socket object."""
-
-        def sendall(self, data):
-            """Sendall for non-blocking sockets."""
-            while data:
-                try:
-                    bytes_sent = self.send(data)
-                    data = data[bytes_sent:]
-                except socket.error, e:
-                    if e.args[0] not in socket_errors_nonblocking:
-                        raise
-
-        def send(self, data):
-            return self._sock.send(data)
-
-        def flush(self):
-            if self._wbuf:
-                buffer = "".join(self._wbuf)
-                self._wbuf = []
-                self.sendall(buffer)
-
-        def recv(self, size):
-            while True:
-                try:
-                    return self._sock.recv(size)
-                except socket.error, e:
-                    if (e.args[0] not in socket_errors_nonblocking
-                        and e.args[0] not in socket_error_eintr):
-                        raise
-
-        def read(self, size=-1):
-            if size < 0:
-                # Read until EOF
-                buffers = [self._rbuf]
-                self._rbuf = ""
-                if self._rbufsize <= 1:
-                    recv_size = self.default_bufsize
-                else:
-                    recv_size = self._rbufsize
-
-                while True:
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                return "".join(buffers)
-            else:
-                # Read until size bytes or EOF seen, whichever comes first
-                data = self._rbuf
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    left = size - buf_len
-                    recv_size = max(self._rbufsize, left)
-                    data = self.recv(recv_size)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return "".join(buffers)
-
-        def readline(self, size=-1):
-            data = self._rbuf
-            if size < 0:
-                # Read until \n or EOF, whichever comes first
-                if self._rbufsize <= 1:
-                    # Speed up unbuffered case
-                    assert data == ""
-                    buffers = []
-                    while data != "\n":
-                        data = self.recv(1)
-                        if not data:
-                            break
-                        buffers.append(data)
-                    return "".join(buffers)
-                nl = data.find('\n')
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    nl = data.find('\n')
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                return "".join(buffers)
-            else:
-                # Read until size bytes or \n or EOF seen, whichever comes first
-                nl = data.find('\n', 0, size)
-                if nl >= 0:
-                    nl += 1
-                    self._rbuf = data[nl:]
-                    return data[:nl]
-                buf_len = len(data)
-                if buf_len >= size:
-                    self._rbuf = data[size:]
-                    return data[:size]
-                buffers = []
-                if data:
-                    buffers.append(data)
-                self._rbuf = ""
-                while True:
-                    data = self.recv(self._rbufsize)
-                    if not data:
-                        break
-                    buffers.append(data)
-                    left = size - buf_len
-                    nl = data.find('\n', 0, left)
-                    if nl >= 0:
-                        nl += 1
-                        self._rbuf = data[nl:]
-                        buffers[-1] = data[:nl]
-                        break
-                    n = len(data)
-                    if n >= left:
-                        self._rbuf = data[left:]
-                        buffers[-1] = data[:left]
-                        break
-                    buf_len += n
-                return "".join(buffers)
-    
-
-class SSL_fileobject(CP_fileobject):
-    """SSL file object attached to a socket object."""
-    
-    ssl_timeout = 3
-    ssl_retry = .01
-    
-    def _safe_call(self, is_reader, call, *args, **kwargs):
-        """Wrap the given call with SSL error-trapping.
-        
-        is_reader: if False EOF errors will be raised. If True, EOF errors
-            will return "" (to emulate normal sockets).
-        """
-        start = time.time()
-        while True:
-            try:
-                return call(*args, **kwargs)
-            except SSL.WantReadError:
-                # Sleep and try again. This is dangerous, because it means
-                # the rest of the stack has no way of differentiating
-                # between a "new handshake" error and "client dropped".
-                # Note this isn't an endless loop: there's a timeout below.
-                time.sleep(self.ssl_retry)
-            except SSL.WantWriteError:
-                time.sleep(self.ssl_retry)
-            except SSL.SysCallError, e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ""
-                
-                errnum = e.args[0]
-                if is_reader and errnum in socket_errors_to_ignore:
-                    return ""
-                raise socket.error(errnum)
-            except SSL.Error, e:
-                if is_reader and e.args == (-1, 'Unexpected EOF'):
-                    return ""
-                
-                thirdarg = None
-                try:
-                    thirdarg = e.args[0][0][2]
-                except IndexError:
-                    pass
-                
-                if thirdarg == 'http request':
-                    # The client is talking HTTP to an HTTPS server.
-                    raise NoSSLError()
-                raise FatalSSLAlert(*e.args)
-            except:
-                raise
-            
-            if time.time() - start > self.ssl_timeout:
-                raise socket.timeout("timed out")
-
-    def recv(self, *args, **kwargs):
-        buf = []
-        r = super(SSL_fileobject, self).recv
-        while True:
-            data = self._safe_call(True, r, *args, **kwargs)
-            buf.append(data)
-            p = self._sock.pending()
-            if not p:
-                return "".join(buf)
-    
-    def sendall(self, *args, **kwargs):
-        return self._safe_call(False, super(SSL_fileobject, self).sendall, *args, **kwargs)
-
-    def send(self, *args, **kwargs):
-        return self._safe_call(False, super(SSL_fileobject, self).send, *args, **kwargs)
-
-
-class HTTPConnection(object):
-    """An HTTP connection (active socket).
-    
-    socket: the raw socket object (usually TCP) for this connection.
-    wsgi_app: the WSGI application for this server/connection.
-    environ: a WSGI environ template. This will be copied for each request.
-    
-    rfile: a fileobject for reading from the socket.
-    send: a function for writing (+ flush) to the socket.
-    """
-    
-    rbufsize = -1
-    RequestHandlerClass = HTTPRequest
-    environ = {"wsgi.version": (1, 0),
-               "wsgi.url_scheme": "http",
-               "wsgi.multithread": True,
-               "wsgi.multiprocess": False,
-               "wsgi.run_once": False,
-               "wsgi.errors": sys.stderr,
-               }
-    
-    def __init__(self, sock, wsgi_app, environ):
-        self.socket = sock
-        self.wsgi_app = wsgi_app
-        
-        # Copy the class environ into self.
-        self.environ = self.environ.copy()
-        self.environ.update(environ)
-        
-        if SSL and isinstance(sock, SSL.ConnectionType):
-            timeout = sock.gettimeout()
-            self.rfile = SSL_fileobject(sock, "rb", self.rbufsize)
-            self.rfile.ssl_timeout = timeout
-            self.wfile = SSL_fileobject(sock, "wb", -1)
-            self.wfile.ssl_timeout = timeout
-        else:
-            self.rfile = CP_fileobject(sock, "rb", self.rbufsize)
-            self.wfile = CP_fileobject(sock, "wb", -1)
-        
-        # Wrap wsgi.input but not HTTPConnection.rfile itself.
-        # We're also not setting maxlen yet; we'll do that separately
-        # for headers and body for each iteration of self.communicate
-        # (if maxlen is 0 the wrapper doesn't check length).
-        self.environ["wsgi.input"] = SizeCheckWrapper(self.rfile, 0)
-    
-    def communicate(self):
-        """Read each request and respond appropriately."""
-        try:
-            while True:
-                # (re)set req to None so that if something goes wrong in
-                # the RequestHandlerClass constructor, the error doesn't
-                # get written to the previous request.
-                req = None
-                req = self.RequestHandlerClass(self.wfile, self.environ,
-                                               self.wsgi_app)
-                
-                # This order of operations should guarantee correct pipelining.
-                req.parse_request()
-                if not req.ready:
-                    return
-                
-                req.respond()
-                if req.close_connection:
-                    return
-        
-        except socket.error, e:
-            errnum = e.args[0]
-            if errnum == 'timed out':
-                if req and not req.sent_headers:
-                    req.simple_response("408 Request Timeout")
-            elif errnum not in socket_errors_to_ignore:
-                if req and not req.sent_headers:
-                    req.simple_response("500 Internal Server Error",
-                                        format_exc())
-            return
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except FatalSSLAlert, e:
-            # Close the connection.
-            return
-        except NoSSLError:
-            if req and not req.sent_headers:
-                # Unwrap our wfile
-                req.wfile = CP_fileobject(self.socket._sock, "wb", -1)
-                req.simple_response("400 Bad Request",
-                    "The client sent a plain HTTP request, but "
-                    "this server only speaks HTTPS on this port.")
-                self.linger = True
-        except Exception, e:
-            if req and not req.sent_headers:
-                req.simple_response("500 Internal Server Error", format_exc())
-    
-    linger = False
-    
-    def close(self):
-        """Close the socket underlying this connection."""
-        self.rfile.close()
-        
-        if not self.linger:
-            # Python's socket module does NOT call close on the kernel socket
-            # when you call socket.close(). We do so manually here because we
-            # want this server to send a FIN TCP segment immediately. Note this
-            # must be called *before* calling socket.close(), because the latter
-            # drops its reference to the kernel socket.
-            self.socket._sock.close()
-            self.socket.close()
-        else:
-            # On the other hand, sometimes we want to hang around for a bit
-            # to make sure the client has a chance to read our entire
-            # response. Skipping the close() calls here delays the FIN
-            # packet until the socket object is garbage-collected later.
-            # Someday, perhaps, we'll do the full lingering_close that
-            # Apache does, but not today.
-            pass
-
-
-def format_exc(limit=None):
-    """Like print_exc() but return a string. Backport for Python 2.3."""
-    try:
-        etype, value, tb = sys.exc_info()
-        return ''.join(traceback.format_exception(etype, value, tb, limit))
-    finally:
-        etype = value = tb = None
-
-
-_SHUTDOWNREQUEST = None
-
-class WorkerThread(threading.Thread):
-    """Thread which continuously polls a Queue for Connection objects.
-    
-    server: the HTTP Server which spawned this thread, and which owns the
-        Queue and is placing active connections into it.
-    ready: a simple flag for the calling server to know when this thread
-        has begun polling the Queue.
-    
-    Due to the timing issues of polling a Queue, a WorkerThread does not
-    check its own 'ready' flag after it has started. To stop the thread,
-    it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
-    (one for each running WorkerThread).
-    """
-    
-    conn = None
-    
-    def __init__(self, server):
-        self.ready = False
-        self.server = server
-        threading.Thread.__init__(self)
-    
-    def run(self):
-        try:
-            self.ready = True
-            while True:
-                conn = self.server.requests.get()
-                if conn is _SHUTDOWNREQUEST:
-                    return
-                
-                self.conn = conn
-                try:
-                    conn.communicate()
-                finally:
-                    conn.close()
-                    self.conn = None
-        except (KeyboardInterrupt, SystemExit), exc:
-            self.server.interrupt = exc
-
-
-class ThreadPool(object):
-    """A Request Queue for the CherryPyWSGIServer which pools threads.
-    
-    ThreadPool objects must provide min, get(), put(obj), start()
-    and stop(timeout) attributes.
-    """
-    
-    def __init__(self, server, min=10, max=-1):
-        self.server = server
-        self.min = min
-        self.max = max
-        self._threads = []
-        self._queue = Queue.Queue()
-        self.get = self._queue.get
-    
-    def start(self):
-        """Start the pool of threads."""
-        for i in xrange(self.min):
-            self._threads.append(WorkerThread(self.server))
-        for worker in self._threads:
-            worker.setName("CP WSGIServer " + worker.getName())
-            worker.start()
-        for worker in self._threads:
-            while not worker.ready:
-                time.sleep(.1)
-    
-    def _get_idle(self):
-        """Number of worker threads which are idle. Read-only."""
-        return len([t for t in self._threads if t.conn is None])
-    idle = property(_get_idle, doc=_get_idle.__doc__)
-    
-    def put(self, obj):
-        self._queue.put(obj)
-        if obj is _SHUTDOWNREQUEST:
-            return
-    
-    def grow(self, amount):
-        """Spawn new worker threads (not above self.max)."""
-        for i in xrange(amount):
-            if self.max > 0 and len(self._threads) >= self.max:
-                break
-            worker = WorkerThread(self.server)
-            worker.setName("CP WSGIServer " + worker.getName())
-            self._threads.append(worker)
-            worker.start()
-    
-    def shrink(self, amount):
-        """Kill off worker threads (not below self.min)."""
-        # Grow/shrink the pool if necessary.
-        # Remove any dead threads from our list
-        for t in self._threads:
-            if not t.isAlive():
-                self._threads.remove(t)
-                amount -= 1
-        
-        if amount > 0:
-            for i in xrange(min(amount, len(self._threads) - self.min)):
-                # Put a number of shutdown requests on the queue equal
-                # to 'amount'. Once each of those is processed by a worker,
-                # that worker will terminate and be culled from our list
-                # in self.put.
-                self._queue.put(_SHUTDOWNREQUEST)
-    
-    def stop(self, timeout=5):
-        # Must shut down threads here so the code that calls
-        # this method can know when all threads are stopped.
-        for worker in self._threads:
-            self._queue.put(_SHUTDOWNREQUEST)
-        
-        # Don't join currentThread (when stop is called inside a request).
-        current = threading.currentThread()
-        while self._threads:
-            worker = self._threads.pop()
-            if worker is not current and worker.isAlive():
-                try:
-                    if timeout is None or timeout < 0:
-                        worker.join()
-                    else:
-                        worker.join(timeout)
-                        if worker.isAlive():
-                            # We exhausted the timeout.
-                            # Forcibly shut down the socket.
-                            c = worker.conn
-                            if c and not c.rfile.closed:
-                                if SSL and isinstance(c.socket, SSL.ConnectionType):
-                                    # pyOpenSSL.socket.shutdown takes no args
-                                    c.socket.shutdown()
-                                else:
-                                    c.socket.shutdown(socket.SHUT_RD)
-                            worker.join()
-                except (AssertionError,
-                        # Ignore repeated Ctrl-C.
-                        # See http://www.cherrypy.org/ticket/691.
-                        KeyboardInterrupt), exc1:
-                    pass
-
-
-
-class SSLConnection:
-    """A thread-safe wrapper for an SSL.Connection.
-    
-    *args: the arguments to create the wrapped SSL.Connection(*args).
-    """
-    
-    def __init__(self, *args):
-        self._ssl_conn = SSL.Connection(*args)
-        self._lock = threading.RLock()
-    
-    for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
-              'renegotiate', 'bind', 'listen', 'connect', 'accept',
-              'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list',
-              'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
-              'makefile', 'get_app_data', 'set_app_data', 'state_string',
-              'sock_shutdown', 'get_peer_certificate', 'want_read',
-              'want_write', 'set_connect_state', 'set_accept_state',
-              'connect_ex', 'sendall', 'settimeout'):
-        exec """def %s(self, *args):
-        self._lock.acquire()
-        try:
-            return self._ssl_conn.%s(*args)
-        finally:
-            self._lock.release()
-""" % (f, f)
-
-
-try:
-    import fcntl
-except ImportError:
-    try:
-        from ctypes import windll, WinError
-    except ImportError:
-        def prevent_socket_inheritance(sock):
-            """Dummy function, since neither fcntl nor ctypes are available."""
-            pass
-    else:
-        def prevent_socket_inheritance(sock):
-            """Mark the given socket fd as non-inheritable (Windows)."""
-            if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
-                raise WinError()
-else:
-    def prevent_socket_inheritance(sock):
-        """Mark the given socket fd as non-inheritable (POSIX)."""
-        fd = sock.fileno()
-        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
-        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
-
-
-class CherryPyWSGIServer(object):
-    """An HTTP server for WSGI.
-    
-    bind_addr: The interface on which to listen for connections.
-        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
-        or IPv6 address, or any valid hostname. The string 'localhost' is a
-        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
-        The string '0.0.0.0' is a special IPv4 entry meaning "any active
-        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
-        IPv6. The empty string or None are not allowed.
-        
-        For UNIX sockets, supply the filename as a string.
-    wsgi_app: the WSGI 'application callable'; multiple WSGI applications
-        may be passed as (path_prefix, app) pairs.
-    numthreads: the number of worker threads to create (default 10).
-    server_name: the string to set for WSGI's SERVER_NAME environ entry.
-        Defaults to socket.gethostname().
-    max: the maximum number of queued requests (defaults to -1 = no limit).
-    request_queue_size: the 'backlog' argument to socket.listen();
-        specifies the maximum number of queued connections (default 5).
-    timeout: the timeout in seconds for accepted connections (default 10).
-    
-    nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket
-        option.
-    
-    protocol: the version string to write in the Status-Line of all
-        HTTP responses. For example, "HTTP/1.1" (the default). This
-        also limits the supported features used in the response.
-    
-    
-    SSL/HTTPS
-    ---------
-    The OpenSSL module must be importable for SSL functionality.
-    You can obtain it from http://pyopenssl.sourceforge.net/
-    
-    ssl_certificate: the filename of the server SSL certificate.
-    ssl_privatekey: the filename of the server's private key file.
-    
-    If either of these is None (both are None by default), this server
-    will not use SSL. If both are given and are valid, they will be read
-    on server start and used in the SSL context for the listening socket.
-    """
-    
-    protocol = "HTTP/1.1"
-    _bind_addr = "127.0.0.1"
-    version = "CherryPy/3.1.2"
-    ready = False
-    _interrupt = None
-    
-    nodelay = True
-    
-    ConnectionClass = HTTPConnection
-    environ = {}
-    
-    # Paths to certificate and private key files
-    ssl_certificate = None
-    ssl_private_key = None
-    
-    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
-                 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
-        self.requests = ThreadPool(self, min=numthreads or 1, max=max)
-        
-        if callable(wsgi_app):
-            # We've been handed a single wsgi_app, in CP-2.1 style.
-            # Assume it's mounted at "".
-            self.wsgi_app = wsgi_app
-        else:
-            # We've been handed a list of (path_prefix, wsgi_app) tuples,
-            # so that the server can call different wsgi_apps, and also
-            # correctly set SCRIPT_NAME.
-            warnings.warn("The ability to pass multiple apps is deprecated "
-                          "and will be removed in 3.2. You should explicitly "
-                          "include a WSGIPathInfoDispatcher instead.",
-                          DeprecationWarning)
-            self.wsgi_app = WSGIPathInfoDispatcher(wsgi_app)
-        
-        self.bind_addr = bind_addr
-        if not server_name:
-            server_name = socket.gethostname()
-        self.server_name = server_name
-        self.request_queue_size = request_queue_size
-        
-        self.timeout = timeout
-        self.shutdown_timeout = shutdown_timeout
-    
-    def _get_numthreads(self):
-        return self.requests.min
-    def _set_numthreads(self, value):
-        self.requests.min = value
-    numthreads = property(_get_numthreads, _set_numthreads)
-    
-    def __str__(self):
-        return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
-                              self.bind_addr)
-    
-    def _get_bind_addr(self):
-        return self._bind_addr
-    def _set_bind_addr(self, value):
-        if isinstance(value, tuple) and value[0] in ('', None):
-            # Despite the socket module docs, using '' does not
-            # allow AI_PASSIVE to work. Passing None instead
-            # returns '0.0.0.0' like we want. In other words:
-            #     host    AI_PASSIVE     result
-            #      ''         Y         192.168.x.y
-            #      ''         N         192.168.x.y
-            #     None        Y         0.0.0.0
-            #     None        N         127.0.0.1
-            # But since you can get the same effect with an explicit
-            # '0.0.0.0', we deny both the empty string and None as values.
-            raise ValueError("Host values of '' or None are not allowed. "
-                             "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
-                             "to listen on all active interfaces.")
-        self._bind_addr = value
-    bind_addr = property(_get_bind_addr, _set_bind_addr,
-        doc="""The interface on which to listen for connections.
-        
-        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
-        or IPv6 address, or any valid hostname. The string 'localhost' is a
-        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
-        The string '0.0.0.0' is a special IPv4 entry meaning "any active
-        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
-        IPv6. The empty string or None are not allowed.
-        
-        For UNIX sockets, supply the filename as a string.""")
-    
-    def start(self):
-        """Run the server forever."""
-        # We don't have to trap KeyboardInterrupt or SystemExit here,
-        # because cherrpy.server already does so, calling self.stop() for us.
-        # If you're using this server with another framework, you should
-        # trap those exceptions in whatever code block calls start().
-        self._interrupt = None
-        
-        # Select the appropriate socket
-        if isinstance(self.bind_addr, basestring):
-            # AF_UNIX socket
-            
-            # So we can reuse the socket...
-            try: os.unlink(self.bind_addr)
-            except: pass
-            
-            # So everyone can access the socket...
-            try: os.chmod(self.bind_addr, 0777)
-            except: pass
-            
-            info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
-        else:
-            # AF_INET or AF_INET6 socket
-            # Get the correct address family for our host (allows IPv6 addresses)
-            host, port = self.bind_addr
-            try:
-                info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                          socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
-            except socket.gaierror:
-                # Probably a DNS issue. Assume IPv4.
-                info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", self.bind_addr)]
-        
-        self.socket = None
-        msg = "No socket could be created"
-        for res in info:
-            af, socktype, proto, canonname, sa = res
-            try:
-                self.bind(af, socktype, proto)
-            except socket.error, msg:
-                if self.socket:
-                    self.socket.close()
-                self.socket = None
-                continue
-            break
-        if not self.socket:
-            raise socket.error, msg
-        
-        # Timeout so KeyboardInterrupt can be caught on Win32
-        self.socket.settimeout(1)
-        self.socket.listen(self.request_queue_size)
-        
-        # Create worker threads
-        self.requests.start()
-        
-        self.ready = True
-        while self.ready:
-            self.tick()
-            if self.interrupt:
-                while self.interrupt is True:
-                    # Wait for self.stop() to complete. See _set_interrupt.
-                    time.sleep(0.1)
-                if self.interrupt:
-                    raise self.interrupt
-    
-    def bind(self, family, type, proto=0):
-        """Create (or recreate) the actual socket object."""
-        self.socket = socket.socket(family, type, proto)
-        prevent_socket_inheritance(self.socket)
-        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        if self.nodelay:
-            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-        if self.ssl_certificate and self.ssl_private_key:
-            if SSL is None:
-                raise ImportError("You must install pyOpenSSL to use HTTPS.")
-            
-            # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
-            ctx = SSL.Context(SSL.SSLv23_METHOD)
-            ctx.use_privatekey_file(self.ssl_private_key)
-            ctx.use_certificate_file(self.ssl_certificate)
-            self.socket = SSLConnection(ctx, self.socket)
-            self.populate_ssl_environ()
-            
-            # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
-            # activate dual-stack. See http://www.cherrypy.org/ticket/871.
-            if (not isinstance(self.bind_addr, basestring)
-                and self.bind_addr[0] == '::' and family == socket.AF_INET6):
-                try:
-                    self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
-                except (AttributeError, socket.error):
-                    # Apparently, the socket option is not available in
-                    # this machine's TCP stack
-                    pass
-        
-        self.socket.bind(self.bind_addr)
-    
-    def tick(self):
-        """Accept a new connection and put it on the Queue."""
-        try:
-            s, addr = self.socket.accept()
-            prevent_socket_inheritance(s)
-            if not self.ready:
-                return
-            if hasattr(s, 'settimeout'):
-                s.settimeout(self.timeout)
-            
-            environ = self.environ.copy()
-            # SERVER_SOFTWARE is common for IIS. It's also helpful for
-            # us to pass a default value for the "Server" response header.
-            if environ.get("SERVER_SOFTWARE") is None:
-                environ["SERVER_SOFTWARE"] = "%s WSGI Server" % self.version
-            # set a non-standard environ entry so the WSGI app can know what
-            # the *real* server protocol is (and what features to support).
-            # See http://www.faqs.org/rfcs/rfc2145.html.
-            environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol
-            environ["SERVER_NAME"] = self.server_name
-            
-            if isinstance(self.bind_addr, basestring):
-                # AF_UNIX. This isn't really allowed by WSGI, which doesn't
-                # address unix domain sockets. But it's better than nothing.
-                environ["SERVER_PORT"] = ""
-            else:
-                environ["SERVER_PORT"] = str(self.bind_addr[1])
-                # optional values
-                # Until we do DNS lookups, omit REMOTE_HOST
-                environ["REMOTE_ADDR"] = addr[0]
-                environ["REMOTE_PORT"] = str(addr[1])
-            
-            conn = self.ConnectionClass(s, self.wsgi_app, environ)
-            self.requests.put(conn)
-        except socket.timeout:
-            # The only reason for the timeout in start() is so we can
-            # notice keyboard interrupts on Win32, which don't interrupt
-            # accept() by default
-            return
-        except socket.error, x:
-            if x.args[0] in socket_error_eintr:
-                # I *think* this is right. EINTR should occur when a signal
-                # is received during the accept() call; all docs say retry
-                # the call, and I *think* I'm reading it right that Python
-                # will then go ahead and poll for and handle the signal
-                # elsewhere. See http://www.cherrypy.org/ticket/707.
-                return
-            if x.args[0] in socket_errors_nonblocking:
-                # Just try again. See http://www.cherrypy.org/ticket/479.
-                return
-            if x.args[0] in socket_errors_to_ignore:
-                # Our socket was closed.
-                # See http://www.cherrypy.org/ticket/686.
-                return
-            raise
-    
-    def _get_interrupt(self):
-        return self._interrupt
-    def _set_interrupt(self, interrupt):
-        self._interrupt = True
-        self.stop()
-        self._interrupt = interrupt
-    interrupt = property(_get_interrupt, _set_interrupt,
-                         doc="Set this to an Exception instance to "
-                             "interrupt the server.")
-    
-    def stop(self):
-        """Gracefully shutdown a server that is serving forever."""
-        self.ready = False
-        
-        sock = getattr(self, "socket", None)
-        if sock:
-            if not isinstance(self.bind_addr, basestring):
-                # Touch our own socket to make accept() return immediately.
-                try:
-                    host, port = sock.getsockname()[:2]
-                except socket.error, x:
-                    if x.args[0] not in socket_errors_to_ignore:
-                        raise
-                else:
-                    # Note that we're explicitly NOT using AI_PASSIVE,
-                    # here, because we want an actual IP to touch.
-                    # localhost won't work if we've bound to a public IP,
-                    # but it will if we bound to '0.0.0.0' (INADDR_ANY).
-                    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
-                                                  socket.SOCK_STREAM):
-                        af, socktype, proto, canonname, sa = res
-                        s = None
-                        try:
-                            s = socket.socket(af, socktype, proto)
-                            # See http://groups.google.com/group/cherrypy-users/
-                            #        browse_frm/thread/bbfe5eb39c904fe0
-                            s.settimeout(1.0)
-                            s.connect((host, port))
-                            s.close()
-                        except socket.error:
-                            if s:
-                                s.close()
-            if hasattr(sock, "close"):
-                sock.close()
-            self.socket = None
-        
-        self.requests.stop(self.shutdown_timeout)
-    
-    def populate_ssl_environ(self):
-        """Create WSGI environ entries to be merged into each request."""
-        cert = open(self.ssl_certificate, 'rb').read()
-        cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
-        ssl_environ = {
-            "wsgi.url_scheme": "https",
-            "HTTPS": "on",
-            # pyOpenSSL doesn't provide access to any of these AFAICT
-##            'SSL_PROTOCOL': 'SSLv2',
-##            SSL_CIPHER 	string 	The cipher specification name
-##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
-##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
-            }
-        
-        # Server certificate attributes
-        ssl_environ.update({
-            'SSL_SERVER_M_VERSION': cert.get_version(),
-            'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
-##            'SSL_SERVER_V_START': Validity of server's certificate (start time),
-##            'SSL_SERVER_V_END': Validity of server's certificate (end time),
-            })
-        
-        for prefix, dn in [("I", cert.get_issuer()),
-                           ("S", cert.get_subject())]:
-            # X509Name objects don't seem to have a way to get the
-            # complete DN string. Use str() and slice it instead,
-            # because str(dn) == "<X509Name object '/C=US/ST=...'>"
-            dnstr = str(dn)[18:-2]
-            
-            wsgikey = 'SSL_SERVER_%s_DN' % prefix
-            ssl_environ[wsgikey] = dnstr
-            
-            # The DN should be of the form: /k1=v1/k2=v2, but we must allow
-            # for any value to contain slashes itself (in a URL).
-            while dnstr:
-                pos = dnstr.rfind("=")
-                dnstr, value = dnstr[:pos], dnstr[pos + 1:]
-                pos = dnstr.rfind("/")
-                dnstr, key = dnstr[:pos], dnstr[pos + 1:]
-                if key and value:
-                    wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
-                    ssl_environ[wsgikey] = value
-        
-        self.environ.update(ssl_environ)
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/.hgignore	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,10 @@
+^MANIFEST$
+^(build|dist|Werkzeug\.egg-info)/
+\.py[co]$
+\.DS_Store$
+^env$
+^docs/_build
+[^/]+-stats\.txt$
+^bench/[ab]/
+\.coverage
+coverage_out/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/.hgtags	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,11 @@
+b4548e55e0257c6c777a0484fbc86a8b82090cd0 0.1
+f93fb9db6fce63c32849e7369ae0b16e137a5b2b 0.2
+40e894d190577011b989051e7104fc4444a72ef4 0.3
+7bcec7da720c22ba07153b832fb6657d97d4b2d6 0.3.1
+0c47d98e422773bebce294d9f5ce74dc189f1952 0.4
+a93240c851ab4ac3c9426f61b69391586d3d7fca 0.4.1
+426d64c7ef3e605eb7551980b3b9de3bb0001dab 0.5
+f972dac514934c6b993c5d5813a03da15bda6a46 0.5.1
+f0716ad49661076e0f129b9ee4e5f7118a25e730 0.6
+a32bf2b4175616347b2493ff58aebbc5e4392901 0.6.1
+3aa163672076c95e0e36e65cfbb28d79bf8368ef 0.6.2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/AUTHORS	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,42 @@
+Werkzeug is written and maintained by the Werkzeug Team and various
+contributors:
+
+Project Leader / Developer:
+
+- Armin Ronacher <armin.ronacher@active-4.com>
+
+- Georg Brandl
+- Leif K-Brooks <eurleif@gmail.com>
+- Thomas Johansson
+- Marian Sigler
+- Ronny Pfannschmidt
+- Noah Slater <nslater@tumbolia.org>
+- Alec Thomas
+- Shannon Behrens
+- Christoph Rauch
+- Clemens Hermann
+- Jason Kirtland
+- Ali Afshar
+- Christopher Grebs <cg@webshox.org>
+- Sean Cazzell <seancazzell@gmail.com>
+- Florent Xicluna
+
+Contributors of code for werkzeug/examples are:
+
+- Itay Neeman <itay@neeman.net>
+
+The SSL related parts of the Werkzeug development server are partially
+taken from Paste.  The original code is MIT licensed which is largely
+compatible with the modfied BSD license.  The following copyrights apply:
+
+- (c) 2005 Ian Bicking and contributors
+- (c) 2005 Clark C. Evans
+
+The rename() function from the posixemulation was taken almost unmodified
+from the Trac project's utility module.  The original code is BSD licensed
+with the following copyrights from that module:
+
+- (c) 2003-2009 Edgewall Software
+- (c) 2003-2006 Jonas Borgström <jonas@edgewall.com>
+- (c) 2006 Matthew Good <trac@matt-good.net>
+- (c) 2005-2006 Christian Boos <cboos@neuf.fr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/CHANGES	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,371 @@
+Werkzeug Changelog
+==================
+
+Version 1.0
+-----------
+(first 1.0 release, release date to be announced)
+
+- improved func:`url_decode` and :func:`url_encode` performance.
+- fixed an issue where the SharedDataMiddleware could cause an
+  internal server error on weird paths when loading via pkg_resources.
+- fixed an URL generation bug that caused URLs to be invalid if a
+  generated component contains a colon.
+- :func:`werkzeug.import_string` now works with partially set up
+  packages properly.
+
+Version 0.6.2
+-------------
+(bugfix release, released on April 23th 2010)
+
+- renamed the attribute `implicit_seqence_conversion` attribute of the
+  request object to `implicit_sequence_conversion`.
+
+Version 0.6.1
+-------------
+(bugfix release, released on April 13th 2010)
+
+- heavily improved local objects.  Should pick up standalone greenlet
+  builds now and support proxies to free callables as well.  There is
+  also a stacked local now that makes it possible to invoke the same
+  application from within itself by pushing current request/response
+  on top of the stack.
+- routing build method will also build non-default method rules properly
+  if no method is provided.
+- added proper IPv6 support for the builtin server.
+- windows specific filesystem session store fixes.
+  (should no be more stable under high concurrency)
+- fixed a `NameError` in the session system.
+- fixed a bug with empty arguments in the werkzeug.script system.
+- fixed a bug where log lines will be duplicated if an application uses
+  :meth:`logging.basicConfig` (#499)
+- added secure password hashing and checking functions.
+- `HEAD` is now implicitly added as method in the routing system if
+  `GET` is present.  Not doing that was considered a bug because often
+  code assumed that this is the case and in web servers that do not
+  normalize `HEAD` to `GET` this could break `HEAD` requests.
+- the script support can start SSL servers now.
+
+Version 0.6
+-----------
+(codename Hammer, released on Feb 19th 2010)
+
+- removed pending deprecations
+- sys.path is now printed from the testapp.
+- fixed an RFC 2068 incompatibility with cookie value quoting.
+- the :class:`FileStorage` now gives access to the multipart headers.
+- `cached_property.writeable` has been deprecated.
+- :meth:`MapAdapter.match` now accepts a `return_rule` keyword argument
+  that returns the matched `Rule` instead of just the `endpoint`
+- :meth:`routing.Map.bind_to_environ` raises a more correct error message
+  now if the map was bound to an invalid WSGI environment.
+- added support for SSL to the builtin development server.
+- Response objects are no longer modified in place when they are evaluated
+  as WSGI applications.  For backwards compatibility the `fix_headers`
+  function is still called in case it was overridden.
+  You should however change your application to use `get_wsgi_headers` if
+  you need header modifications before responses are sent as the backwards
+  compatibility support will go away in future versions.
+- :func:`append_slash_redirect` no longer requires the QUERY_STRING to be
+  in the WSGI environment.
+- added :class:`~werkzeug.contrib.wrappers.DynamicCharsetResponseMixin`
+- added :class:`~werkzeug.contrib.wrappers.DynamicCharsetRequestMixin`
+- added :attr:`BaseRequest.url_charset`
+- request and response objects have a default `__repr__` now.
+- builtin data structures can be pickled now.
+- the form data parser will now look at the filename instead the
+  content type to figure out if it should treat the upload as regular
+  form data or file upload.  This fixes a bug with Google Chrome.
+- improved performance of `make_line_iter` and the multipart parser
+  for binary uploads.
+- fixed :attr:`~werkzeug.BaseResponse.is_streamed`
+- fixed a path quoting bug in `EnvironBuilder` that caused PATH_INFO and
+  SCRIPT_NAME to end up in the environ unquoted.
+- :meth:`werkzeug.BaseResponse.freeze` now sets the content length.
+- for unknown HTTP methods the request stream is now always limited
+  instead of being empty.  This makes it easier to implement DAV
+  and other protocols on top of Werkzeug.
+- added :meth:`werkzeug.MIMEAccept.best_match`
+- multi-value test-client posts from a standard dictionary are now
+  supported.  Previously you had to use a multi dict.
+- rule templates properly work with submounts, subdomains and
+  other rule factories now.
+- deprecated non-silent usage of the :class:`werkzeug.LimitedStream`.
+- added support for IRI handling to many parts of Werkzeug.
+- development server properly logs to the werkzeug logger now.
+- added :func:`werkzeug.extract_path_info`
+- fixed a querystring quoting bug in :func:`url_fix`
+- added `fallback_mimetype` to :class:`werkzeug.SharedDataMiddleware`.
+- deprecated :meth:`BaseResponse.iter_encoded`'s charset parameter.
+- added :meth:`BaseResponse.make_sequence`,
+  :attr:`BaseResponse.is_sequence` and
+  :meth:`BaseResponse._ensure_sequence`.
+- added better __repr__ of :class:`werkzeug.Map`
+- `import_string` accepts unicode strings as well now.
+- development server doesn't break on double slashes after the host name.
+- better `__repr__` and `__str__` of
+  :exc:`werkzeug.exceptions.HTTPException`
+- test client works correctly with multiple cookies now.
+- the :class:`werkzeug.routing.Map` now has a class attribute with
+  the default converter mapping.  This helps subclasses to override
+  the converters without passing them to the constructor.
+- implemented :class:`OrderedMultiDict`
+- improved the session support for more efficient session storing
+  on the filesystem.  Also added support for listing of sessions
+  currently stored in the filesystem session store.
+- werkzeug no longer utilizes the Python time module for parsing
+  which means that dates in a broader range can be parsed.
+- the wrappers have no class attributes that make it possible to
+  swap out the dict and list types it uses.
+- werkzeug debugger should work on the appengine dev server now.
+- the URL builder supports dropping of unexpected arguments now.
+  Previously they were always appended to the URL as query string.
+- profiler now writes to the correct stream.
+
+Version 0.5.1
+-------------
+(bugfix release for 0.5, released on July 9th 2009)
+
+- fixed boolean check of :class:`FileStorage`
+- url routing system properly supports unicode URL rules now.
+- file upload streams no longer have to provide a truncate()
+  method.
+- implemented :meth:`BaseRequest._form_parsing_failed`.
+- fixed #394 
+- :meth:`ImmutableDict.copy`, :meth:`ImmutableMultiDict.copy` and
+  :meth:`ImmutableTypeConversionDict.copy` return mutable shallow
+  copies.
+- fixed a bug with the `make_runserver` script action.
+- :meth:`MultiDict.items` and :meth:`MutiDict.iteritems` now accept an
+  argument to return a pair for each value of each key.
+- the multipart parser works better with hand-crafted multipart
+  requests now that have extra newlines added.  This fixes a bug
+  with setuptools uploades not handled properly (#390)
+- fixed some minor bugs in the atom feed generator.
+- fixed a bug with client cookie header parsing being case sensitive.
+- fixed a not-working deprecation warning.
+- fixed package loading for :class:`SharedDataMiddleware`.
+- fixed a bug in the secure cookie that made server-side expiration
+  on servers with a local time that was not set to UTC impossible.
+- fixed console of the interactive debugger.
+
+
+Version 0.5
+-----------
+(codename Schlagbohrer, released on April 24th 2009)
+
+- requires Python 2.4 now
+- fixed a bug in :class:`~contrib.IterIO`
+- added :class:`MIMEAccept` and :class:`CharsetAccept` that work like the
+  regular :class:`Accept` but have extra special normalization for mimetypes
+  and charsets and extra convenience methods.
+- switched the serving system from wsgiref to something homebrew.
+- the :class:`Client` now supports cookies.
+- added the :mod:`~werkzeug.contrib.fixers` module with various
+  fixes for webserver bugs and hosting setup side-effects.
+- added :mod:`werkzeug.contrib.wrappers`
+- added :func:`is_hop_by_hop_header`
+- added :func:`is_entity_header`
+- added :func:`remove_hop_by_hop_headers`
+- added :func:`pop_path_info`
+- added :func:`peek_path_info`
+- added :func:`wrap_file` and :class:`FileWrapper`
+- moved `LimitedStream` from the contrib package into the regular
+  werkzeug one and changed the default behavior to raise exceptions
+  rather than stopping without warning.  The old class will stick in
+  the module until 0.6.
+- implemented experimental multipart parser that replaces the old CGI hack.
+- added :func:`dump_options_header` and :func:`parse_options_header`
+- added :func:`quote_header_value` and :func:`unquote_header_value`
+- :func:`url_encode` and :func:`url_decode` now accept a separator
+  argument to switch between `&` and `;` as pair separator.  The magic
+  switch is no longer in place.
+- all form data parsing functions as well as the :class:`BaseRequest`
+  object have parameters (or attributes) to limit the number of
+  incoming bytes (either totally or per field).
+- added :class:`LanguageAccept`
+- request objects are now enforced to be read only for all collections.
+- added many new collection classes, refactored collections in general.
+- test support was refactored, semi-undocumented `werkzeug.test.File`
+  was replaced by :class:`werkzeug.FileStorage`.
+- :class:`EnvironBuilder` was added and unifies the previous distinct
+  :func:`create_environ`, :class:`Client` and
+  :meth:`BaseRequest.from_values`.  They all work the same now which
+  is less confusing.
+- officially documented imports from the internal modules as undefined
+  behavior.  These modules were never exposed as public interfaces.
+- removed `FileStorage.__len__` which previously made the object
+  falsy for browsers not sending the content length which all browsers
+  do.
+- :class:`SharedDataMiddleware` uses `wrap_file` now and has a
+  configurable cache timeout.
+- added :class:`CommonRequestDescriptorsMixin`
+- added :attr:`CommonResponseDescriptorsMixin.mimetype_params`
+- added :mod:`werkzeug.contrib.lint`
+- added `passthrough_errors` to `run_simple`.
+- added `secure_filename`
+- added :func:`make_line_iter` 
+- :class:`MultiDict` copies now instead of revealing internal
+  lists to the caller for `getlist` and iteration functions that
+  return lists.
+- added :attr:`follow_redirect` to the :func:`open` of :class:`Client`.
+- added support for `extra_files` in
+  :func:`~werkzeug.script.make_runserver`
+
+Version 0.4.1
+-------------
+(Bugfix release, released on January 11th 2009)
+
+- `werkzeug.contrib.cache.Memcached` accepts now objects that
+  implement the memcache.Client interface as alternative to a list of
+  strings with server addresses.
+  There is also now a `GAEMemcachedCache` that connects to the Google
+  appengine cache.
+- explicitly convert secret keys to bytestrings now because Python
+  2.6 no longer does that.
+- `url_encode` and all interfaces that call it, support ordering of
+  options now which however is disabled by default.
+- the development server no longer resolves the addresses of clients.
+- Fixed a typo in `werkzeug.test` that broke `File`.
+- `Map.bind_to_environ` uses the `Host` header now if available.
+- Fixed `BaseCache.get_dict` (#345)
+- `werkzeug.test.Client` can now run the application buffered in which
+  case the application is properly closed automatically.
+- Fixed `Headers.set` (#354).  Caused header duplication before.
+- Fixed `Headers.pop` (#349).  default parameter was not properly
+  handled.
+- Fixed UnboundLocalError in `create_environ` (#351)
+- `Headers` is more compatible with wsgiref now.
+- `Template.render` accepts multidicts now.
+- dropped support for Python 2.3
+
+Version 0.4
+-----------
+(codename Schraubenzieher, released on November 23rd 2008)
+
+- `Client` supports an empty `data` argument now.
+- fixed a bug in `Response.application` that made it impossible to use it
+  as method decorator.
+- the session system should work on appengine now
+- the secure cookie works properly in load balanced environments with
+  different cpu architectures now.
+- `CacheControl.no_cache` and `CacheControl.private` behavior changed to
+  reflect the possibilities of the HTTP RFC.  Setting these attributes to
+  `None` or `True` now sets the value to "the empty value".
+  More details in the documentation.
+- fixed `werkzeug.contrib.atom.AtomFeed.__call__`. (#338)
+- `BaseResponse.make_conditional` now always returns `self`.  Previously
+  it didn't for post requests and such.
+- fixed a bug in boolean attribute handling of `html` and `xhtml`.
+- added graceful error handling to the debugger pastebin feature.
+- added a more list like interface to `Headers` (slicing and indexing
+  works now)
+- fixed a bug with the `__setitem__` method of `Headers` that didn't
+  properly remove all keys on replacing.
+- added `remove_entity_headers` which removes all entity headers from
+  a list of headers (or a `Headers` object)
+- the responses now automatically call `remove_entity_headers` if the
+  status code is 304.
+- fixed a bug with `Href` query parameter handling.  Previously the last
+  item of a call to `Href` was not handled properly if it was a dict.
+- headers now support a `pop` operation to better work with environ
+  properties.
+
+
+Version 0.3.1
+-------------
+(released on June 24th 2008)
+
+- fixed a security problem with `werkzeug.contrib.SecureCookie`.
+  More details available in the `release announcement`_.
+
+.. _release announcement: http://lucumr.pocoo.org/cogitations/2008/06/24/werkzeug-031-released/
+
+Version 0.3
+-----------
+(codename EUR325CAT6, released on June 14th 2008)
+
+- added support for redirecting in url routing.
+- added `Authorization` and `AuthorizationMixin`
+- added `WWWAuthenticate` and `WWWAuthenticateMixin`
+- added `parse_list_header`
+- added `parse_dict_header`
+- added `parse_authorization_header`
+- added `parse_www_authenticate_header`
+- added `_get_current_object` method to `LocalProxy` objects
+- added `parse_form_data`
+- `MultiDict`, `CombinedMultiDict`, `Headers`, and `EnvironHeaders` raise
+  special key errors now that are subclasses of `BadRequest` so if you
+  don't catch them they give meaningful HTTP responses.
+- added support for alternative encoding error handling and the new
+  `HTTPUnicodeError` which (if not caught) behaves like a `BadRequest`.
+- added `BadRequest.wrap`.
+- added ETag support to the SharedDataMiddleware and added an option
+  to disable caching.
+- fixed `is_xhr` on the request objects.
+- fixed error handling of the url adapter's `dispatch` method. (#318)
+- fixed bug with `SharedDataMiddleware`.
+- fixed `Accept.values`.
+- `EnvironHeaders` contain content-type and content-length now
+- `url_encode` treats lists and tuples in dicts passed to it as multiple
+  values for the same key so that one doesn't have to pass a `MultiDict`
+  to the function.
+- added `validate_arguments`
+- added `BaseRequest.application`
+- improved Python 2.3 support
+- `run_simple` accepts `use_debugger` and `use_evalex` parameters now,
+  like the `make_runserver` factory function from the script module.
+- the `environ_property` is now read-only by default
+- it's now possible to initialize requests as "shallow" requests which
+  causes runtime errors if the request object tries to consume the
+  input stream.
+
+
+Version 0.2
+-----------
+(codename Faustkeil, released Feb 14th 2008)
+
+- Added `AnyConverter` to the routing system.
+- Added `werkzeug.contrib.securecookie`
+- Exceptions have a ``get_response()`` method that return a response object
+- fixed the path ordering bug (#293), thanks Thomas Johansson
+- `BaseReporterStream` is now part of the werkzeug contrib module.  From
+  Werkzeug 0.3 onwards you will have to import it from there.
+- added `DispatcherMiddleware`.
+- `RequestRedirect` is now a subclass of `HTTPException` and uses a
+  301 status code instead of 302.
+- `url_encode` and `url_decode` can optionally treat keys as unicode strings
+  now, too.
+- `werkzeug.script` has a different caller format for boolean arguments now.
+- renamed `lazy_property` to `cached_property`.
+- added `import_string`.
+- added is_* properties to request objects.
+- added `empty()` method to routing rules.
+- added `werkzeug.contrib.profiler`.
+- added `extends` to `Headers`.
+- added `dump_cookie` and `parse_cookie`.
+- added `as_tuple` to the `Client`.
+- added `werkzeug.contrib.testtools`.
+- added `werkzeug.unescape`
+- added `BaseResponse.freeze`
+- added `werkzeug.contrib.atom`
+- the HTTPExceptions accept an argument `description` now which overrides the
+  default description.
+- the `MapAdapter` has a default for path info now.  If you use
+  `bind_to_environ` you don't have to pass the path later.
+- the wsgiref subclass werkzeug uses for the dev server does not use direct
+  sys.stderr logging any more but a logger called "werkzeug".
+- implemented `Href`.
+- implemented `find_modules`
+- refactored request and response objects into base objects, mixins and
+  full featured subclasses that implement all mixins.
+- added simple user agent parser
+- werkzeug's routing raises `MethodNotAllowed` now if it matches a
+  rule but for a different method.
+- many fixes and small improvements
+
+
+Version 0.1
+-----------
+(codename Wictorinoxger, released Dec 9th 2007)
+
+- Initial release
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/LICENSE	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,29 @@
+Copyright (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of the contributors may not be used to endorse or
+      promote products derived from this software without specific
+      prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/MANIFEST.in	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,8 @@
+include Makefile CHANGES LICENSE AUTHORS
+recursive-include werkzeug/debug/shared *
+recursive-include werkzeug/debug/templates *
+recursive-include tests *
+recursive-include docs *
+recursive-include examples *
+recursive-include artwork *
+prune docs/_build/doctrees
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/Makefile	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,29 @@
+#
+# Werkzeug Makefile
+# ~~~~~~~~~~~~~~~~~
+#
+# Shortcuts for various tasks.
+#
+# :copyright: (c) 2008 by the Werkzeug Team, see AUTHORS for more details.
+# :license: BSD, see LICENSE for more details.
+#
+
+TESTS = \
+	tests \
+	tests/contrib
+
+TEST_OPTIONS = \
+	-v \
+	-e '^test_app$$' #skip the test_app application object which is not a test
+
+documentation:
+	@(cd docs; make html)
+
+test:
+	@(nosetests $(TEST_OPTIONS) $(TESTS))
+
+coverage:
+	@(nosetests $(TEST_OPTIONS) --with-coverage --cover-package=werkzeug --cover-html --cover-html-dir=coverage_out $(TESTS))
+
+doctest:
+	@(cd docs; sphinx-build -b doctest . _build/doctest)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/setup.cfg	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,6 @@
+[egg_info]
+tag_build = dev
+tag_date = true
+
+[aliases]
+release = egg_info -RDb ''
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/setup.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+"""
+Werkzeug
+========
+
+Werkzeug started as simple collection of various utilities for WSGI
+applications and has become one of the most advanced WSGI utility
+modules.  It includes a powerful debugger, full featured request and
+response objects, HTTP utilities to handle entity tags, cache control
+headers, HTTP dates, cookie handling, file uploads, a powerful URL
+routing system and a bunch of community contributed addon modules.
+
+Werkzeug is unicode aware and doesn't enforce a specific template
+engine, database adapter or anything else.  It doesn't even enforce
+a specific way of handling requests and leaves all that up to the
+developer. It's most useful for end user applications which should work
+on as many server environments as possible (such as blogs, wikis,
+bulletin boards, etc.).
+
+Details and example applications are available on the
+`Werkzeug website <http://werkzeug.pocoo.org/>`_.
+
+
+Features
+--------
+
+-   unicode awareness
+
+-   request and response objects
+
+-   various utility functions for dealing with HTTP headers such as
+    `Accept` and `Cache-Control` headers.
+
+-   thread local objects with proper cleanup at request end
+
+-   an interactive debugger
+
+-   A simple WSGI server with support for threading and forking
+    with an automatic reloader.
+
+-   a flexible URL routing system with REST support.
+
+-   fully WSGI compatible
+
+
+Development Version
+-------------------
+
+The `Werkzeug tip <http://dev.pocoo.org/hg/werkzeug-main/archive/tip.zip#egg=Werkzeug-dev>`_
+is installable via `easy_install` with ``easy_install Werkzeug==dev``.
+"""
+import os
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+
+
+setup(
+    name='Werkzeug',
+    version='1.0',
+    url='http://werkzeug.pocoo.org/',
+    license='BSD',
+    author='Armin Ronacher',
+    author_email='armin.ronacher@active-4.com',
+    description='The Swiss Army knife of Python web development',
+    long_description=__doc__,
+    classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+        'Topic :: Software Development :: Libraries :: Python Modules'
+    ],
+    packages=['werkzeug', 'werkzeug.debug', 'werkzeug.contrib'],
+    package_data={
+        'werkzeug.debug': ['shared/*', 'templates/*']
+    },
+    platforms='any'
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/__init__.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,157 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug
+    ~~~~~~~~
+
+    Werkzeug is the Swiss Army knife of Python web development.
+
+    It provides useful classes and functions for any WSGI application to make
+    the life of a python web developer much easier.  All of the provided
+    classes are independent from each other so you can mix it with any other
+    library.
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from types import ModuleType
+import sys
+
+# This import magic raises concerns quite often which is why the implementation
+# and motivation is explained here in detail now.
+#
+# The majority of the functions and classes provided by Werkzeug work on the
+# HTTP and WSGI layer.  There is no useful grouping for those which is why
+# they are all importable from "werkzeug" instead of the modules where they are
+# implemented.  The downside of that is, that now everything would be loaded at
+# once, even if unused.
+#
+# The implementation of a lazy-loading module in this file replaces the
+# werkzeug package when imported from within.  Attribute access to the werkzeug
+# module will then lazily import from the modules that implement the objects.
+
+
+# import mapping to objects in other modules
+all_by_module = {
+    'werkzeug.debug':       ['DebuggedApplication'],
+    'werkzeug.local':       ['Local', 'LocalManager', 'LocalProxy',
+                             'LocalStack', 'release_local'],
+    'werkzeug.templates':   ['Template'],
+    'werkzeug.serving':     ['run_simple'],
+    'werkzeug.test':        ['Client', 'EnvironBuilder', 'create_environ',
+                             'run_wsgi_app'],
+    'werkzeug.testapp':     ['test_app'],
+    'werkzeug.exceptions':  ['abort', 'Aborter'],
+    'werkzeug.urls':        ['url_decode', 'url_encode', 'url_quote',
+                             'url_quote_plus', 'url_unquote',
+                             'url_unquote_plus', 'url_fix', 'Href',
+                             'iri_to_uri', 'uri_to_iri'],
+    'werkzeug.formparser':  ['parse_form_data'],
+    'werkzeug.utils':       ['escape', 'environ_property', 'cookie_date',
+                             'http_date', 'append_slash_redirect', 'redirect',
+                             'cached_property', 'import_string',
+                             'dump_cookie', 'parse_cookie', 'unescape',
+                             'format_string', 'find_modules', 'header_property',
+                             'html', 'xhtml', 'HTMLBuilder',
+                             'validate_arguments', 'ArgumentValidationError',
+                             'bind_arguments', 'secure_filename'],
+    'werkzeug.wsgi':        ['get_current_url', 'get_host', 'pop_path_info',
+                             'peek_path_info', 'SharedDataMiddleware',
+                             'DispatcherMiddleware', 'ClosingIterator',
+                             'FileWrapper', 'make_line_iter', 'LimitedStream',
+                             'responder', 'wrap_file', 'extract_path_info'],
+    'werkzeug.datastructures': ['MultiDict', 'CombinedMultiDict', 'Headers',
+                             'EnvironHeaders', 'ImmutableList',
+                             'ImmutableDict', 'ImmutableMultiDict',
+                             'TypeConversionDict', 'ImmutableTypeConversionDict',
+                             'Accept', 'MIMEAccept', 'CharsetAccept',
+                             'LanguageAccept', 'RequestCacheControl',
+                             'ResponseCacheControl', 'ETags', 'HeaderSet',
+                             'WWWAuthenticate', 'Authorization',
+                             'FileMultiDict', 'CallbackDict', 'FileStorage',
+                             'OrderedMultiDict', 'ImmutableOrderedMultiDict'],
+    'werkzeug.useragents':  ['UserAgent'],
+    'werkzeug.http':        ['parse_etags', 'parse_date', 'parse_cache_control_header',
+                             'is_resource_modified', 'parse_accept_header',
+                             'parse_set_header', 'quote_etag', 'unquote_etag',
+                             'generate_etag', 'dump_header',
+                             'parse_list_header', 'parse_dict_header',
+                             'parse_authorization_header',
+                             'parse_www_authenticate_header',
+                             'remove_entity_headers', 'is_entity_header',
+                             'remove_hop_by_hop_headers', 'parse_options_header',
+                             'dump_options_header', 'is_hop_by_hop_header',
+                             'unquote_header_value',
+                             'quote_header_value', 'HTTP_STATUS_CODES'],
+    'werkzeug.wrappers':    ['BaseResponse', 'BaseRequest', 'Request',
+                             'Response', 'AcceptMixin', 'ETagRequestMixin',
+                             'ETagResponseMixin', 'ResponseStreamMixin',
+                             'CommonResponseDescriptorsMixin',
+                             'UserAgentMixin', 'AuthorizationMixin',
+                             'WWWAuthenticateMixin',
+                             'CommonRequestDescriptorsMixin'],
+    'werkzeug.security':    ['generate_password_hash', 'check_password_hash'],
+    # the undocumented easteregg ;-)
+    'werkzeug._internal':   ['_easteregg']
+}
+
+# modules that should be imported when accessed as attributes of werkzeug
+attribute_modules = frozenset(['exceptions', 'routing', 'script'])
+
+
+object_origins = {}
+for module, items in all_by_module.iteritems():
+    for item in items:
+        object_origins[item] = module
+
+
+#: the cached version of the library.  We get the distribution from
+#: pkg_resources the first time this attribute is accessed.  Because
+#: this operation is quite slow it speeds up importing a lot.
+version = None
+
+class module(ModuleType):
+    """Automatically import objects from the modules."""
+
+    def __getattr__(self, name):
+        if name in object_origins:
+            module = __import__(object_origins[name], None, None, [name])
+            for extra_name in all_by_module[module.__name__]:
+                setattr(self, extra_name, getattr(module, extra_name))
+            return getattr(module, name)
+        elif name in attribute_modules:
+            __import__('werkzeug.' + name)
+        return ModuleType.__getattribute__(self, name)
+
+    def __dir__(self):
+        """Just show what we want to show."""
+        result = list(new_module.__all__)
+        result.extend(('__file__', '__path__', '__doc__', '__all__',
+                       '__docformat__', '__name__', '__path__',
+                       '__package__', '__version__'))
+        return result
+
+    @property
+    def __version__(self):
+        global version
+        if version is None:
+            try:
+                version = __import__('pkg_resources') \
+                          .get_distribution('Werkzeug').version
+            except:
+                version = 'unknown'
+        return version
+
+# keep a reference to this module so that it's not garbage collected
+old_module = sys.modules['werkzeug']
+
+
+# setup the new module and patch it into the dict of loaded modules
+new_module = sys.modules['werkzeug'] = module('werkzeug')
+new_module.__dict__.update({
+    '__file__':         __file__,
+    '__path__':         __path__,
+    '__doc__':          __doc__,
+    '__all__':          tuple(object_origins) + tuple(attribute_modules),
+    '__docformat__':    'restructuredtext en'
+})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/_internal.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,398 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug._internal
+    ~~~~~~~~~~~~~~~~~~
+
+    This module provides internally used helpers and constants.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import inspect
+from weakref import WeakKeyDictionary
+from cStringIO import StringIO
+from Cookie import BaseCookie, Morsel, CookieError
+from time import gmtime
+from datetime import datetime, date
+
+
+_logger = None
+_empty_stream = StringIO('')
+_signature_cache = WeakKeyDictionary()
+_epoch_ord = date(1970, 1, 1).toordinal()
+
+
+HTTP_STATUS_CODES = {
+    100:    'Continue',
+    101:    'Switching Protocols',
+    102:    'Processing',
+    200:    'OK',
+    201:    'Created',
+    202:    'Accepted',
+    203:    'Non Authoritative Information',
+    204:    'No Content',
+    205:    'Reset Content',
+    206:    'Partial Content',
+    207:    'Multi Status',
+    226:    'IM Used',              # see RFC 3229
+    300:    'Multiple Choices',
+    301:    'Moved Permanently',
+    302:    'Found',
+    303:    'See Other',
+    304:    'Not Modified',
+    305:    'Use Proxy',
+    307:    'Temporary Redirect',
+    400:    'Bad Request',
+    401:    'Unauthorized',
+    402:    'Payment Required',     # unused
+    403:    'Forbidden',
+    404:    'Not Found',
+    405:    'Method Not Allowed',
+    406:    'Not Acceptable',
+    407:    'Proxy Authentication Required',
+    408:    'Request Timeout',
+    409:    'Conflict',
+    410:    'Gone',
+    411:    'Length Required',
+    412:    'Precondition Failed',
+    413:    'Request Entity Too Large',
+    414:    'Request URI Too Long',
+    415:    'Unsupported Media Type',
+    416:    'Requested Range Not Satisfiable',
+    417:    'Expectation Failed',
+    418:    'I\'m a teapot',        # see RFC 2324
+    422:    'Unprocessable Entity',
+    423:    'Locked',
+    424:    'Failed Dependency',
+    426:    'Upgrade Required',
+    449:    'Retry With',           # proprietary MS extension
+    500:    'Internal Server Error',
+    501:    'Not Implemented',
+    502:    'Bad Gateway',
+    503:    'Service Unavailable',
+    504:    'Gateway Timeout',
+    505:    'HTTP Version Not Supported',
+    507:    'Insufficient Storage',
+    510:    'Not Extended'
+}
+
+
+class _Missing(object):
+
+    def __repr__(self):
+        return 'no value'
+
+    def __reduce__(self):
+        return '_missing'
+
+_missing = _Missing()
+
+
+def _proxy_repr(cls):
+    def proxy_repr(self):
+        return '%s(%s)' % (self.__class__.__name__, cls.__repr__(self))
+    return proxy_repr
+
+
+def _get_environ(obj):
+    env = getattr(obj, 'environ', obj)
+    assert isinstance(env, dict), \
+        '%r is not a WSGI environment (has to be a dict)' % type(obj).__name__
+    return env
+
+
+def _log(type, message, *args, **kwargs):
+    """Log into the internal werkzeug logger."""
+    global _logger
+    if _logger is None:
+        import logging
+        _logger = logging.getLogger('werkzeug')
+        # Only set up a default log handler if the
+        # end-user application didn't set anything up.
+        if not logging.root.handlers and _logger.level == logging.NOTSET:
+            _logger.setLevel(logging.INFO)
+            handler = logging.StreamHandler()
+            _logger.addHandler(handler)
+    getattr(_logger, type)(message.rstrip(), *args, **kwargs)
+
+
+def _parse_signature(func):
+    """Return a signature object for the function."""
+    if hasattr(func, 'im_func'):
+        func = func.im_func
+
+    # if we have a cached validator for this function, return it
+    parse = _signature_cache.get(func)
+    if parse is not None:
+        return parse
+
+    # inspect the function signature and collect all the information
+    positional, vararg_var, kwarg_var, defaults = inspect.getargspec(func)
+    defaults = defaults or ()
+    arg_count = len(positional)
+    arguments = []
+    for idx, name in enumerate(positional):
+        if isinstance(name, list):
+            raise TypeError('cannot parse functions that unpack tuples '
+                            'in the function signature')
+        try:
+            default = defaults[idx - arg_count]
+        except IndexError:
+            param = (name, False, None)
+        else:
+            param = (name, True, default)
+        arguments.append(param)
+    arguments = tuple(arguments)
+
+    def parse(args, kwargs):
+        new_args = []
+        missing = []
+        extra = {}
+
+        # consume as many arguments as positional as possible
+        for idx, (name, has_default, default) in enumerate(arguments):
+            try:
+                new_args.append(args[idx])
+            except IndexError:
+                try:
+                    new_args.append(kwargs.pop(name))
+                except KeyError:
+                    if has_default:
+                        new_args.append(default)
+                    else:
+                        missing.append(name)
+            else:
+                if name in kwargs:
+                    extra[name] = kwargs.pop(name)
+
+        # handle extra arguments
+        extra_positional = args[arg_count:]
+        if vararg_var is not None:
+            new_args.extend(extra_positional)
+            extra_positional = ()
+        if kwargs and not kwarg_var is not None:
+            extra.update(kwargs)
+            kwargs = {}
+
+        return new_args, kwargs, missing, extra, extra_positional, \
+               arguments, vararg_var, kwarg_var
+    _signature_cache[func] = parse
+    return parse
+
+
+def _patch_wrapper(old, new):
+    """Helper function that forwards all the function details to the
+    decorated function."""
+    try:
+        new.__name__ = old.__name__
+        new.__module__ = old.__module__
+        new.__doc__ = old.__doc__
+        new.__dict__ = old.__dict__
+    except:
+        pass
+    return new
+
+
+def _decode_unicode(value, charset, errors):
+    """Like the regular decode function but this one raises an
+    `HTTPUnicodeError` if errors is `strict`."""
+    fallback = None
+    if errors.startswith('fallback:'):
+        fallback = errors[9:]
+        errors = 'strict'
+    try:
+        return value.decode(charset, errors)
+    except UnicodeError, e:
+        if fallback is not None:
+            return value.decode(fallback, 'ignore')
+        from werkzeug.exceptions import HTTPUnicodeError
+        raise HTTPUnicodeError(str(e))
+
+
+def _iter_modules(path):
+    """Iterate over all modules in a package."""
+    import os
+    import pkgutil
+    if hasattr(pkgutil, 'iter_modules'):
+        for importer, modname, ispkg in pkgutil.iter_modules(path):
+            yield modname, ispkg
+        return
+    from inspect import getmodulename
+    from pydoc import ispackage
+    found = set()
+    for path in path:
+        for filename in os.listdir(path):
+            p = os.path.join(path, filename)
+            modname = getmodulename(filename)
+            if modname and modname != '__init__':
+                if modname not in found:
+                    found.add(modname)
+                    yield modname, ispackage(modname)
+
+
+def _dump_date(d, delim):
+    """Used for `http_date` and `cookie_date`."""
+    if d is None:
+        d = gmtime()
+    elif isinstance(d, datetime):
+        d = d.utctimetuple()
+    elif isinstance(d, (int, long, float)):
+        d = gmtime(d)
+    return '%s, %02d%s%s%s%s %02d:%02d:%02d GMT' % (
+        ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')[d.tm_wday],
+        d.tm_mday, delim,
+        ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
+         'Oct', 'Nov', 'Dec')[d.tm_mon - 1],
+        delim, str(d.tm_year), d.tm_hour, d.tm_min, d.tm_sec
+    )
+
+
+def _date_to_unix(arg):
+    """Converts a timetuple, integer or datetime object into the seconds from
+    epoch in utc.
+    """
+    if isinstance(arg, datetime):
+        arg = arg.utctimetuple()
+    elif isinstance(arg, (int, long, float)):
+        return int(arg)
+    year, month, day, hour, minute, second = arg[:6]
+    days = date(year, month, 1).toordinal() - _epoch_ord + day - 1
+    hours = days * 24 + hour
+    minutes = hours * 60 + minute
+    seconds = minutes * 60 + second
+    return seconds
+
+
+class _ExtendedMorsel(Morsel):
+    _reserved = {'httponly': 'HttpOnly'}
+    _reserved.update(Morsel._reserved)
+
+    def __init__(self, name=None, value=None):
+        Morsel.__init__(self)
+        if name is not None:
+            self.set(name, value, value)
+
+    def OutputString(self, attrs=None):
+        httponly = self.pop('httponly', False)
+        result = Morsel.OutputString(self, attrs).rstrip('\t ;')
+        if httponly:
+            result += '; HttpOnly'
+        return result
+
+
+class _ExtendedCookie(BaseCookie):
+    """Form of the base cookie that doesn't raise a `CookieError` for
+    malformed keys.  This has the advantage that broken cookies submitted
+    by nonstandard browsers don't cause the cookie to be empty.
+    """
+
+    def _BaseCookie__set(self, key, real_value, coded_value):
+        morsel = self.get(key, _ExtendedMorsel())
+        try:
+            morsel.set(key, real_value, coded_value)
+        except CookieError:
+            pass
+        dict.__setitem__(self, key, morsel)
+
+
+class _DictAccessorProperty(object):
+    """Baseclass for `environ_property` and `header_property`."""
+    read_only = False
+
+    def __init__(self, name, default=None, load_func=None, dump_func=None,
+                 read_only=None, doc=None):
+        self.name = name
+        self.default = default
+        self.load_func = load_func
+        self.dump_func = dump_func
+        if read_only is not None:
+            self.read_only = read_only
+        self.__doc__ = doc
+
+    def __get__(self, obj, type=None):
+        if obj is None:
+            return self
+        storage = self.lookup(obj)
+        if self.name not in storage:
+            return self.default
+        rv = storage[self.name]
+        if self.load_func is not None:
+            try:
+                rv = self.load_func(rv)
+            except (ValueError, TypeError):
+                rv = self.default
+        return rv
+
+    def __set__(self, obj, value):
+        if self.read_only:
+            raise AttributeError('read only property')
+        if self.dump_func is not None:
+            value = self.dump_func(value)
+        self.lookup(obj)[self.name] = value
+
+    def __delete__(self, obj):
+        if self.read_only:
+            raise AttributeError('read only property')
+        self.lookup(obj).pop(self.name, None)
+
+    def __repr__(self):
+        return '<%s %s>' % (
+            self.__class__.__name__,
+            self.name
+        )
+
+
+def _easteregg(app):
+    """Like the name says.  But who knows how it works?"""
+    gyver = '\n'.join([x + (77 - len(x)) * ' ' for x in '''
+eJyFlzuOJDkMRP06xRjymKgDJCDQStBYT8BCgK4gTwfQ2fcFs2a2FzvZk+hvlcRvRJD148efHt9m
+9Xz94dRY5hGt1nrYcXx7us9qlcP9HHNh28rz8dZj+q4rynVFFPdlY4zH873NKCexrDM6zxxRymzz
+4QIxzK4bth1PV7+uHn6WXZ5C4ka/+prFzx3zWLMHAVZb8RRUxtFXI5DTQ2n3Hi2sNI+HK43AOWSY
+jmEzE4naFp58PdzhPMdslLVWHTGUVpSxImw+pS/D+JhzLfdS1j7PzUMxij+mc2U0I9zcbZ/HcZxc
+q1QjvvcThMYFnp93agEx392ZdLJWXbi/Ca4Oivl4h/Y1ErEqP+lrg7Xa4qnUKu5UE9UUA4xeqLJ5
+jWlPKJvR2yhRI7xFPdzPuc6adXu6ovwXwRPXXnZHxlPtkSkqWHilsOrGrvcVWXgGP3daXomCj317
+8P2UOw/NnA0OOikZyFf3zZ76eN9QXNwYdD8f8/LdBRFg0BO3bB+Pe/+G8er8tDJv83XTkj7WeMBJ
+v/rnAfdO51d6sFglfi8U7zbnr0u9tyJHhFZNXYfH8Iafv2Oa+DT6l8u9UYlajV/hcEgk1x8E8L/r
+XJXl2SK+GJCxtnyhVKv6GFCEB1OO3f9YWAIEbwcRWv/6RPpsEzOkXURMN37J0PoCSYeBnJQd9Giu
+LxYQJNlYPSo/iTQwgaihbART7Fcyem2tTSCcwNCs85MOOpJtXhXDe0E7zgZJkcxWTar/zEjdIVCk
+iXy87FW6j5aGZhttDBoAZ3vnmlkx4q4mMmCdLtnHkBXFMCReqthSGkQ+MDXLLCpXwBs0t+sIhsDI
+tjBB8MwqYQpLygZ56rRHHpw+OAVyGgaGRHWy2QfXez+ZQQTTBkmRXdV/A9LwH6XGZpEAZU8rs4pE
+1R4FQ3Uwt8RKEtRc0/CrANUoes3EzM6WYcFyskGZ6UTHJWenBDS7h163Eo2bpzqxNE9aVgEM2CqI
+GAJe9Yra4P5qKmta27VjzYdR04Vc7KHeY4vs61C0nbywFmcSXYjzBHdiEjraS7PGG2jHHTpJUMxN
+Jlxr3pUuFvlBWLJGE3GcA1/1xxLcHmlO+LAXbhrXah1tD6Ze+uqFGdZa5FM+3eHcKNaEarutAQ0A
+QMAZHV+ve6LxAwWnXbbSXEG2DmCX5ijeLCKj5lhVFBrMm+ryOttCAeFpUdZyQLAQkA06RLs56rzG
+8MID55vqr/g64Qr/wqwlE0TVxgoiZhHrbY2h1iuuyUVg1nlkpDrQ7Vm1xIkI5XRKLedN9EjzVchu
+jQhXcVkjVdgP2O99QShpdvXWoSwkp5uMwyjt3jiWCqWGSiaaPAzohjPanXVLbM3x0dNskJsaCEyz
+DTKIs+7WKJD4ZcJGfMhLFBf6hlbnNkLEePF8Cx2o2kwmYF4+MzAxa6i+6xIQkswOqGO+3x9NaZX8
+MrZRaFZpLeVTYI9F/djY6DDVVs340nZGmwrDqTCiiqD5luj3OzwpmQCiQhdRYowUYEA3i1WWGwL4
+GCtSoO4XbIPFeKGU13XPkDf5IdimLpAvi2kVDVQbzOOa4KAXMFlpi/hV8F6IDe0Y2reg3PuNKT3i
+RYhZqtkQZqSB2Qm0SGtjAw7RDwaM1roESC8HWiPxkoOy0lLTRFG39kvbLZbU9gFKFRvixDZBJmpi
+Xyq3RE5lW00EJjaqwp/v3EByMSpVZYsEIJ4APaHmVtpGSieV5CALOtNUAzTBiw81GLgC0quyzf6c
+NlWknzJeCsJ5fup2R4d8CYGN77mu5vnO1UqbfElZ9E6cR6zbHjgsr9ly18fXjZoPeDjPuzlWbFwS
+pdvPkhntFvkc13qb9094LL5NrA3NIq3r9eNnop9DizWOqCEbyRBFJTHn6Tt3CG1o8a4HevYh0XiJ
+sR0AVVHuGuMOIfbuQ/OKBkGRC6NJ4u7sbPX8bG/n5sNIOQ6/Y/BX3IwRlTSabtZpYLB85lYtkkgm
+p1qXK3Du2mnr5INXmT/78KI12n11EFBkJHHp0wJyLe9MvPNUGYsf+170maayRoy2lURGHAIapSpQ
+krEDuNoJCHNlZYhKpvw4mspVWxqo415n8cD62N9+EfHrAvqQnINStetek7RY2Urv8nxsnGaZfRr/
+nhXbJ6m/yl1LzYqscDZA9QHLNbdaSTTr+kFg3bC0iYbX/eQy0Bv3h4B50/SGYzKAXkCeOLI3bcAt
+mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p
+7f2zLkGNv8b191cD/3vs9Q833z8t'''.decode('base64').decode('zlib').splitlines()])
+    def easteregged(environ, start_response):
+        def injecting_start_response(status, headers, exc_info=None):
+            headers.append(('X-Powered-By', 'Werkzeug'))
+            return start_response(status, headers, exc_info)
+        if environ.get('QUERY_STRING') != 'macgybarchakku':
+            return app(environ, injecting_start_response)
+        injecting_start_response('200 OK', [('Content-Type', 'text/html')])
+        return ['''<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>About Werkzeug</>
+<style type="text/css">
+  body { font: 15px Georgia, serif; text-align: center; }
+  a { color: #333; text-decoration: none; }
+  h1 { font-size: 30px; margin: 20px 0 10px 0; }
+  p { margin: 0 0 30px 0; }
+  pre { font: 11px 'Consolas', 'Monaco', monospace; line-height: 0.95; }
+</style>
+<h1><a href="http://werkzeug.pocoo.org/">Werkzeug</a></h1>
+<p>the Swiss Army knife of Python web development.
+<pre>%s\n\n\n</>''' % gyver]
+    return easteregged
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/__init__.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib
+    ~~~~~~~~~~~~~~~~
+
+    Contains user-submitted code that other users may find useful, but which
+    is not part of the Werkzeug core.  Anyone can write code for inclusion in
+    the `contrib` package.  All modules in this package are distributed as an
+    add-on library and thus are not part of Werkzeug itself.
+
+    This file itself is mostly for informational purposes and to tell the
+    Python interpreter that `contrib` is a package.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/atom.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,343 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.atom
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    This module provides a class called :class:`AtomFeed` which can be
+    used to generate feeds in the Atom syndication format (see :rfc:`4287`).
+
+    Example::
+
+        def atom_feed(request):
+            feed = AtomFeed("My Blog", feed_url=request.url,
+                            url=request.host_url,
+                            subtitle="My example blog for a feed test.")
+            for post in Post.query.limit(10).all():
+                feed.add(post.title, post.body, content_type='html',
+                         author=post.author, url=post.url, id=post.uid,
+                         updated=post.last_update, published=post.pub_date)
+            return feed.get_response()
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from datetime import datetime
+from werkzeug.utils import escape
+from werkzeug.wrappers import BaseResponse
+
+
+XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'
+
+
+def _make_text_block(name, content, content_type=None):
+    """Helper function for the builder that creates an XML text block."""
+    if content_type == 'xhtml':
+        return u'<%s type="xhtml"><div xmlns="%s">%s</div></%s>\n' % \
+               (name, XHTML_NAMESPACE, content, name)
+    if not content_type:
+        return u'<%s>%s</%s>\n' % (name, escape(content), name)
+    return u'<%s type="%s">%s</%s>\n' % (name, content_type,
+                                         escape(content), name)
+
+
+def format_iso8601(obj):
+    """Format a datetime object for iso8601"""
+    return obj.strftime('%Y-%m-%dT%H:%M:%SZ')
+
+
+class AtomFeed(object):
+    """A helper class that creates Atom feeds.
+
+    :param title: the title of the feed. Required.
+    :param title_type: the type attribute for the title element.  One of
+                       ``'html'``, ``'text'`` or ``'xhtml'``.
+    :param url: the url for the feed (not the url *of* the feed)
+    :param id: a globally unique id for the feed.  Must be an URI.  If
+               not present the `feed_url` is used, but one of both is
+               required.
+    :param updated: the time the feed was modified the last time.  Must
+                    be a :class:`datetime.datetime` object.  If not
+                    present the latest entry's `updated` is used.
+    :param feed_url: the URL to the feed.  Should be the URL that was
+                     requested.
+    :param author: the author of the feed.  Must be either a string (the
+                   name) or a dict with name (required) and uri or
+                   email (both optional).  Can be a list of (may be
+                   mixed, too) strings and dicts, too, if there are
+                   multiple authors. Required if not every entry has an
+                   author element.
+    :param icon: an icon for the feed.
+    :param logo: a logo for the feed.
+    :param rights: copyright information for the feed.
+    :param rights_type: the type attribute for the rights element.  One of
+                        ``'html'``, ``'text'`` or ``'xhtml'``.  Default is
+                        ``'text'``.
+    :param subtitle: a short description of the feed.
+    :param subtitle_type: the type attribute for the subtitle element.
+                          One of ``'text'``, ``'html'``, ``'text'``
+                          or ``'xhtml'``.  Default is ``'text'``.
+    :param links: additional links.  Must be a list of dictionaries with
+                  href (required) and rel, type, hreflang, title, length
+                  (all optional)
+    :param generator: the software that generated this feed.  This must be
+                      a tuple in the form ``(name, url, version)``.  If
+                      you don't want to specify one of them, set the item
+                      to `None`.
+    :param entries: a list with the entries for the feed. Entries can also
+                    be added later with :meth:`add`.
+
+    For more information on the elements see
+    http://www.atomenabled.org/developers/syndication/
+
+    Everywhere where a list is demanded, any iterable can be used.
+    """
+
+    default_generator = ('Werkzeug', None, None)
+
+    def __init__(self, title=None, entries=None, **kwargs):
+        self.title = title
+        self.title_type = kwargs.get('title_type', 'text')
+        self.url = kwargs.get('url')
+        self.feed_url = kwargs.get('feed_url', self.url)
+        self.id = kwargs.get('id', self.feed_url)
+        self.updated = kwargs.get('updated')
+        self.author = kwargs.get('author', ())
+        self.icon = kwargs.get('icon')
+        self.logo = kwargs.get('logo')
+        self.rights = kwargs.get('rights')
+        self.rights_type = kwargs.get('rights_type')
+        self.subtitle = kwargs.get('subtitle')
+        self.subtitle_type = kwargs.get('subtitle_type', 'text')
+        self.generator = kwargs.get('generator')
+        if self.generator is None:
+            self.generator = self.default_generator
+        self.links = kwargs.get('links', [])
+        self.entries = entries and list(entries) or []
+
+        if not hasattr(self.author, '__iter__') \
+           or isinstance(self.author, (basestring, dict)):
+            self.author = [self.author]
+        for i, author in enumerate(self.author):
+            if not isinstance(author, dict):
+                self.author[i] = {'name': author}
+
+        if not self.title:
+            raise ValueError('title is required')
+        if not self.id:
+            raise ValueError('id is required')
+        for author in self.author:
+            if 'name' not in author:
+                raise TypeError('author must contain at least a name')
+
+    def add(self, *args, **kwargs):
+        """Add a new entry to the feed.  This function can either be called
+        with a :class:`FeedEntry` or some keyword and positional arguments
+        that are forwarded to the :class:`FeedEntry` constructor.
+        """
+        if len(args) == 1 and not kwargs and isinstance(args[0], FeedEntry):
+            self.entries.append(args[0])
+        else:
+            kwargs['feed_url'] = self.feed_url
+            self.entries.append(FeedEntry(*args, **kwargs))
+
+    def __repr__(self):
+        return '<%s %r (%d entries)>' % (
+            self.__class__.__name__,
+            self.title,
+            len(self.entries)
+        )
+
+    def generate(self):
+        """Return a generator that yields pieces of XML."""
+        # atom demands either an author element in every entry or a global one
+        if not self.author:
+            if False in map(lambda e: bool(e.author), self.entries):
+                self.author = ({'name': u'unbekannter Autor'},)
+
+        if not self.updated:
+            dates = sorted([entry.updated for entry in self.entries])
+            self.updated = dates and dates[-1] or datetime.utcnow()
+
+        yield u'<?xml version="1.0" encoding="utf-8"?>\n'
+        yield u'<feed xmlns="http://www.w3.org/2005/Atom">\n'
+        yield '  ' + _make_text_block('title', self.title, self.title_type)
+        yield u'  <id>%s</id>\n' % escape(self.id)
+        yield u'  <updated>%s</updated>\n' % format_iso8601(self.updated)
+        if self.url:
+            yield u'  <link href="%s" />\n' % escape(self.url, True)
+        if self.feed_url:
+            yield u'  <link href="%s" rel="self" />\n' % \
+                escape(self.feed_url, True)
+        for link in self.links:
+            yield u'  <link %s/>\n' % ''.join('%s="%s" ' % \
+                (k, escape(link[k], True)) for k in link)
+        for author in self.author:
+            yield u'  <author>\n'
+            yield u'    <name>%s</name>\n' % escape(author['name'])
+            if 'uri' in author:
+                yield u'    <uri>%s</uri>\n' % escape(author['uri'])
+            if 'email' in author:
+                yield '    <email>%s</email>\n' % escape(author['email'])
+            yield '  </author>\n'
+        if self.subtitle:
+            yield '  ' + _make_text_block('subtitle', self.subtitle,
+                                          self.subtitle_type)
+        if self.icon:
+            yield u'  <icon>%s</icon>\n' % escape(self.icon)
+        if self.logo:
+            yield u'  <logo>%s</logo>\n' % escape(self.logo)
+        if self.rights:
+            yield '  ' + _make_text_block('rights', self.rights,
+                                          self.rights_type)
+        generator_name, generator_url, generator_version = self.generator
+        if generator_name or generator_url or generator_version:
+            tmp = [u'  <generator']
+            if generator_url:
+                tmp.append(u' uri="%s"' % escape(generator_url, True))
+            if generator_version:
+                tmp.append(u' version="%s"' % escape(generator_version, True))
+            tmp.append(u'>%s</generator>\n' % escape(generator_name))
+            yield u''.join(tmp)
+        for entry in self.entries:
+            for line in entry.generate():
+                yield u'  ' + line
+        yield u'</feed>\n'
+
+    def to_string(self):
+        """Convert the feed into a string."""
+        return u''.join(self.generate())
+
+    def get_response(self):
+        """Return a response object for the feed."""
+        return BaseResponse(self.to_string(), mimetype='application/atom+xml')
+
+    def __call__(self, environ, start_response):
+        """Use the class as WSGI response object."""
+        return self.get_response()(environ, start_response)
+
+    def __unicode__(self):
+        return self.to_string()
+
+    def __str__(self):
+        return self.to_string().encode('utf-8')
+
+
+class FeedEntry(object):
+    """Represents a single entry in a feed.
+
+    :param title: the title of the entry. Required.
+    :param title_type: the type attribute for the title element.  One of
+                       ``'html'``, ``'text'`` or ``'xhtml'``.
+    :param content: the content of the entry.
+    :param content_type: the type attribute for the content element.  One
+                         of ``'html'``, ``'text'`` or ``'xhtml'``.
+    :param summary: a summary of the entry's content.
+    :param summary_type: the type attribute for the summary element.  One
+                         of ``'html'``, ``'text'`` or ``'xhtml'``.
+    :param url: the url for the entry.
+    :param id: a globally unique id for the entry.  Must be an URI.  If
+               not present the URL is used, but one of both is required.
+    :param updated: the time the entry was modified the last time.  Must
+                    be a :class:`datetime.datetime` object. Required.
+    :param author: the author of the feed.  Must be either a string (the
+                   name) or a dict with name (required) and uri or
+                   email (both optional).  Can be a list of (may be
+                   mixed, too) strings and dicts, too, if there are
+                   multiple authors. Required if not every entry has an
+                   author element.
+    :param published: the time the entry was initially published.  Must
+                      be a :class:`datetime.datetime` object.
+    :param rights: copyright information for the entry.
+    :param rights_type: the type attribute for the rights element.  One of
+                        ``'html'``, ``'text'`` or ``'xhtml'``.  Default is
+                        ``'text'``.
+    :param links: additional links.  Must be a list of dictionaries with
+                  href (required) and rel, type, hreflang, title, length
+                  (all optional)
+    :param xml_base: The xml base (url) for this feed item.  If not provided
+                     it will default to the item url.
+
+    For more information on the elements see
+    http://www.atomenabled.org/developers/syndication/
+
+    Everywhere where a list is demanded, any iterable can be used.
+    """
+
+    def __init__(self, title=None, content=None, feed_url=None, **kwargs):
+        self.title = title
+        self.title_type = kwargs.get('title_type', 'text')
+        self.content = content
+        self.content_type = kwargs.get('content_type', 'html')
+        self.url = kwargs.get('url')
+        self.id = kwargs.get('id', self.url)
+        self.updated = kwargs.get('updated')
+        self.summary = kwargs.get('summary')
+        self.summary_type = kwargs.get('summary_type', 'html')
+        self.author = kwargs.get('author')
+        self.published = kwargs.get('published')
+        self.rights = kwargs.get('rights')
+        self.links = kwargs.get('links', [])
+        self.xml_base = kwargs.get('xml_base', feed_url)
+
+        if not hasattr(self.author, '__iter__') \
+           or isinstance(self.author, (basestring, dict)):
+            self.author = [self.author]
+        for i, author in enumerate(self.author):
+            if not isinstance(author, dict):
+                self.author[i] = {'name': author}
+
+        if not self.title:
+            raise ValueError('title is required')
+        if not self.id:
+            raise ValueError('id is required')
+        if not self.updated:
+            raise ValueError('updated is required')
+
+    def __repr__(self):
+        return '<%s %r>' % (
+            self.__class__.__name__,
+            self.title
+        )
+
+    def generate(self):
+        """Yields pieces of ATOM XML."""
+        base = ''
+        if self.xml_base:
+            base = ' xml:base="%s"' % escape(self.xml_base, True)
+        yield u'<entry%s>\n' % base
+        yield u'  ' + _make_text_block('title', self.title, self.title_type)
+        yield u'  <id>%s</id>\n' % escape(self.id)
+        yield u'  <updated>%s</updated>\n' % format_iso8601(self.updated)
+        if self.published:
+            yield u'  <published>%s</published>\n' % \
+                  format_iso8601(self.published)
+        if self.url:
+            yield u'  <link href="%s" />\n' % escape(self.url)
+        for author in self.author:
+            yield u'  <author>\n'
+            yield u'    <name>%s</name>\n' % escape(author['name'])
+            if 'uri' in author:
+                yield u'    <uri>%s</uri>\n' % escape(author['uri'])
+            if 'email' in author:
+                yield u'    <email>%s</email>\n' % escape(author['email'])
+            yield u'  </author>\n'
+        for link in self.links:
+            yield u'  <link %s/>\n' % ''.join('%s="%s" ' % \
+                (k, escape(link[k], True)) for k in link)
+        if self.summary:
+            yield u'  ' + _make_text_block('summary', self.summary,
+                                           self.summary_type)
+        if self.content:
+            yield u'  ' + _make_text_block('content', self.content,
+                                           self.content_type)
+        yield u'</entry>\n'
+
+    def to_string(self):
+        """Convert the feed item into a unicode object."""
+        return u''.join(self.generate())
+
+    def __unicode__(self):
+        return self.to_string()
+
+    def __str__(self):
+        return self.to_string().encode('utf-8')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/cache.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,511 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.cache
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    The main problem with dynamic Web sites is, well, they're dynamic.  Each
+    time a user requests a page, the webserver executes a lot of code, queries
+    the database, renders templates until the visitor gets the page he sees.
+
+    This is a lot more expensive than just loading a file from the file system
+    and sending it to the visitor.
+
+    For most Web applications, this overhead isn't a big deal but once it
+    becomes, you will be glad to have a cache system in place.
+
+    How Caching Works
+    =================
+
+    Caching is pretty simple.  Basically you have a cache object lurking around
+    somewhere that is connected to a remote cache or the file system or
+    something else.  When the request comes in you check if the current page
+    is already in the cache and if, you're returning it.  Otherwise you generate
+    the page and put it into the cache.  (Or a fragment of the page, you don't
+    have to cache the full thing)
+
+    Here a simple example of how to cache a sidebar for a template::
+
+        def get_sidebar(user):
+            identifier = 'sidebar_for/user%d' % user.id
+            value = cache.get(identifier)
+            if value is not None:
+                return value
+            value = generate_sidebar_for(user=user)
+            cache.set(identifier, value, timeout=60 * 5)
+            return value
+
+    Creating a Cache Object
+    =======================
+
+    To create a cache object you just import the cache system of your choice
+    from the cache module and instanciate it.  Then you can start working
+    with that object:
+
+    >>> from werkzeug.contrib.cache import SimpleCache
+    >>> c = SimpleCache()
+    >>> c.set("foo", "value")
+    >>> c.get("foo")
+    'value'
+    >>> c.get("missing") is None
+    True
+
+    Please keep in mind that you have to create the cache and put it somewhere
+    you have access to it (either as a module global you can import or if you
+    put it onto your WSGI application).
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import re
+try:
+    from hashlib import md5
+except ImportError:
+    from md5 import new as md5
+from itertools import izip
+from time import time
+from cPickle import loads, dumps, load, dump, HIGHEST_PROTOCOL
+
+
+class BaseCache(object):
+    """Baseclass for the cache systems.  All the cache systems implement this
+    API or a superset of it.
+
+    :param default_timeout: the default timeout that is used if no timeout is
+                            specified on :meth:`set`.
+    """
+
+    def __init__(self, default_timeout=300):
+        self.default_timeout = default_timeout
+
+    def get(self, key):
+        """Looks up key in the cache and returns it.  If the key does not
+        exist `None` is returned instead.
+
+        :param key: the key to be looked up.
+        """
+        return None
+
+    def delete(self, key):
+        """Deletes `key` from the cache.  If it does not exist in the cache
+        nothing happens.
+
+        :param key: the key to delete.
+        """
+        pass
+
+    def get_many(self, *keys):
+        """Returns a list of keys.  For each key a item in the list is
+        created.  Example::
+
+            foo, bar = cache.get_many("foo", "bar")
+
+        If a key can't be looked up `None` is returned for that key
+        instead.
+
+        :param keys: The function accepts multiple keys as positional
+                     arguments.
+        """
+        return map(self.get, keys)
+
+    def get_dict(self, *keys):
+        """Works like :meth:`get_many` but returns a dict::
+
+            d = cache.get_dict("foo", "bar")
+            foo = d["foo"]
+            bar = d["bar"]
+
+        :param keys: The function accepts multiple keys as positional
+                     arguments.
+        """
+        return dict(izip(keys, self.get_many(*keys)))
+
+    def set(self, key, value, timeout=None):
+        """Adds or overrides a key in the cache.
+
+        :param key: the key to set
+        :param value: the value for the key
+        :param timeout: the cache timeout for the key or the default
+                        timeout if not specified.
+        """
+        pass
+
+    def add(self, key, value, timeout=None):
+        """Works like :meth:`set` but does not override already existing
+        values.
+
+        :param key: the key to set
+        :param value: the value for the key
+        :param timeout: the cache timeout for the key or the default
+                        timeout if not specified.
+        """
+        pass
+
+    def set_many(self, mapping, timeout=None):
+        """Sets multiple keys and values from a dict.
+
+        :param mapping: a dict with the values to set.
+        :param timeout: the cache timeout for the key or the default
+                        timeout if not specified.
+        """
+        for key, value in mapping.iteritems():
+            self.set(key, value, timeout)
+
+    def delete_many(self, *keys):
+        """Deletes multiple keys at once.
+
+        :param keys: The function accepts multiple keys as positional
+                     arguments.
+        """
+        for key in keys:
+            self.delete(key)
+
+    def clear(self):
+        """Clears the cache.  Keep in mind that not all caches support
+        clearning of the full cache.
+        """
+        pass
+
+    def inc(self, key, delta=1):
+        """Increments the value of a key by `delta`.  If the key does
+        not yet exist it is initialized with `delta`.
+
+        For supporting caches this is an atomic operation.
+
+        :param key: the key to increment.
+        :param delta: the delta to add.
+        """
+        self.set(key, (self.get(key) or 0) + delta)
+
+    def dec(self, key, delta=1):
+        """Decrements the value of a key by `delta`.  If the key does
+        not yet exist it is initialized with `-delta`.
+
+        For supporting caches this is an atomic operation.
+
+        :param key: the key to increment.
+        :param delta: the delta to subtract.
+        """
+        self.set(key, (self.get(key) or 0) - delta)
+
+
+class NullCache(BaseCache):
+    """A cache that doesn't cache.  This can be useful for unit testing.
+
+    :param default_timeout: a dummy parameter that is ignored but exists
+                            for API compatibility with other caches.
+    """
+
+
+class SimpleCache(BaseCache):
+    """Simple memory cache for single process environments.  This class exists
+    mainly for the development server and is not 100% thread safe.  It tries
+    to use as many atomic operations as possible and no locks for simplicity
+    but it could happen under heavy load that keys are added multiple times.
+
+    :param threshold: the maximum number of items the cache stores before
+                      it starts deleting some.
+    :param default_timeout: the default timeout that is used if no timeout is
+                            specified on :meth:`~BaseCache.set`.
+    """
+
+    def __init__(self, threshold=500, default_timeout=300):
+        BaseCache.__init__(self, default_timeout)
+        self._cache = {}
+        self.clear = self._cache.clear
+        self._threshold = threshold
+
+    def _prune(self):
+        if len(self._cache) > self._threshold:
+            now = time()
+            for idx, (key, (expires, _)) in enumerate(self._cache.items()):
+                if expires <= now or idx % 3 == 0:
+                    self._cache.pop(key, None)
+
+    def get(self, key):
+        now = time()
+        expires, value = self._cache.get(key, (0, None))
+        if expires > time():
+            return loads(value)
+
+    def set(self, key, value, timeout=None):
+        if timeout is None:
+            timeout = self.default_timeout
+        self._prune()
+        self._cache[key] = (time() + timeout, dumps(value, HIGHEST_PROTOCOL))
+
+    def add(self, key, value, timeout=None):
+        if timeout is None:
+            timeout = self.default_timeout
+        if len(self._cache) > self._threshold:
+            self._prune()
+        item = (time() + timeout, dumps(value, HIGHEST_PROTOCOL))
+        self._cache.setdefault(key, item)
+
+    def delete(self, key):
+        self._cache.pop(key, None)
+
+
+_test_memcached_key = re.compile(r'[^\x00-\x21\xff]{1,250}$').match
+
+class MemcachedCache(BaseCache):
+    """A cache that uses memcached as backend.
+
+    The first argument can either be a list or tuple of server addresses
+    in which case Werkzeug tries to import the memcache module and connect
+    to it, or an object that resembles the API of a :class:`memcache.Client`.
+
+    Implementation notes:  This cache backend works around some limitations in
+    memcached to simplify the interface.  For example unicode keys are encoded
+    to utf-8 on the fly.  Methods such as :meth:`~BaseCache.get_dict` return
+    the keys in the same format as passed.  Furthermore all get methods
+    silently ignore key errors to not cause problems when untrusted user data
+    is passed to the get methods which is often the case in web applications.
+
+    :param servers: a list or tuple of server addresses or alternatively
+                    a :class:`memcache.Client` or a compatible client.
+    :param default_timeout: the default timeout that is used if no timeout is
+                            specified on :meth:`~BaseCache.set`.
+    :param key_prefix: a prefix that is added before all keys.  This makes it
+                       possible to use the same memcached server for different
+                       applications.  Keep in mind that
+                       :meth:`~BaseCache.clear` will also clear keys with a
+                       different prefix.
+    """
+
+    def __init__(self, servers, default_timeout=300, key_prefix=None):
+        BaseCache.__init__(self, default_timeout)
+        if isinstance(servers, (list, tuple)):
+            try:
+                import cmemcache as memcache
+                is_cmemcache = True
+            except ImportError:
+                try:
+                    import memcache
+                    is_cmemcache = False
+                except ImportError:
+                    raise RuntimeError('no memcache module found')
+
+            # cmemcache has a bug that debuglog is not defined for the
+            # client.  Whenever pickle fails you get a weird AttributError.
+            if is_cmemcache:
+                client = memcache.Client(map(str, servers))
+                try:
+                    client.debuglog = lambda *a: None
+                except:
+                    pass
+            else:
+                client = memcache.Client(servers, False, HIGHEST_PROTOCOL)
+        else:
+            client = servers
+
+        self._client = client
+        self.key_prefix = key_prefix
+
+    def get(self, key):
+        if isinstance(key, unicode):
+            key = key.encode('utf-8')
+        if self.key_prefix:
+            key = self.key_prefix + key
+        # memcached doesn't support keys longer than that.  Because often
+        # checks for so long keys can occour because it's tested from user
+        # submitted data etc we fail silently for getting.
+        if _test_memcached_key(key):
+            return self._client.get(key)
+
+    def get_dict(self, *keys):
+        key_mapping = {}
+        have_encoded_keys = False
+        for idx, key in enumerate(keys):
+            if isinstance(key, unicode):
+                encoded_key = key.encode('utf-8')
+                have_encoded_keys = True
+            else:
+                encoded_key = key
+            if self.key_prefix:
+                encoded_key = self.key_prefix + encoded_key
+            if _test_memcached_key(key):
+                key_mapping[encoded_key] = key
+        # the keys call here is important because otherwise cmemcache
+        # does ugly things.  What exaclty I don't know, i think it does
+        # Py_DECREF but quite frankly i don't care.
+        d = rv = self._client.get_multi(key_mapping.keys())
+        if have_encoded_keys or self.key_prefix:
+            rv = {}
+            for key, value in d.iteritems():
+                rv[key_mapping[key]] = value
+        if len(rv) < len(keys):
+            for key in keys:
+                if key not in rv:
+                    rv[key] = None
+        return rv
+
+    def add(self, key, value, timeout=None):
+        if timeout is None:
+            timeout = self.default_timeout
+        if isinstance(key, unicode):
+            key = key.encode('utf-8')
+        if self.key_prefix:
+            key = self.key_prefix + key
+        self._client.add(key, value, timeout)
+
+    def set(self, key, value, timeout=None):
+        if timeout is None:
+            timeout = self.default_timeout
+        if isinstance(key, unicode):
+            key = key.encode('utf-8')
+        if self.key_prefix:
+            key = self.key_prefix + key
+        self._client.set(key, value, timeout)
+
+    def get_many(self, *keys):
+        d = self.get_dict(*keys)
+        return [d[key] for key in keys]
+
+    def set_many(self, mapping, timeout=None):
+        if timeout is None:
+            timeout = self.default_timeout
+        new_mapping = {}
+        for key, value in mapping.iteritems():
+            if isinstance(key, unicode):
+                key = key.encode('utf-8')
+            if self.key_prefix:
+                key = self.key_prefix + key
+            new_mapping[key] = value
+        self._client.set_multi(new_mapping, timeout)
+
+    def delete(self, key):
+        if isinstance(key, unicode):
+            key = key.encode('utf-8')
+        if self.key_prefix:
+            key = self.key_prefix + key
+        if _test_memcached_key(key):
+            self._client.delete(key)
+
+    def delete_many(self, *keys):
+        new_keys = []
+        for key in keys:
+            if isinstance(key, unicode):
+                key = key.encode('utf-8')
+            if self.key_prefix:
+                key = self.key_prefix + key
+            if _test_memcached_key(key):
+                new_keys.append(key)
+        self._client.delete_multi(new_keys)
+
+    def clear(self):
+        self._client.flush_all()
+
+    def inc(self, key, delta=1):
+        if isinstance(key, unicode):
+            key = key.encode('utf-8')
+        if self.key_prefix:
+            key = self.key_prefix + key
+        self._client.incr(key, delta)
+
+    def dec(self, key, delta=1):
+        if isinstance(key, unicode):
+            key = key.encode('utf-8')
+        if self.key_prefix:
+            key = self.key_prefix + key
+        self._client.decr(key, delta)
+
+
+class GAEMemcachedCache(MemcachedCache):
+    """Connects to the Google appengine memcached Cache.
+
+    :param default_timeout: the default timeout that is used if no timeout is
+                            specified on :meth:`~BaseCache.set`.
+    :param key_prefix: a prefix that is added before all keys.  This makes it
+                       possible to use the same memcached server for different
+                       applications.  Keep in mind that
+                       :meth:`~BaseCache.clear` will also clear keys with a
+                       different prefix.
+    """
+
+    def __init__(self, default_timeout=300, key_prefix=None):
+        from google.appengine.api import memcache
+        MemcachedCache.__init__(self, memcache.Client(),
+                                default_timeout, key_prefix)
+
+
+class FileSystemCache(BaseCache):
+    """A cache that stores the items on the file system.  This cache depends
+    on being the only user of the `cache_dir`.  Make absolutely sure that
+    nobody but this cache stores files there or otherwise the chace will
+    randomely delete files therein.
+
+    :param cache_dir: the directory where cached files are stored.
+    :param threshold: the maximum number of items the cache stores before
+                      it starts deleting some.
+    :param default_timeout: the default timeout that is used if no timeout is
+                            specified on :meth:`~BaseCache.set`.
+    """
+
+    def __init__(self, cache_dir, threshold=500, default_timeout=300):
+        BaseCache.__init__(self, default_timeout)
+        self._path = cache_dir
+        self._threshold = threshold
+        if not os.path.exists(self._path):
+            os.makedirs(self._path)
+
+    def _prune(self):
+        entries = os.listdir(self._path)
+        if len(entries) > self._threshold:
+            now = time()
+            for idx, key in enumerate(entries):
+                try:
+                    f = file(self._get_filename(key))
+                    if load(f) > now and idx % 3 != 0:
+                        f.close()
+                        continue
+                except:
+                    f.close()
+                self.delete(key)
+
+    def _get_filename(self, key):
+        hash = md5(key).hexdigest()
+        return os.path.join(self._path, hash)
+
+    def get(self, key):
+        filename = self._get_filename(key)
+        try:
+            f = file(filename, 'rb')
+            try:
+                if load(f) >= time():
+                    return load(f)
+            finally:
+                f.close()
+            os.remove(filename)
+        except:
+            return None
+
+    def add(self, key, value, timeout=None):
+        filename = self._get_filename(key)
+        if not os.path.exists(filename):
+            self.set(key, value, timeout)
+
+    def set(self, key, value, timeout=None):
+        if timeout is None:
+            timeout = self.default_timeout
+        filename = self._get_filename(key)
+        self._prune()
+        try:
+            f = file(filename, 'wb')
+            try:
+                dump(int(time() + timeout), f, 1)
+                dump(value, f, HIGHEST_PROTOCOL)
+            finally:
+                f.close()
+        except (IOError, OSError):
+            pass
+
+    def delete(self, key):
+        try:
+            os.remove(self._get_filename(key))
+        except (IOError, OSError):
+            pass
+
+    def clear(self):
+        for key in os.listdir(self._path):
+            self.delete(key)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/fixers.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.fixers
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    .. versionadded:: 0.5
+
+    This module includes various helpers that fix bugs in web servers.  They may
+    be necessary for some versions of a buggy web server but not others.  We try
+    to stay updated with the status of the bugs as good as possible but you have
+    to make sure whether they fix the problem you encounter.
+
+    If you notice bugs in webservers not fixed in this module consider
+    contributing a patch.
+
+    :copyright: Copyright 2009 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from urllib import unquote
+from werkzeug.http import parse_options_header, parse_cache_control_header, \
+     parse_set_header, dump_header
+from werkzeug.useragents import UserAgent
+from werkzeug.datastructures import Headers, ResponseCacheControl
+
+
+class LighttpdCGIRootFix(object):
+    """Wrap the application in this middleware if you are using lighttpd
+    with FastCGI or CGI and the application is mounted on the URL root.
+
+    :param app: the WSGI application
+    """
+
+    def __init__(self, app):
+        self.app = app
+
+    def __call__(self, environ, start_response):
+        environ['PATH_INFO'] = environ.get('SCRIPT_NAME', '') + \
+                               environ.get('PATH_INFO', '')
+        environ['SCRIPT_NAME'] = ''
+        return self.app(environ, start_response)
+
+
+class PathInfoFromRequestUriFix(object):
+    """On windows environment variables are limited to the system charset
+    which makes it impossible to store the `PATH_INFO` variable in the
+    environment without loss of information on some systems.
+
+    This is for example a problem for CGI scripts on a Windows Apache.
+
+    This fixer works by recreating the `PATH_INFO` from `REQUEST_URI`,
+    `REQUEST_URL`, or `UNENCODED_URL` (whatever is available).  Thus the
+    fix can only be applied if the webserver supports either of these
+    variables.
+
+    :param app: the WSGI application
+    """
+
+    def __init__(self, app):
+        self.app = app
+
+    def __call__(self, environ, start_response):
+        for key in 'REQUEST_URL', 'REQUEST_URI', 'UNENCODED_URL':
+            if key not in environ:
+                continue
+            request_uri = unquote(environ[key])
+            script_name = unquote(environ.get('SCRIPT_NAME', ''))
+            if request_uri.startswith(script_name):
+                environ['PATH_INFO'] = request_uri[len(script_name):] \
+                    .split('?', 1)[0]
+                break
+        return self.app(environ, start_response)
+
+
+class ProxyFix(object):
+    """This middleware can be applied to add HTTP proxy support to an
+    application that was not designed with HTTP proxies in mind.  It
+    sets `REMOTE_ADDR`, `HTTP_HOST` from `X-Forwarded` headers.
+
+    Werkzeug wrappers have builtin support for this by setting the
+    :attr:`~werkzeug.BaseRequest.is_behind_proxy` attribute to `True`.
+
+    Do not use this middleware in non-proxy setups for security reasons.
+
+    The original values of `REMOTE_ADDR` and `HTTP_HOST` are stored in
+    the WSGI environment as `werkzeug.proxy_fix.orig_remote_addr` and
+    `werkzeug.proxy_fix.orig_http_host`.
+
+    :param app: the WSGI application
+    """
+
+    def __init__(self, app):
+        self.app = app
+
+    def __call__(self, environ, start_response):
+        getter = environ.get
+        forwarded_for = getter('HTTP_X_FORWARDED_FOR', '').split(',')
+        forwarded_host = getter('HTTP_X_FORWARDED_HOST', '')
+        environ.update({
+            'werkzeug.proxy_fix.orig_remote_addr':  getter('REMOTE_ADDR'),
+            'werkzeug.proxy_fix.orig_http_host':    getter('HTTP_HOST')
+        })
+        if forwarded_for:
+            environ['REMOTE_ADDR'] = forwarded_for[0].strip()
+        if forwarded_host:
+            environ['HTTP_HOST'] = forwarded_host
+        return self.app(environ, start_response)
+
+
+class HeaderRewriterFix(object):
+    """This middleware can remove response headers and add others.  This
+    is for example useful to remove the `Date` header from responses if you
+    are using a server that adds that header, no matter if it's present or
+    not or to add `X-Powered-By` headers::
+
+        app = HeaderRewriterFix(app, remove_headers=['Date'],
+                                add_headers=[('X-Powered-By', 'WSGI')])
+
+    :param app: the WSGI application
+    :param remove_headers: a sequence of header keys that should be
+                           removed.
+    :param add_headers: a sequence of ``(key, value)`` tuples that should
+                        be added.
+    """
+
+    def __init__(self, app, remove_headers=None, add_headers=None):
+        self.app = app
+        self.remove_headers = set(x.lower() for x in (remove_headers or ()))
+        self.add_headers = list(add_headers or ())
+
+    def __call__(self, environ, start_response):
+        def rewriting_start_response(status, headers, exc_info=None):
+            new_headers = []
+            for key, value in headers:
+                if key.lower() not in self.remove_headers:
+                    new_headers.append((key, value))
+            new_headers += self.add_headers
+            return start_response(status, new_headers, exc_info)
+        return self.app(environ, rewriting_start_response)
+
+
+class InternetExplorerFix(object):
+    """This middleware fixes a couple of bugs with Microsoft Internet
+    Explorer.  Currently the following fixes are applied:
+
+    -   removing of `Vary` headers for unsupported mimetypes which
+        causes troubles with caching.  Can be disabled by passing
+        ``fix_vary=False`` to the constructor.
+        see: http://support.microsoft.com/kb/824847/en-us
+
+    -   removes offending headers to work around caching bugs in
+        Internet Explorer if `Content-Disposition` is set.  Can be
+        disabled by passing ``fix_attach=False`` to the constructor.
+
+    If it does not detect affected Internet Explorer versions it won't touch
+    the request / response.
+    """
+
+    # This code was inspired by Django fixers for the same bugs.  The
+    # fix_vary and fix_attach fixers were originally implemented in Django
+    # by Michael Axiak and is available as part of the Django project:
+    #     http://code.djangoproject.com/ticket/4148
+
+    def __init__(self, app, fix_vary=True, fix_attach=True):
+        self.app = app
+        self.fix_vary = fix_vary
+        self.fix_attach = fix_attach
+
+    def fix_headers(self, environ, headers, status=None):
+        if self.fix_vary:
+            header = headers.get('content-type', '')
+            mimetype, options = parse_options_header(header)
+            if mimetype not in ('text/html', 'text/plain', 'text/sgml'):
+                headers.pop('vary', None)
+
+        if self.fix_attach and 'content-disposition' in headers:
+            pragma = parse_set_header(headers.get('pragma', ''))
+            pragma.discard('no-cache')
+            header = pragma.to_header()
+            if not header:
+                headers.pop('pragma', '')
+            else:
+                headers['Pragma'] = header
+            header = headers.get('cache-control', '')
+            if header:
+                cc = parse_cache_control_header(header,
+                                                cls=ResponseCacheControl)
+                cc.no_cache = None
+                cc.no_store = False
+                header = cc.to_header()
+                if not header:
+                    headers.pop('cache-control', '')
+                else:
+                    headers['Cache-Control'] = header
+
+    def run_fixed(self, environ, start_response):
+        def fixing_start_response(status, headers, exc_info=None):
+            self.fix_headers(environ, Headers.linked(headers), status)
+            return start_response(status, headers, exc_info)
+        return self.app(environ, fixing_start_response)
+
+    def __call__(self, environ, start_response):
+        ua = UserAgent(environ)
+        if ua.browser != 'msie':
+            return self.app(environ, start_response)
+        return self.run_fixed(environ, start_response)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/iterio.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+r"""
+    werkzeug.contrib.iterio
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module implements a :class:`IterIO` that converts an iterator into
+    a stream object and the other way round.  Converting streams into
+    iterators requires the `greenlet`_ module.
+
+    To convert an iterator into a stream all you have to do is to pass it
+    directly to the :class:`IterIO` constructor.  In this example we pass it
+    a newly created generator::
+
+        def foo():
+            yield "something\n"
+            yield "otherthings"
+        stream = IterIO(foo())
+        print stream.read()         # read the whole iterator
+
+    The other way round works a bit different because we have to ensure that
+    the code execution doesn't take place yet.  An :class:`IterIO` call with a
+    callable as first argument does two things.  The function itself is passed
+    an :class:`IterIO` stream it can feed.  The object returned by the
+    :class:`IterIO` constructor on the other hand is not an stream object but
+    an iterator::
+
+        def foo(stream):
+            stream.write("some")
+            stream.write("thing")
+            stream.flush()
+            stream.write("otherthing")
+        iterator = IterIO(foo)
+        print iterator.next()       # prints something
+        print iterator.next()       # prints otherthing
+        iterator.next()             # raises StopIteration
+
+    .. _greenlet: http://codespeak.net/py/dist/greenlet.html
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+try:
+    from py.magic import greenlet
+except:
+    greenlet = None
+
+
+class IterIO(object):
+    """Instances of this object implement an interface compatible with the
+    standard Python :class:`file` object.  Streams are either read-only or
+    write-only depending on how the object is created.
+    """
+
+    def __new__(cls, obj):
+        try:
+            iterator = iter(obj)
+        except TypeError:
+            return IterI(obj)
+        return IterO(iterator)
+
+    def __iter__(self):
+        return self
+
+    def tell(self):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        return self.pos
+
+    def isatty(self):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        return False
+
+    def seek(self, pos, mode=0):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def truncate(self, size=None):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def write(self, s):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def writelines(self, list):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def read(self, n=-1):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def readlines(self, sizehint=0):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def readline(self, length=None):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def flush(self):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        raise IOError(9, 'Bad file descriptor')
+
+    def next(self):
+        if self.closed:
+            raise StopIteration()
+        line = self.readline()
+        if not line:
+            raise StopIteration()
+        return line
+
+
+class IterI(IterIO):
+    """Convert an stream into an iterator."""
+
+    def __new__(cls, func):
+        if greenlet is None:
+            raise RuntimeError('IterI requires greenlet support')
+        stream = object.__new__(cls)
+        stream.__init__(greenlet.getcurrent())
+
+        def run():
+            func(stream)
+            stream.flush()
+
+        g = greenlet(run, stream._parent)
+        while 1:
+            rv = g.switch()
+            if not rv:
+                return
+            yield rv[0]
+
+    def __init__(self, parent):
+        self._parent = parent
+        self._buffer = []
+        self.closed = False
+        self.pos = 0
+
+    def close(self):
+        if not self.closed:
+            self.closed = True
+
+    def write(self, s):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        self.pos += len(s)
+        self._buffer.append(s)
+
+    def writelines(self, list):
+        self.write(''.join(list))
+
+    def flush(self):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        data = ''.join(self._buffer)
+        self._buffer = []
+        self._parent.switch((data,))
+
+
+class IterO(IterIO):
+    """Iter output.  Wrap an iterator and give it a stream like interface."""
+
+    def __new__(cls, gen):
+        return object.__new__(cls)
+
+    def __init__(self, gen):
+        self._gen = gen
+        self._buf = ''
+        self.closed = False
+        self.pos = 0
+
+    def __iter__(self):
+        return self
+
+    def close(self):
+        if not self.closed:
+            self.closed = True
+            if hasattr(self._gen, 'close'):
+                self._gen.close()
+
+    def seek(self, pos, mode=0):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        if mode == 1:
+            pos += self.pos
+        elif mode == 2:
+            self.read()
+            self.pos = min(self.pos, self.pos + pos)
+            return
+        elif mode != 0:
+            raise IOError('Invalid argument')
+        buf = []
+        try:
+            tmp_end_pos = len(self._buf)
+            while pos > tmp_end_pos:
+                item = self._gen.next()
+                tmp_end_pos += len(item)
+                buf.append(item)
+        except StopIteration:
+            pass
+        if buf:
+            self._buf += ''.join(buf)
+        self.pos = max(0, pos)
+
+    def read(self, n=-1):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        if n < 0:
+            self._buf += ''.join(self._gen)
+            result = self._buf[self.pos:]
+            self.pos += len(result)
+            return result
+        new_pos = self.pos + n
+        buf = []
+        try:
+            tmp_end_pos = len(self._buf)
+            while new_pos > tmp_end_pos:
+                item = self._gen.next()
+                tmp_end_pos += len(item)
+                buf.append(item)
+        except StopIteration:
+            pass
+        if buf:
+            self._buf += ''.join(buf)
+        new_pos = max(0, new_pos)
+        try:
+            return self._buf[self.pos:new_pos]
+        finally:
+            self.pos = min(new_pos, len(self._buf))
+
+    def readline(self, length=None):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        nl_pos = self._buf.find('\n', self.pos)
+        buf = []
+        try:
+            pos = self.pos
+            while nl_pos < 0:
+                item = self._gen.next()
+                local_pos = item.find('\n')
+                buf.append(item)
+                if local_pos >= 0:
+                    nl_pos = pos + local_pos
+                    break
+                pos += len(item)
+        except StopIteration:
+            pass
+        if buf:
+            self._buf += ''.join(buf)
+        if nl_pos < 0:
+            new_pos = len(self._buf)
+        else:
+            new_pos = nl_pos + 1
+        if length is not None and self.pos + length < new_pos:
+            new_pos = self.pos + length
+        try:
+            return self._buf[self.pos:new_pos]
+        finally:
+            self.pos = min(new_pos, len(self._buf))
+
+    def readlines(self, sizehint=0):
+        total = 0
+        lines = []
+        line = self.readline()
+        while line:
+            lines.append(line)
+            total += len(line)
+            if 0 < sizehint <= total:
+                break
+            line = self.readline()
+        return lines
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/jsrouting.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.jsrouting
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Addon module that allows to create a JavaScript function from a map
+    that generates rules.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+try:
+    from simplejson import dumps
+except ImportError:
+    def dumps(*args):
+        raise RuntimeError('simplejson required for jsrouting')
+
+from inspect import getmro
+from werkzeug.templates import Template
+from werkzeug.routing import NumberConverter
+
+
+_javascript_routing_template = Template(u'''\
+<% if name_parts %>\
+<% for idx in xrange(0, len(name_parts) - 1) %>\
+if (typeof ${'.'.join(name_parts[:idx + 1])} === 'undefined') \
+${'.'.join(name_parts[:idx + 1])} = {};
+<% endfor %>\
+${'.'.join(name_parts)} = <% endif %>\
+(function (server_name, script_name, subdomain, url_scheme) {
+    var converters = ${', '.join(converters)};
+    var rules = $rules;
+    function in_array(array, value) {
+        if (array.indexOf != undefined) {
+            return array.indexOf(value) != -1;
+        }
+        for (var i = 0; i < array.length; i++) {
+            if (array[i] == value) {
+                return true;
+            }
+        }
+        return false;
+    }
+    function array_diff(array1, array2) {
+        array1 = array1.slice();
+        for (var i = array1.length-1; i >= 0; i--) {
+            if (in_array(array2, array1[i])) {
+                array1.splice(i, 1);
+            }
+        }
+        return array1;
+    }
+    function split_obj(obj) {
+        var names = [];
+        var values = [];
+        for (var name in obj) {
+            if (typeof(obj[name]) != 'function') {
+                names.push(name);
+                values.push(obj[name]);
+            }
+        }
+        return {names: names, values: values, original: obj};
+    }
+    function suitable(rule, args) {
+        var default_args = split_obj(rule.defaults || {});
+        var diff_arg_names = array_diff(rule.arguments, default_args.names);
+
+        for (var i = 0; i < diff_arg_names.length; i++) {
+            if (!in_array(args.names, diff_arg_names[i])) {
+                return false;
+            }
+        }
+
+        if (array_diff(rule.arguments, args.names).length == 0) {
+            if (rule.defaults == null) {
+                return true;
+            }
+            for (var i = 0; i < default_args.names.length; i++) {
+                var key = default_args.names[i];
+                var value = default_args.values[i];
+                if (value != args.original[key]) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+    function build(rule, args) {
+        var tmp = [];
+        var processed = rule.arguments.slice();
+        for (var i = 0; i < rule.trace.length; i++) {
+            var part = rule.trace[i];
+            if (part.is_dynamic) {
+                var converter = converters[rule.converters[part.data]];
+                var data = converter(args.original[part.data]);
+                if (data == null) {
+                    return null;
+                }
+                tmp.push(data);
+                processed.push(part.name);
+            } else {
+                tmp.push(part.data);
+            }
+        }
+        tmp = tmp.join('');
+        var pipe = tmp.indexOf('|');
+        var subdomain = tmp.substring(0, pipe);
+        var url = tmp.substring(pipe+1);
+
+        var unprocessed = array_diff(args.names, processed);
+        var first_query_var = true;
+        for (var i = 0; i < unprocessed.length; i++) {
+            if (first_query_var) {
+                url += '?';
+            } else {
+                url += '&';
+            }
+            first_query_var = false;
+            url += encodeURIComponent(unprocessed[i]);
+            url += '=';
+            url += encodeURIComponent(args.original[unprocessed[i]]);
+        }
+        return {subdomain: subdomain, path: url};
+    }
+    function lstrip(s, c) {
+        while (s && s.substring(0, 1) == c) {
+            s = s.substring(1);
+        }
+        return s;
+    }
+    function rstrip(s, c) {
+        while (s && s.substring(s.length-1, s.length) == c) {
+            s = s.substring(0, s.length-1);
+        }
+        return s;
+    }
+    return function(endpoint, args, force_external) {
+        args = split_obj(args);
+        var rv = null;
+        for (var i = 0; i < rules.length; i++) {
+            var rule = rules[i];
+            if (rule.endpoint != endpoint) continue;
+            if (suitable(rule, args)) {
+                rv = build(rule, args);
+                if (rv != null) {
+                    break;
+                }
+            }
+        }
+        if (rv == null) {
+            return null;
+        }
+        if (!force_external && rv.subdomain == subdomain) {
+            return rstrip(script_name, '/') + '/' + lstrip(rv.path, '/');
+        } else {
+            return url_scheme + '://'
+                   + (rv.subdomain ? rv.subdomain + '.' : '')
+                   + server_name + rstrip(script_name, '/')
+                   + '/' + lstrip(rv.path, '/');
+        }
+    };
+})''')
+
+
+def generate_map(map, name='url_map'):
+    """
+    Generates a JavaScript function containing the rules defined in
+    this map, to be used with a MapAdapter's generate_javascript
+    method.  If you don't pass a name the returned JavaScript code is
+    an expression that returns a function.  Otherwise it's a standalone
+    script that assigns the function with that name.  Dotted names are
+    resolved (so you an use a name like 'obj.url_for')
+
+    In order to use JavaScript generation, simplejson must be installed.
+
+    Note that using this feature will expose the rules
+    defined in your map to users. If your rules contain sensitive
+    information, don't use JavaScript generation!
+    """
+    map.update()
+    rules = []
+    converters = []
+    for rule in map.iter_rules():
+        trace = [{
+            'is_dynamic':   is_dynamic,
+            'data':         data
+        } for is_dynamic, data in rule._trace]
+        rule_converters = {}
+        for key, converter in rule._converters.iteritems():
+            js_func = js_to_url_function(converter)
+            try:
+                index = converters.index(js_func)
+            except ValueError:
+                converters.append(js_func)
+                index = len(converters) - 1
+            rule_converters[key] = index
+        rules.append({
+            u'endpoint':    rule.endpoint,
+            u'arguments':   list(rule.arguments),
+            u'converters':  rule_converters,
+            u'trace':       trace,
+            u'defaults':    rule.defaults
+        })
+
+    return _javascript_routing_template.render({
+        'name_parts':   name and name.split('.') or [],
+        'rules':        dumps(rules),
+        'converters':   converters
+    })
+
+
+def generate_adapter(adapter, name='url_for', map_name='url_map'):
+    """Generates the url building function for a map."""
+    values = {
+        u'server_name':     dumps(adapter.server_name),
+        u'script_name':     dumps(adapter.script_name),
+        u'subdomain':       dumps(adapter.subdomain),
+        u'url_scheme':      dumps(adapter.url_scheme),
+        u'name':            name,
+        u'map_name':        map_name
+    }
+    return u'''\
+var %(name)s = %(map_name)s(
+    %(server_name)s,
+    %(script_name)s,
+    %(subdomain)s,
+    %(url_scheme)s
+);''' % values
+
+
+def js_to_url_function(converter):
+    """Get the JavaScript converter function from a rule."""
+    if hasattr(converter, 'js_to_url_function'):
+        data = converter.js_to_url_function()
+    else:
+        for cls in getmro(type(converter)):
+            if cls in js_to_url_functions:
+                data = js_to_url_functions[cls](converter)
+                break
+        else:
+            return 'encodeURIComponent'
+    return '(function(value) { %s })' % data
+
+
+def NumberConverter_js_to_url(conv):
+    if conv.fixed_digits:
+        return u'''\
+var result = value.toString();
+while (result.length < %s)
+    result = '0' + result;
+return result;''' % conv.fixed_digits
+    return u'return value.toString();'
+
+
+js_to_url_functions = {
+    NumberConverter:    NumberConverter_js_to_url
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/kickstart.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,284 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.kickstart
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module provides some simple shortcuts to make using Werkzeug simpler
+    for small scripts.
+
+    These improvements include predefined `Request` and `Response` objects as
+    well as a predefined `Application` object which can be customized in child
+    classes, of course.  The `Request` and `Reponse` objects handle URL
+    generation as well as sessions via `werkzeug.contrib.sessions` and are
+    purely optional.
+
+    There is also some integration of template engines.  The template loaders
+    are, of course, not neccessary to use the template engines in Werkzeug,
+    but they provide a common interface.  Currently supported template engines
+    include Werkzeug's minitmpl and Genshi_.  Support for other engines can be
+    added in a trivial way.  These loaders provide a template interface
+    similar to the one used by Django_.
+
+    .. _Genshi: http://genshi.edgewall.org/
+    .. _Django: http://www.djangoproject.com/
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from os import path
+from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
+from werkzeug.templates import Template
+from werkzeug.exceptions import HTTPException
+from werkzeug.routing import RequestRedirect
+
+__all__ = ['Request', 'Response', 'TemplateNotFound', 'TemplateLoader',
+           'GenshiTemplateLoader', 'Application']
+
+
+class Request(RequestBase):
+    """A handy subclass of the base request that adds a URL builder.
+    It when supplied a session store, it is also able to handle sessions.
+    """
+
+    def __init__(self, environ, url_map,
+            session_store=None, cookie_name=None):
+        # call the parent for initialization
+        RequestBase.__init__(self, environ)
+        # create an adapter
+        self.url_adapter = url_map.bind_to_environ(environ)
+        # create all stuff for sessions
+        self.session_store = session_store
+        self.cookie_name = cookie_name
+
+        if session_store is not None and cookie_name is not None:
+            if cookie_name in self.cookies:
+                # get the session out of the storage
+                self.session = session_store.get(self.cookies[cookie_name])
+            else:
+                # create a new session
+                self.session = session_store.new()
+
+    def url_for(self, callback, **values):
+        return self.url_adapter.build(callback, values)
+
+
+class Response(ResponseBase):
+    """
+    A subclass of base response which sets the default mimetype to text/html.
+    It the `Request` that came in is using Werkzeug sessions, this class
+    takes care of saving that session.
+    """
+    default_mimetype = 'text/html'
+
+    def __call__(self, environ, start_response):
+        # get the request object
+        request = environ['werkzeug.request']
+
+        if request.session_store is not None:
+            # save the session if neccessary
+            request.session_store.save_if_modified(request.session)
+
+            # set the cookie for the browser if it is not there:
+            if request.cookie_name not in request.cookies:
+                self.set_cookie(request.cookie_name, request.session.sid)
+
+        # go on with normal response business
+        return ResponseBase.__call__(self, environ, start_response)
+
+
+class Processor(object):
+    """A request and response processor - it is what Django calls a
+    middleware, but Werkzeug also includes straight-foward support for real
+    WSGI middlewares, so another name was chosen.
+
+    The code of this processor is derived from the example in the Werkzeug
+    trac, called `Request and Response Processor
+    <http://dev.pocoo.org/projects/werkzeug/wiki/RequestResponseProcessor>`_
+    """
+
+    def process_request(self, request):
+        return request
+
+    def process_response(self, request, response):
+        return response
+
+    def process_view(self, request, view_func, view_args, view_kwargs):
+        """process_view() is called just before the Application calls the
+        function specified by view_func.
+
+        If this returns None, the Application processes the next Processor,
+        and if it returns something else (like a Response instance), that
+        will be returned without any further processing.
+        """
+        return None
+
+    def process_exception(self, request, exception):
+        return None
+
+
+class Application(object):
+    """A generic WSGI application which can be used to start with Werkzeug in
+    an easy, straightforward way.
+    """
+
+    def __init__(self, name, url_map, session=False, processors=None):
+        # save the name and the URL-map, as it'll be needed later on
+        self.name = name
+        self.url_map = url_map
+        # save the list of processors if supplied
+        self.processors = processors or []
+        # create an instance of the storage
+        if session:
+            self.store = session
+        else:
+            self.store = None
+
+    def __call__(self, environ, start_response):
+        # create a request - with or without session support
+        if self.store is not None:
+            request = Request(environ, self.url_map,
+                session_store=self.store, cookie_name='%s_sid' % self.name)
+        else:
+            request = Request(environ, self.url_map)
+
+        # apply the request processors
+        for processor in self.processors:
+            request = processor.process_request(request)
+
+        try:
+            # find the callback to which the URL is mapped
+            callback, args = request.url_adapter.match(request.path)
+        except (HTTPException, RequestRedirect), e:
+            response = e
+        else:
+            # check all view processors
+            for processor in self.processors:
+                action = processor.process_view(request, callback, (), args)
+                if action is not None:
+                    # it is overriding the default behaviour, this is
+                    # short-circuiting the processing, so it returns here
+                    return action(environ, start_response)
+
+            try:
+                response = callback(request, **args)
+            except Exception, exception:
+                # the callback raised some exception, need to process that
+                for processor in reversed(self.processors):
+                    # filter it through the exception processor
+                    action = processor.process_exception(request, exception)
+                    if action is not None:
+                        # the exception processor returned some action
+                        return action(environ, start_response)
+                # still not handled by a exception processor, so re-raise
+                raise
+
+        # apply the response processors
+        for processor in reversed(self.processors):
+            response = processor.process_response(request, response)
+
+        # return the completely processed response
+        return response(environ, start_response)
+
+
+    def config_session(self, store, expiration='session'):
+        """
+        Configures the setting for cookies. You can also disable cookies by
+        setting store to None.
+        """
+        self.store = store
+        # expiration=session is the default anyway
+        # TODO: add settings to define the expiration date, the domain, the
+        # path any maybe the secure parameter.
+
+
+class TemplateNotFound(IOError, LookupError):
+    """
+    A template was not found by the template loader.
+    """
+
+    def __init__(self, name):
+        IOError.__init__(self, name)
+        self.name = name
+
+
+class TemplateLoader(object):
+    """
+    A simple loader interface for the werkzeug minitmpl
+    template language.
+    """
+
+    def __init__(self, search_path, encoding='utf-8'):
+        self.search_path = path.abspath(search_path)
+        self.encoding = encoding
+
+    def get_template(self, name):
+        """Get a template from a given name."""
+        filename = path.join(self.search_path, *[p for p in name.split('/')
+                                                 if p and p[0] != '.'])
+        if not path.exists(filename):
+            raise TemplateNotFound(name)
+        return Template.from_file(filename, self.encoding)
+
+    def render_to_response(self, *args, **kwargs):
+        """Load and render a template into a response object."""
+        return Response(self.render_to_string(*args, **kwargs))
+
+    def render_to_string(self, *args, **kwargs):
+        """Load and render a template into a unicode string."""
+        try:
+            template_name, args = args[0], args[1:]
+        except IndexError:
+            raise TypeError('name of template required')
+        return self.get_template(template_name).render(*args, **kwargs)
+
+
+class GenshiTemplateLoader(TemplateLoader):
+    """A unified interface for loading Genshi templates. Actually a quite thin
+    wrapper for Genshi's TemplateLoader.
+
+    It sets some defaults that differ from the Genshi loader, most notably
+    auto_reload is active. All imporant options can be passed through to
+    Genshi.
+    The default output type is 'html', but can be adjusted easily by changing
+    the `output_type` attribute.
+    """
+    def __init__(self, search_path, encoding='utf-8', **kwargs):
+        TemplateLoader.__init__(self, search_path, encoding)
+        # import Genshi here, because we don't want a general Genshi
+        # dependency, only a local one
+        from genshi.template import TemplateLoader as GenshiLoader
+        from genshi.template.loader import TemplateNotFound
+
+        self.not_found_exception = TemplateNotFound
+        # set auto_reload to True per default
+        reload_template = kwargs.pop('auto_reload', True)
+        # get rid of default_encoding as this template loaders overwrites it
+        # with the value of encoding
+        kwargs.pop('default_encoding', None)
+
+        # now, all arguments are clean, pass them on
+        self.loader = GenshiLoader(search_path, default_encoding=encoding,
+                auto_reload=reload_template, **kwargs)
+
+        # the default output is HTML but can be overridden easily
+        self.output_type = 'html'
+        self.encoding = encoding
+
+    def get_template(self, template_name):
+        """Get the template which is at the given name"""
+        try:
+            return self.loader.load(template_name, encoding=self.encoding)
+        except self.not_found_exception, e:
+            # catch the exception raised by Genshi, convert it into a werkzeug
+            # exception (for the sake of consistency)
+            raise TemplateNotFound(template_name)
+
+    def render_to_string(self, template_name, context=None):
+        """Load and render a template into an unicode string"""
+        # create an empty context if no context was specified
+        context = context or {}
+        tmpl = self.get_template(template_name)
+        # render the template into a unicode string (None means unicode)
+        return tmpl. \
+            generate(**context). \
+            render(self.output_type, encoding=None)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/limiter.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.limiter
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    A middleware that limits incoming data.  This works around problems with
+    Trac_ or Django_ because those directly stream into the memory.
+
+    .. _Trac: http://trac.edgewall.org/
+    .. _Django: http://www.djangoproject.com/
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from warnings import warn
+
+from werkzeug import LimitedStream
+
+
+class StreamLimitMiddleware(object):
+    """Limits the input stream to a given number of bytes.  This is useful if
+    you have a WSGI application that reads form data into memory (django for
+    example) and you don't want users to harm the server by uploading tons of
+    data.
+
+    Default is 10MB
+    """
+
+    def __init__(self, app, maximum_size=1024 * 1024 * 10):
+        self.app = app
+        self.maximum_size = maximum_size
+
+    def __call__(self, environ, start_response):
+        limit = min(self.maximum_size, int(environ.get('CONTENT_LENGTH') or 0))
+        environ['wsgi.input'] = LimitedStream(environ['wsgi.input'], limit)
+        return self.app(environ, start_response)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/lint.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,331 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.lint
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    .. versionadded:: 0.5
+
+    This module provides a middleware that performs sanity checks of the WSGI
+    application.  It checks that :pep:`333` is properly implemented and warns
+    on some common HTTP errors such as non-empty responses for 304 status
+    codes.
+
+    This module provides a middleware, the :class:`LintMiddleware`.  Wrap your
+    application with it and it will warn about common problems with WSGI and
+    HTTP while your application is running.
+
+    It's strongly recommended to use it during development.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from urlparse import urlparse
+from warnings import warn
+
+from werkzeug import Headers, FileWrapper, is_entity_header
+
+
+class WSGIWarning(Warning):
+    """Warning class for WSGI warnings."""
+
+
+class HTTPWarning(Warning):
+    """Warning class for HTTP warnings."""
+
+
+def check_string(context, obj, stacklevel=3):
+    if type(obj) is not str:
+        warn(WSGIWarning('%s requires bytestrings, got %s' %
+            (context, obj.__class__.__name__)))
+
+
+class InputStream(object):
+
+    def __init__(self, stream):
+        self._stream = stream
+
+    def read(self, *args):
+        if len(args) == 0:
+            warn(WSGIWarning('wsgi does not guarantee an EOF marker on the '
+                             'input stream, thus making calls to '
+                             'wsgi.input.read() unsafe.  Conforming servers '
+                             'may never return from this call.'),
+                 stacklevel=2)
+        elif len(args) != 1:
+            warn(WSGIWarning('too many parameters passed to wsgi.input.read()'),
+                 stacklevel=2)
+        return self._stream.read(*args)
+
+    def readline(self, *args):
+        if len(args) == 0:
+            warn(WSGIWarning('Calls to wsgi.input.readline() without arguments'
+                             ' are unsafe.  Use wsgi.input.read() instead.'),
+                 stacklevel=2)
+        elif len(args) == 1:
+            warn(WSGIWarning('wsgi.input.readline() was called with a size hint. '
+                             'WSGI does not support this, although it\'s available '
+                             'on all major servers.'),
+                 stacklevel=2)
+        else:
+            raise TypeError('too many arguments passed to wsgi.input.readline()')
+        return self._stream.readline(*args)
+
+    def __iter__(self):
+        try:
+            return iter(self._stream)
+        except TypeError:
+            warn(WSGIWarning('wsgi.input is not iterable.'), stacklevel=2)
+            return iter(())
+
+    def close(self):
+        warn(WSGIWarning('application closed the input stream!'),
+             stacklevel=2)
+        self._stream.close()
+
+
+class ErrorStream(object):
+
+    def __init__(self, stream):
+        self._stream = stream
+
+    def write(self, s):
+        check_string('wsgi.error.write()', s)
+        self._stream.write(s)
+
+    def flush(self):
+        self._stream.flush()
+
+    def writelines(self, seq):
+        for line in seq:
+            self.write(seq)
+
+    def close(self):
+        warn(WSGIWarning('application closed the error stream!'),
+             stacklevel=2)
+        self._stream.close()
+
+
+class GuardedWrite(object):
+
+    def __init__(self, write, chunks):
+        self._write = write
+        self._chunks = chunks
+
+    def __call__(self, s):
+        check_string('write()', s)
+        self._write.write(s)
+        self._chunks.append(len(s))
+
+
+class GuardedIterator(object):
+
+    def __init__(self, iterator, headers_set, chunks):
+        self._iterator = iterator
+        self._next = iter(iterator).next
+        self.closed = False
+        self.headers_set = headers_set
+        self.chunks = chunks
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        if self.closed:
+            warn(WSGIWarning('iterated over closed app_iter'),
+                 stacklevel=2)
+        rv = self._next()
+        if not self.headers_set:
+            warn(WSGIWarning('Application returned before it '
+                             'started the response'), stacklevel=2)
+        check_string('application iterator items', rv)
+        self.chunks.append(len(rv))
+        return rv
+
+    def close(self):
+        self.closed = True
+        if hasattr(self._iterator, 'close'):
+            self._iterator.close()
+
+        if self.headers_set:
+            status_code, headers = self.headers_set
+            bytes_sent = sum(self.chunks)
+            content_length = headers.get('content-length', type=int)
+
+            if status_code == 304:
+                for key, value in headers:
+                    key = key.lower()
+                    if key not in ('expires', 'content-location') and \
+                       is_entity_header(key):
+                        warn(HTTPWarning('entity header %r found in 304 '
+                            'response' % key))
+                if bytes_sent:
+                    warn(HTTPWarning('304 responses must not have a body'))
+            elif 100 <= status_code < 200 or status_code == 204:
+                if content_length != 0:
+                    warn(HTTPWarning('%r responses must have an empty '
+                                     'content length') % status_code)
+                if bytes_sent:
+                    warn(HTTPWarning('%r responses must not have a body' %
+                                     status_code))
+            elif content_length is not None and content_length != bytes_sent:
+                warn(WSGIWarning('Content-Length and the number of bytes '
+                                 'sent to the client do not match.'))
+
+    def __del__(self):
+        if not self.closed:
+            try:
+                warn(WSGIWarning('Iterator was garbage collected before '
+                                 'it was closed.'))
+            except:
+                pass
+
+
+class LintMiddleware(object):
+    """This middleware wraps an application and warns on common errors.
+    Among other thing it currently checks for the following problems:
+
+    -   invalid status codes
+    -   non-bytestrings sent to the WSGI server
+    -   strings returned from the WSGI application
+    -   non-empty conditional responses
+    -   unquoted etags
+    -   relative URLs in the Location header
+    -   unsafe calls to wsgi.input
+    -   unclosed iterators
+
+    Detected errors are emitted using the standard Python :mod:`warnings`
+    system and usually end up on :data:`stderr`.
+
+    ::
+
+        from werkzeug.contrib.lint import LintMiddleware
+        app = LintMiddleware(app)
+
+    :param app: the application to wrap
+    """
+
+    def __init__(self, app):
+        self.app = app
+
+    def check_environ(self, environ):
+        if type(environ) is not dict:
+            warn(WSGIWarning('WSGI environment is not a standard python dict.'),
+                 stacklevel=4)
+        for key in ('REQUEST_METHOD', 'SERVER_NAME', 'SERVER_PORT',
+                    'wsgi.version', 'wsgi.input', 'wsgi.errors',
+                    'wsgi.multithread', 'wsgi.multiprocess',
+                    'wsgi.run_once'):
+            if key not in environ:
+                warn(WSGIWarning('required environment key %r not found'
+                     % key), stacklevel=3)
+        if environ['wsgi.version'] != (1, 0):
+            warn(WSGIWarning('environ is not a WSGI 1.0 environ'),
+                 stacklevel=3)
+
+        script_name = environ.get('SCRIPT_NAME', '')
+        if script_name and script_name[:1] != '/':
+            warn(WSGIWarning('SCRIPT_NAME does not start with a slash: %r'
+                             % script_name), stacklevel=3)
+        path_info = environ.get('PATH_INFO', '')
+        if path_info[:1] != '/':
+            warn(WSGIWarning('PATH_INFO does not start with a slash: %r'
+                             % path_info), stacklevel=3)
+
+
+    def check_start_response(self, status, headers, exc_info):
+        check_string('status', status)
+        status_code = status.split(None, 1)[0]
+        if len(status_code) != 3 or not status_code.isdigit():
+            warn(WSGIWarning('Status code must be three digits'), stacklevel=3)
+        if len(status) < 4 or status[3] != ' ':
+            warn(WSGIWarning('Invalid value for status %r.  Valid '
+                             'status strings are three digits, a space '
+                             'and a status explanation'), stacklevel=3)
+        status_code = int(status_code)
+        if status_code < 100:
+            warn(WSGIWarning('status code < 100 detected'), stacklevel=3)
+
+        if type(headers) is not list:
+            warn(WSGIWarning('header list is not a list'), stacklevel=3)
+        for item in headers:
+            if type(item) is not tuple or len(item) != 2:
+                warn(WSGIWarning('Headers must tuple 2-item tuples'),
+                     stacklevel=3)
+            name, value = item
+            if type(name) is not str or type(value) is not str:
+                warn(WSGIWarning('header items must be strings'),
+                     stacklevel=3)
+            if name.lower() == 'status':
+                warn(WSGIWarning('The status header is not supported due to '
+                                 'conflicts with the CGI spec.'),
+                                 stacklevel=3)
+
+        if exc_info is not None and not isinstance(exc_info, tuple):
+            warn(WSGIWarning('invalid value for exc_info'), stacklevel=3)
+
+        headers = Headers(headers)
+        self.check_headers(headers)
+
+        return status_code, headers
+
+    def check_headers(self, headers):
+        etag = headers.get('etag')
+        if etag is not None:
+            if etag.startswith('w/'):
+                etag = etag[2:]
+            if not (etag[:1] == etag[-1:] == '"'):
+                warn(HTTPWarning('unquoted etag emitted.'), stacklevel=4)
+
+        location = headers.get('location')
+        if location is not None:
+            if not urlparse(location).netloc:
+                warn(HTTPWarning('absolute URLs required for location header'),
+                     stacklevel=4)
+
+    def check_iterator(self, app_iter):
+        if isinstance(app_iter, basestring):
+            warn(WSGIWarning('application returned string.  Response will '
+                             'send character for character to the client '
+                             'which will kill the performance.  Return a '
+                             'list or iterable instead.'), stacklevel=3)
+
+    def __call__(self, *args, **kwargs):
+        if len(args) != 2:
+            warn(WSGIWarning('Two arguments to WSGI app required'), stacklevel=2)
+        if kwargs:
+            warn(WSGIWarning('No keyword arguments to WSGI app allowed'),
+                 stacklevel=2)
+        environ, start_response = args
+
+        self.check_environ(environ)
+        environ['wsgi.input'] = InputStream(environ['wsgi.input'])
+        environ['wsgi.errors'] = ErrorStream(environ['wsgi.errors'])
+
+        # hook our own file wrapper in so that applications will always
+        # iterate to the end and we can check the content length
+        environ['wsgi.file_wrapper'] = FileWrapper
+
+        headers_set = []
+        chunks = []
+
+        def checking_start_response(*args, **kwargs):
+            if len(args) not in (2, 3):
+                warn(WSGIWarning('Invalid number of arguments: %s, expected '
+                     '2 or 3' % len(args), stacklevel=2))
+            if kwargs:
+                warn(WSGIWarning('no keyword arguments allowed.'))
+
+            status, headers = args[:2]
+            if len(args) == 3:
+                exc_info = args[2]
+            else:
+                exc_info = None
+
+            headers_set[:] = self.check_start_response(status, headers,
+                                                       exc_info)
+            return GuardedWrite(start_response(status, headers, exc_info),
+                                chunks)
+
+        app_iter = self.app(environ, checking_start_response)
+        self.check_iterator(app_iter)
+        return GuardedIterator(app_iter, headers_set, chunks)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/profiler.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.profiler
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module provides a simple WSGI profiler middleware for finding
+    bottlenecks in web application.  It uses the :mod:`profile` or
+    :mod:`cProfile` module to do the profiling and writes the stats to the
+    stream provided (defaults to stderr).
+
+    Example usage::
+
+        from werkzeug.contrib.profiler import ProfilerMiddleware
+        app = ProfilerMiddleware(app)
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import sys
+try:
+    try:
+        from cProfile import Profile
+    except ImportError:
+        from profile import Profile
+    from pstats import Stats
+    available = True
+except ImportError:
+    available = False
+
+
+class MergeStream(object):
+    """An object that redirects `write` calls to multiple streams.
+    Use this to log to both `sys.stdout` and a file::
+
+        f = open('profiler.log', 'w')
+        stream = MergeStream(sys.stdout, f)
+        profiler = ProfilerMiddleware(app, stream)
+    """
+
+    def __init__(self, *streams):
+        if not streams:
+            raise TypeError('at least one stream must be given')
+        self.streams = streams
+
+    def write(self, data):
+        for stream in self.streams:
+            stream.write(data)
+
+
+class ProfilerMiddleware(object):
+    """Simple profiler middleware.  Wraps a WSGI application and profiles
+    a request.  This intentionally buffers the response so that timings are
+    more exact.
+
+    For the exact meaning of `sort_by` and `restrictions` consult the
+    :mod:`profile` documentation.
+
+    :param app: the WSGI application to profile.
+    :param stream: the stream for the profiled stats.  defaults to stderr.
+    :param sort_by: a tuple of columns to sort the result by.
+    :param restrictions: a tuple of profiling strictions.
+    """
+
+    def __init__(self, app, stream=None,
+                 sort_by=('time', 'calls'), restrictions=()):
+        if not available:
+            raise RuntimeError('the profiler is not available because '
+                               'profile or pstat is not installed.')
+        self._app = app
+        self._stream = stream or sys.stdout
+        self._sort_by = sort_by
+        self._restrictions = restrictions
+
+    def __call__(self, environ, start_response):
+        response_body = []
+
+        def catching_start_response(status, headers, exc_info=None):
+            start_response(status, headers, exc_info)
+            return response_body.append
+
+        def runapp():
+            appiter = self._app(environ, catching_start_response)
+            response_body.extend(appiter)
+            if hasattr(appiter, 'close'):
+                appiter.close()
+
+        p = Profile()
+        p.runcall(runapp)
+        body = ''.join(response_body)
+        stats = Stats(p, stream=self._stream)
+        stats.sort_stats(*self._sort_by)
+
+        self._stream.write('-' * 80)
+        self._stream.write('\nPATH: %r\n' % environ.get('PATH_INFO'))
+        stats.print_stats(*self._restrictions)
+        self._stream.write('-' * 80 + '\n\n')
+
+        return [body]
+
+
+def make_action(app_factory, hostname='localhost', port=5000,
+                threaded=False, processes=1, stream=None,
+                sort_by=('time', 'calls'), restrictions=()):
+    """Return a new callback for :mod:`werkzeug.script` that starts a local
+    server with the profiler enabled::
+
+        from werkzeug.contrib import profiler
+        action_profile = profiler.make_action(make_app)
+    """
+    def action(hostname=('h', hostname), port=('p', port),
+               threaded=threaded, processes=processes):
+        """Start a new development server."""
+        from werkzeug.serving import run_simple
+        app = ProfilerMiddleware(app_factory(), stream, sort_by, restrictions)
+        run_simple(hostname, port, app, False, None, threaded, processes)
+    return action
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/securecookie.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,328 @@
+# -*- coding: utf-8 -*-
+r"""
+    werkzeug.contrib.securecookie
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module implements a cookie that is not alterable from the client
+    because it adds a checksum the server checks for.  You can use it as
+    session replacement if all you have is a user id or something to mark
+    a logged in user.
+
+    Keep in mind that the data is still readable from the client as a
+    normal cookie is.  However you don't have to store and flush the
+    sessions you have at the server.
+
+    Example usage:
+
+    >>> from werkzeug.contrib.securecookie import SecureCookie
+    >>> x = SecureCookie({"foo": 42, "baz": (1, 2, 3)}, "deadbeef")
+
+    Dumping into a string so that one can store it in a cookie:
+
+    >>> value = x.serialize()
+
+    Loading from that string again:
+
+    >>> x = SecureCookie.unserialize(value, "deadbeef")
+    >>> x["baz"]
+    (1, 2, 3)
+
+    If someone modifies the cookie and the checksum is wrong the unserialize
+    method will fail silently and return a new empty `SecureCookie` object.
+
+    Keep in mind that the values will be visible in the cookie so do not
+    store data in a cookie you don't want the user to see.
+
+    Application Integration
+    =======================
+
+    If you are using the werkzeug request objects you could integrate the
+    secure cookie into your application like this::
+
+        from werkzeug import BaseRequest, cached_property
+        from werkzeug.contrib.securecookie import SecureCookie
+
+        # don't use this key but a different one; you could just use
+        # os.urandom(20) to get something random
+        SECRET_KEY = '\xfa\xdd\xb8z\xae\xe0}4\x8b\xea'
+
+        class Request(BaseRequest):
+
+            @cached_property
+            def client_session(self):
+                data = self.cookies.get('session_data')
+                if not data:
+                    return SecureCookie(secret_key=SECRET_KEY)
+                return SecureCookie.unserialize(data, SECRET_KEY)
+
+        def application(environ, start_response):
+            request = Request(environ, start_response)
+
+            # get a response object here
+            response = ...
+
+            if request.client_session.should_save:
+                session_data = request.client_session.serialize()
+                response.set_cookie('session_data', session_data,
+                                    httponly=True)
+            return response(environ, start_response)
+
+    A less verbose integration can be achieved by using shorthand methods::
+
+        class Request(BaseRequest):
+
+            @cached_property
+            def client_session(self):
+                return SecureCookie.load_cookie(self, secret_key=COOKIE_SECRET)
+
+        def application(environ, start_response):
+            request = Request(environ, start_response)
+
+            # get a response object here
+            response = ...
+
+            request.client_session.save_cookie(response)
+            return response(environ, start_response)
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import sys
+import cPickle as pickle
+from hmac import new as hmac
+from datetime import datetime
+from time import time, mktime, gmtime
+from werkzeug import url_quote_plus, url_unquote_plus
+from werkzeug._internal import _date_to_unix
+from werkzeug.contrib.sessions import ModificationTrackingDict
+
+
+# rather ugly way to import the correct hash method.  Because
+# hmac either accepts modules with a new method (sha, md5 etc.)
+# or a hashlib factory function we have to figure out what to
+# pass to it.  If we have 2.5 or higher (so not 2.4 with a
+# custom hashlib) we import from hashlib and fail if it does
+# not exist (have seen that in old OS X versions).
+# in all other cases the now deprecated sha module is used.
+_default_hash = None
+if sys.version_info >= (2, 5):
+    try:
+        from hashlib import sha1 as _default_hash
+    except ImportError:
+        pass
+if _default_hash is None:
+    import sha as _default_hash
+
+
+class UnquoteError(Exception):
+    """Internal exception used to signal failures on quoting."""
+
+
+class SecureCookie(ModificationTrackingDict):
+    """Represents a secure cookie.  You can subclass this class and provide
+    an alternative mac method.  The import thing is that the mac method
+    is a function with a similar interface to the hashlib.  Required
+    methods are update() and digest().
+
+    Example usage:
+
+    >>> x = SecureCookie({"foo": 42, "baz": (1, 2, 3)}, "deadbeef")
+    >>> x["foo"]
+    42
+    >>> x["baz"]
+    (1, 2, 3)
+    >>> x["blafasel"] = 23
+    >>> x.should_save
+    True
+
+    :param data: the initial data.  Either a dict, list of tuples or `None`.
+    :param secret_key: the secret key.  If not set `None` or not specified
+                       it has to be set before :meth:`serialize` is called.
+    :param new: The initial value of the `new` flag.
+    """
+
+    #: The hash method to use.  This has to be a module with a new function
+    #: or a function that creates a hashlib object.  Such as `hashlib.md5`
+    #: Subclasses can override this attribute.  The default hash is sha1.
+    hash_method = _default_hash
+
+    #: the module used for serialization.  Unless overriden by subclasses
+    #: the standard pickle module is used.
+    serialization_method = pickle
+
+    #: if the contents should be base64 quoted.  This can be disabled if the
+    #: serialization process returns cookie safe strings only.
+    quote_base64 = True
+
+    def __init__(self, data=None, secret_key=None, new=True):
+        ModificationTrackingDict.__init__(self, data or ())
+        # explicitly convert it into a bytestring because python 2.6
+        # no longer performs an implicit string conversion on hmac
+        if secret_key is not None:
+            secret_key = str(secret_key)
+        self.secret_key = secret_key
+        self.new = new
+
+    def __repr__(self):
+        return '<%s %s%s>' % (
+            self.__class__.__name__,
+            dict.__repr__(self),
+            self.should_save and '*' or ''
+        )
+
+    @property
+    def should_save(self):
+        """True if the session should be saved.  By default this is only true
+        for :attr:`modified` cookies, not :attr:`new`.
+        """
+        return self.modified
+
+    @classmethod
+    def quote(cls, value):
+        """Quote the value for the cookie.  This can be any object supported
+        by :attr:`serialization_method`.
+
+        :param value: the value to quote.
+        """
+        if cls.serialization_method is not None:
+            value = cls.serialization_method.dumps(value)
+        if cls.quote_base64:
+            value = ''.join(value.encode('base64').splitlines()).strip()
+        return value
+
+    @classmethod
+    def unquote(cls, value):
+        """Unquote the value for the cookie.  If unquoting does not work a
+        :exc:`UnquoteError` is raised.
+
+        :param value: the value to unquote.
+        """
+        try:
+            if cls.quote_base64:
+                value = value.decode('base64')
+            if cls.serialization_method is not None:
+                value = cls.serialization_method.loads(value)
+            return value
+        except:
+            # unfortunately pickle and other serialization modules can
+            # cause pretty every error here.  if we get one we catch it
+            # and convert it into an UnquoteError
+            raise UnquoteError()
+
+    def serialize(self, expires=None):
+        """Serialize the secure cookie into a string.
+
+        If expires is provided, the session will be automatically invalidated
+        after expiration when you unseralize it. This provides better
+        protection against session cookie theft.
+
+        :param expires: an optional expiration date for the cookie (a
+                        :class:`datetime.datetime` object)
+        """
+        if self.secret_key is None:
+            raise RuntimeError('no secret key defined')
+        if expires:
+            self['_expires'] = _date_to_unix(expires)
+        result = []
+        mac = hmac(self.secret_key, None, self.hash_method)
+        for key, value in sorted(self.items()):
+            result.append('%s=%s' % (
+                url_quote_plus(key),
+                self.quote(value)
+            ))
+            mac.update('|' + result[-1])
+        return '%s?%s' % (
+            mac.digest().encode('base64').strip(),
+            '&'.join(result)
+        )
+
+    @classmethod
+    def unserialize(cls, string, secret_key):
+        """Load the secure cookie from a serialized string.
+
+        :param string: the cookie value to unserialize.
+        :param secret_key: the secret key used to serialize the cookie.
+        :return: a new :class:`SecureCookie`.
+        """
+        if isinstance(string, unicode):
+            string = string.encode('utf-8', 'ignore')
+        try:
+            base64_hash, data = string.split('?', 1)
+        except (ValueError, IndexError):
+            items = ()
+        else:
+            items = {}
+            mac = hmac(secret_key, None, cls.hash_method)
+            for item in data.split('&'):
+                mac.update('|' + item)
+                if not '=' in item:
+                    items = None
+                    break
+                key, value = item.split('=', 1)
+                # try to make the key a string
+                key = url_unquote_plus(key)
+                try:
+                    key = str(key)
+                except UnicodeError:
+                    pass
+                items[key] = value
+
+            # no parsing error and the mac looks okay, we can now
+            # sercurely unpickle our cookie.
+            try:
+                client_hash = base64_hash.decode('base64')
+            except Exception:
+                items = client_hash = None
+            if items is not None and client_hash == mac.digest():
+                try:
+                    for key, value in items.iteritems():
+                        items[key] = cls.unquote(value)
+                except UnquoteError:
+                    items = ()
+                else:
+                    if '_expires' in items:
+                        if time() > items['_expires']:
+                            items = ()
+                        else:
+                            del items['_expires']
+            else:
+                items = ()
+        return cls(items, secret_key, False)
+
+    @classmethod
+    def load_cookie(cls, request, key='session', secret_key=None):
+        """Loads a :class:`SecureCookie` from a cookie in request.  If the
+        cookie is not set, a new :class:`SecureCookie` instanced is
+        returned.
+
+        :param request: a request object that has a `cookies` attribute
+                        which is a dict of all cookie values.
+        :param key: the name of the cookie.
+        :param secret_key: the secret key used to unquote the cookie.
+                           Always provide the value even though it has
+                           no default!
+        """
+        data = request.cookies.get(key)
+        if not data:
+            return cls(secret_key=secret_key)
+        return cls.unserialize(data, secret_key)
+
+    def save_cookie(self, response, key='session', expires=None,
+                    session_expires=None, max_age=None, path='/', domain=None,
+                    secure=None, httponly=False, force=False):
+        """Saves the SecureCookie in a cookie on response object.  All
+        parameters that are not described here are forwarded directly
+        to :meth:`~BaseResponse.set_cookie`.
+
+        :param response: a response object that has a
+                         :meth:`~BaseResponse.set_cookie` method.
+        :param key: the name of the cookie.
+        :param session_expires: the expiration date of the secure cookie
+                                stored information.  If this is not provided
+                                the cookie `expires` date is used instead.
+        """
+        if force or self.should_save:
+            data = self.serialize(session_expires or expires)
+            response.set_cookie(key, data, expires=expires, max_age=max_age,
+                                path=path, domain=domain, secure=secure,
+                                httponly=httponly)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/sessions.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,342 @@
+# -*- coding: utf-8 -*-
+r"""
+    werkzeug.contrib.sessions
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module contains some helper classes that help one to add session
+    support to a python WSGI application.  For full client-side session
+    storage see :mod:`~werkzeug.contrib.securecookie` which implements a
+    secure, client-side session storage.
+
+
+    Application Integration
+    =======================
+
+    ::
+
+        from werkzeug.contrib.sessions import SessionMiddleware, \
+             FilesystemSessionStore
+
+        app = SessionMiddleware(app, FilesystemSessionStore())
+
+    The current session will then appear in the WSGI environment as
+    `werkzeug.session`.  However it's recommended to not use the middleware
+    but the stores directly in the application.  However for very simple
+    scripts a middleware for sessions could be sufficient.
+
+    This module does not implement methods or ways to check if a session is
+    expired.  That should be done by a cronjob and storage specific.  For
+    example to prune unused filesystem sessions one could check the modified
+    time of the files.  It sessions are stored in the database the new()
+    method should add an expiration timestamp for the session.
+
+    For better flexibility it's recommended to not use the middleware but the
+    store and session object directly in the application dispatching::
+
+        session_store = FilesystemSessionStore()
+
+        def application(environ, start_response):
+            request = Request(environ)
+            sid = request.cookies.get('cookie_name')
+            if sid is None:
+                request.session = session_store.new()
+            else:
+                request.session = session_store.get(sid)
+            response = get_the_response_object(request)
+            if request.session.should_save:
+                session_store.save(request.session)
+                response.set_cookie('cookie_name', request.session.sid)
+            return response(environ, start_response)
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import os
+import sys
+import tempfile
+from os import path
+from time import time
+from random import random
+try:
+    from hashlib import sha1
+except ImportError:
+    from sha import new as sha1
+from cPickle import dump, load, HIGHEST_PROTOCOL
+
+from werkzeug import ClosingIterator, dump_cookie, parse_cookie, CallbackDict
+from werkzeug.posixemulation import rename
+
+
+_sha1_re = re.compile(r'^[a-f0-9]{40}$')
+
+
+def _urandom():
+    if hasattr(os, 'urandom'):
+        return os.urandom(30)
+    return random()
+
+
+def generate_key(salt=None):
+    return sha1('%s%s%s' % (salt, time(), _urandom())).hexdigest()
+
+
+class ModificationTrackingDict(CallbackDict):
+    __slots__ = ('modified',)
+
+    def __init__(self, *args, **kwargs):
+        def on_update(self):
+            self.modified = True
+        self.modified = False
+        CallbackDict.__init__(self, on_update=on_update)
+        dict.update(self, *args, **kwargs)
+
+    def copy(self):
+        """Create a flat copy of the dict."""
+        missing = object()
+        result = object.__new__(self.__class__)
+        for name in self.__slots__:
+            val = getattr(self, name, missing)
+            if val is not missing:
+                setattr(result, name, val)
+        return result
+
+    def __copy__(self):
+        return self.copy()
+
+
+class Session(ModificationTrackingDict):
+    """Subclass of a dict that keeps track of direct object changes.  Changes
+    in mutable structures are not tracked, for those you have to set
+    `modified` to `True` by hand.
+    """
+    __slots__ = ModificationTrackingDict.__slots__ + ('sid', 'new')
+
+    def __init__(self, data, sid, new=False):
+        ModificationTrackingDict.__init__(self, data)
+        self.sid = sid
+        self.new = new
+
+    def __repr__(self):
+        return '<%s %s%s>' % (
+            self.__class__.__name__,
+            dict.__repr__(self),
+            self.should_save and '*' or ''
+        )
+
+    @property
+    def should_save(self):
+        """True if the session should be saved.
+
+        .. versionchanged:: 0.6
+           By default the session is now only saved if the session is
+           modified, not if it is new like it was before.
+        """
+        return self.modified
+
+
+class SessionStore(object):
+    """Baseclass for all session stores.  The Werkzeug contrib module does not
+    implement any useful stores besides the filesystem store, application
+    developers are encouraged to create their own stores.
+
+    :param session_class: The session class to use.  Defaults to
+                          :class:`Session`.
+    """
+
+    def __init__(self, session_class=None):
+        if session_class is None:
+            session_class = Session
+        self.session_class = session_class
+
+    def is_valid_key(self, key):
+        """Check if a key has the correct format."""
+        return _sha1_re.match(key) is not None
+
+    def generate_key(self, salt=None):
+        """Simple function that generates a new session key."""
+        return generate_key(salt)
+
+    def new(self):
+        """Generate a new session."""
+        return self.session_class({}, self.generate_key(), True)
+
+    def save(self, session):
+        """Save a session."""
+
+    def save_if_modified(self, session):
+        """Save if a session class wants an update."""
+        if session.should_save:
+            self.save(session)
+
+    def delete(self, session):
+        """Delete a session."""
+
+    def get(self, sid):
+        """Get a session for this sid or a new session object.  This method
+        has to check if the session key is valid and create a new session if
+        that wasn't the case.
+        """
+        return self.session_class({}, sid, True)
+
+
+#: used for temporary files by the filesystem session store
+_fs_transaction_suffix = '.__wz_sess'
+
+
+class FilesystemSessionStore(SessionStore):
+    """Simple example session store that saves sessions on the filesystem.
+    This store works best on POSIX systems and Windows Vista / Windows
+    Server 2008 and newer.
+
+    .. versionchanged:: 0.6
+       `renew_missing` was added.  Previously this was considered `True`,
+       now the default changed to `False` and it can be explicitly
+       deactivated.
+
+    :param path: the path to the folder used for storing the sessions.
+                 If not provided the default temporary directory is used.
+    :param filename_template: a string template used to give the session
+                              a filename.  ``%s`` is replaced with the
+                              session id.
+    :param session_class: The session class to use.  Defaults to
+                          :class:`Session`.
+    :param renew_missing: set to `True` if you want the store to
+                          give the user a new sid if the session was
+                          not yet saved.
+    """
+
+    def __init__(self, path=None, filename_template='werkzeug_%s.sess',
+                 session_class=None, renew_missing=False, mode=0644):
+        SessionStore.__init__(self, session_class)
+        if path is None:
+            path = tempfile.gettempdir()
+        self.path = path
+        if isinstance(filename_template, unicode):
+            filename_template = filename_template.encode(
+                sys.getfilesystemencoding() or 'utf-8')
+        assert not filename_template.endswith(_fs_transaction_suffix), \
+            'filename templates may not end with %s' % _fs_transaction_suffix
+        self.filename_template = filename_template
+        self.renew_missing = renew_missing
+        self.mode = mode
+
+    def get_session_filename(self, sid):
+        # out of the box, this should be a strict ASCII subset but
+        # you might reconfigure the session object to have a more
+        # arbitrary string.
+        if isinstance(sid, unicode):
+            sid = sid.encode(sys.getfilesystemencoding() or 'utf-8')
+        return path.join(self.path, self.filename_template % sid)
+
+    def save(self, session):
+        fn = self.get_session_filename(session.sid)
+        fd, tmp = tempfile.mkstemp(suffix=_fs_transaction_suffix,
+                                   dir=self.path)
+        f = os.fdopen(fd, 'wb')
+        try:
+            dump(dict(session), f, HIGHEST_PROTOCOL)
+        finally:
+            f.close()
+        try:
+            rename(tmp, fn)
+            os.chmod(fn, self.mode)
+        except (IOError, OSError):
+            pass
+
+    def delete(self, session):
+        fn = self.get_session_filename(session.sid)
+        try:
+            os.unlink(fn)
+        except OSError:
+            pass
+
+    def get(self, sid):
+        if not self.is_valid_key(sid):
+            return self.new()
+        try:
+            f = open(self.get_session_filename(sid), 'rb')
+        except IOError:
+            if self.renew_missing:
+                return self.new()
+            data = {}
+        else:
+            try:
+                try:
+                    data = load(f)
+                except Exception:
+                    data = {}
+            finally:
+                f.close()
+        return self.session_class(data, sid, False)
+
+    def list(self):
+        """Lists all sessions in the store.
+
+        .. versionadded:: 0.6
+        """
+        before, after = self.filename_template.split('%s', 1)
+        filename_re = re.compile(r'%s(.{5,})%s$' % (re.escape(before),
+                                                    re.escape(after)))
+        result = []
+        for filename in os.listdir(self.path):
+            #: this is a session that is still being saved.
+            if filename.endswith(_fs_transaction_suffix):
+                continue
+            match = filename_re.match(filename)
+            if match is not None:
+                result.append(match.group(1))
+        return result
+
+
+class SessionMiddleware(object):
+    """A simple middleware that puts the session object of a store provided
+    into the WSGI environ.  It automatically sets cookies and restores
+    sessions.
+
+    However a middleware is not the preferred solution because it won't be as
+    fast as sessions managed by the application itself and will put a key into
+    the WSGI environment only relevant for the application which is against
+    the concept of WSGI.
+
+    The cookie parameters are the same as for the :func:`~werkzeug.dump_cookie`
+    function just prefixed with ``cookie_``.  Additionally `max_age` is
+    called `cookie_age` and not `cookie_max_age` because of backwards
+    compatibility.
+    """
+
+    def __init__(self, app, store, cookie_name='session_id',
+                 cookie_age=None, cookie_expires=None, cookie_path='/',
+                 cookie_domain=None, cookie_secure=None,
+                 cookie_httponly=False, environ_key='werkzeug.session'):
+        self.app = app
+        self.store = store
+        self.cookie_name = cookie_name
+        self.cookie_age = cookie_age
+        self.cookie_expires = cookie_expires
+        self.cookie_path = cookie_path
+        self.cookie_domain = cookie_domain
+        self.cookie_secure = cookie_secure
+        self.cookie_httponly = cookie_httponly
+        self.environ_key = environ_key
+
+    def __call__(self, environ, start_response):
+        cookie = parse_cookie(environ.get('HTTP_COOKIE', ''))
+        sid = cookie.get(self.cookie_name, None)
+        if sid is None:
+            session = self.store.new()
+        else:
+            session = self.store.get(sid)
+        environ[self.environ_key] = session
+
+        def injecting_start_response(status, headers, exc_info=None):
+            if session.should_save:
+                self.store.save(session)
+                headers.append(('Set-Cookie', dump_cookie(self.cookie_name,
+                                session.sid, self.cookie_age,
+                                self.cookie_expires, self.cookie_path,
+                                self.cookie_domain, self.cookie_secure,
+                                self.cookie_httponly)))
+            return start_response(status, headers, exc_info)
+        return ClosingIterator(self.app(environ, injecting_start_response),
+                               lambda: self.store.save_if_modified(session))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/testtools.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.testtools
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module implements extended wrappers for simplified testing.
+
+    `TestResponse`
+        A response wrapper which adds various cached attributes for
+        simplified assertions on various content types.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from werkzeug import Response, cached_property, import_string
+
+
+class ContentAccessors(object):
+    """
+    A mixin class for response objects that provides a couple of useful
+    accessors for unittesting.
+    """
+
+    def xml(self):
+        """Get an etree if possible."""
+        if 'xml' not in self.mimetype:
+            raise AttributeError(
+                'Not a XML response (Content-Type: %s)'
+                % self.mimetype)
+        for module in ['xml.etree.ElementTree', 'ElementTree',
+                       'elementtree.ElementTree']:
+            etree = import_string(module, silent=True)
+            if etree is not None:
+                return etree.XML(self.body)
+        raise RuntimeError('You must have ElementTree installed '
+                           'to use TestResponse.xml')
+    xml = cached_property(xml)
+
+    def lxml(self):
+        """Get an lxml etree if possible."""
+        if ('html' not in self.mimetype and 'xml' not in self.mimetype):
+            raise AttributeError('Not an HTML/XML response')
+        from lxml import etree
+        try:
+            from lxml.html import fromstring
+        except ImportError:
+            fromstring = etree.HTML
+        if self.mimetype=='text/html':
+            return fromstring(self.data)
+        return etree.XML(self.data)
+    lxml = cached_property(lxml)
+
+    def json(self):
+        """Get the result of simplejson.loads if possible."""
+        if 'json' not in self.mimetype:
+            raise AttributeError('Not a JSON response')
+        try:
+            from simplejson import loads
+        except:
+            from json import loads
+        return loads(self.data)
+    json = cached_property(json)
+
+
+class TestResponse(Response, ContentAccessors):
+    """Pass this to `werkzeug.test.Client` for easier unittesting."""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/contrib/wrappers.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,275 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.contrib.wrappers
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Extra wrappers or mixins contributed by the community.  These wrappers can
+    be mixed in into request objects to add extra functionality.
+
+    Example::
+
+        from werkzeug import Request as RequestBase
+        from werkzeug.contrib.wrappers import JSONRequestMixin
+
+        class Request(RequestBase, JSONRequestMixin):
+            pass
+
+    Afterwards this request object provides the extra functionality of the
+    :class:`JSONRequestMixin`.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import codecs
+from werkzeug.exceptions import BadRequest
+from werkzeug.utils import cached_property
+from werkzeug.http import dump_options_header, parse_options_header
+from werkzeug._internal import _decode_unicode
+try:
+    from simplejson import loads
+except ImportError:
+    from json import loads
+
+
+def is_known_charset(charset):
+    """Checks if the given charset is known to Python."""
+    try:
+        codecs.lookup(charset)
+    except LookupError:
+        return False
+    return True
+
+
+class JSONRequestMixin(object):
+    """Add json method to a request object.  This will parse the input data
+    through simplejson if possible.
+
+    :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
+    is not json or if the data itself cannot be parsed as json.
+    """
+
+    @cached_property
+    def json(self):
+        """Get the result of simplejson.loads if possible."""
+        if 'json' not in self.environ.get('CONTENT_TYPE', ''):
+            raise BadRequest('Not a JSON request')
+        try:
+            return loads(self.data)
+        except Exception:
+            raise BadRequest('Unable to read JSON request')
+
+
+class ProtobufRequestMixin(object):
+    """Add protobuf parsing method to a request object.  This will parse the
+    input data through `protobuf`_ if possible.
+
+    :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
+    is not protobuf or if the data itself cannot be parsed property.
+
+    .. _protobuf: http://code.google.com/p/protobuf/
+    """
+
+    #: by default the :class:`ProtobufRequestMixin` will raise a
+    #: :exc:`~werkzeug.exceptions.BadRequest` if the object is not
+    #: initialized.  You can bypass that check by setting this
+    #: attribute to `False`.
+    protobuf_check_initialization = True
+
+    def parse_protobuf(self, proto_type):
+        """Parse the data into an instance of proto_type."""
+        if 'protobuf' not in self.environ.get('CONTENT_TYPE', ''):
+            raise BadRequest('Not a Protobuf request')
+
+        obj = proto_type()
+        try:
+            obj.ParseFromString(self.data)
+        except Exception:
+            raise BadRequest("Unable to parse Protobuf request")
+
+        # Fail if not all required fields are set
+        if self.protobuf_check_initialization and not obj.IsInitialized():
+            raise BadRequest("Partial Protobuf request")
+
+        return obj
+
+
+class RoutingArgsRequestMixin(object):
+    """This request mixin adds support for the wsgiorg routing args
+    `specification`_.
+
+    .. _specification: http://www.wsgi.org/wsgi/Specifications/routing_args
+    """
+
+    def _get_routing_args(self):
+        return self.environ.get('wsgiorg.routing_args', (()))[0]
+
+    def _set_routing_args(self, value):
+        if self.shallow:
+            raise RuntimeError('A shallow request tried to modify the WSGI '
+                               'environment.  If you really want to do that, '
+                               'set `shallow` to False.')
+        self.environ['wsgiorg.routing_args'] = (value, self.routing_vars)
+
+    routing_args = property(_get_routing_args, _set_routing_args, doc='''
+        The positional URL arguments as `tuple`.''')
+    del _get_routing_args, _set_routing_args
+
+    def _get_routing_vars(self):
+        rv = self.environ.get('wsgiorg.routing_args')
+        if rv is not None:
+            return rv[1]
+        rv = {}
+        if not self.shallow:
+            self.routing_vars = rv
+        return rv
+
+    def _set_routing_vars(self, value):
+        if self.shallow:
+            raise RuntimeError('A shallow request tried to modify the WSGI '
+                               'environment.  If you really want to do that, '
+                               'set `shallow` to False.')
+        self.environ['wsgiorg.routing_args'] = (self.routing_args, value)
+
+    routing_vars = property(_get_routing_vars, _set_routing_vars, doc='''
+        The keyword URL arguments as `dict`.''')
+    del _get_routing_vars, _set_routing_vars
+
+
+class ReverseSlashBehaviorRequestMixin(object):
+    """This mixin reverses the trailing slash behavior of :attr:`script_root`
+    and :attr:`path`.  This makes it possible to use :func:`~urlparse.urljoin`
+    directly on the paths.
+
+    Because it changes the behavior or :class:`Request` this class has to be
+    mixed in *before* the actual request class::
+
+        class MyRequest(ReverseSlashBehaviorRequestMixin, Request):
+            pass
+
+    This example shows the differences (for an application mounted on
+    `/application` and the request going to `/application/foo/bar`):
+
+        +---------------+-------------------+---------------------+
+        |               | normal behavior   | reverse behavior    |
+        +===============+===================+=====================+
+        | `script_root` | ``/application``  | ``/application/``   |
+        +---------------+-------------------+---------------------+
+        | `path`        | ``/foo/bar``      | ``foo/bar``         |
+        +---------------+-------------------+---------------------+
+    """
+
+    @cached_property
+    def path(self):
+        """Requested path as unicode.  This works a bit like the regular path
+        info in the WSGI environment but will not include a leading slash.
+        """
+        path = (self.environ.get('PATH_INFO') or '').lstrip('/')
+        return _decode_unicode(path, self.charset, self.encoding_errors)
+
+    @cached_property
+    def script_root(self):
+        """The root path of the script includling a trailing slash."""
+        path = (self.environ.get('SCRIPT_NAME') or '').rstrip('/') + '/'
+        return _decode_unicode(path, self.charset, self.encoding_errors)
+
+
+class DynamicCharsetRequestMixin(object):
+    """"If this mixin is mixed into a request class it will provide
+    a dynamic `charset` attribute.  This means that if the charset is
+    transmitted in the content type headers it's used from there.
+
+    Because it changes the behavior or :class:`Request` this class has
+    to be mixed in *before* the actual request class::
+
+        class MyRequest(DynamicCharsetRequestMixin, Request):
+            pass
+
+    By default the request object assumes that the URL charset is the
+    same as the data charset.  If the charset varies on each request
+    based on the transmitted data it's not a good idea to let the URLs
+    change based on that.  Most browsers assume either utf-8 or latin1
+    for the URLs if they have troubles figuring out.  It's strongly
+    recommended to set the URL charset to utf-8::
+
+        class MyRequest(DynamicCharsetRequestMixin, Request):
+            url_charset = 'utf-8'
+
+    .. versionadded:: 0.6
+    """
+
+    #: the default charset that is assumed if the content type header
+    #: is missing or does not contain a charset parameter.  The default
+    #: is latin1 which is what HTTP specifies as default charset.
+    #: You may however want to set this to utf-8 to better support
+    #: browsers that do not transmit a charset for incoming data.
+    default_charset = 'latin1'
+
+    def unknown_charset(self, charset):
+        """Called if a charset was provided but is not supported by
+        the Python codecs module.  By default latin1 is assumed then
+        to not lose any information, you may override this method to
+        change the behavior.
+
+        :param charset: the charset that was not found.
+        :return: the replacement charset.
+        """
+        return 'latin1'
+
+    @cached_property
+    def charset(self):
+        """The charset from the content type."""
+        header = self.environ.get('CONTENT_TYPE')
+        if header:
+            ct, options = parse_options_header(header)
+            charset = options.get('charset')
+            if charset:
+                if is_known_charset(charset):
+                    return charset
+                return self.unknown_charset(charset)
+        return self.default_charset
+
+
+class DynamicCharsetResponseMixin(object):
+    """If this mixin is mixed into a response class it will provide
+    a dynamic `charset` attribute.  This means that if the charset is
+    looked up and stored in the `Content-Type` header and updates
+    itself automatically.  This also means a small performance hit but
+    can be useful if you're working with different charsets on
+    responses.
+
+    Because the charset attribute is no a property at class-level, the
+    default value is stored in `default_charset`.
+
+    Because it changes the behavior or :class:`Response` this class has
+    to be mixed in *before* the actual response class::
+
+        class MyResponse(DynamicCharsetResponseMixin, Response):
+            pass
+
+    .. versionadded:: 0.6
+    """
+
+    #: the default charset.
+    default_charset = 'utf-8'
+
+    def _get_charset(self):
+        header = self.headers.get('content-type')
+        if header:
+            charset = parse_options_header(header)[1].get('charset')
+            if charset:
+                return charset
+        return self.default_charset
+
+    def _set_charset(self, charset):
+        header = self.headers.get('content-type')
+        ct, options = parse_options_header(header)
+        if not ct:
+            raise TypeError('Cannot set charset if Content-Type '
+                            'header is missing.')
+        options['charset'] = charset
+        self.headers['Content-Type'] = dump_options_header(ct, options)
+
+    charset = property(_get_charset, _set_charset, doc="""
+        The charset for the response.  It's stored inside the
+        Content-Type header as a parameter.""")
+    del _get_charset, _set_charset
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/datastructures.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,2331 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.datastructures
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module provides mixins and classes with an immutable interface.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import codecs
+import mimetypes
+
+from werkzeug._internal import _proxy_repr, _missing, _empty_stream
+
+
+_locale_delim_re = re.compile(r'[_-]')
+
+
+def is_immutable(self):
+    raise TypeError('%r objects are immutable' % self.__class__.__name__)
+
+
+def iter_multi_items(mapping):
+    """Iterates over the items of a mapping yielding keys and values
+    without dropping any from more complex structures.
+    """
+    if isinstance(mapping, MultiDict):
+        for item in mapping.iteritems(multi=True):
+            yield item
+    elif isinstance(mapping, dict):
+        for key, value in mapping.iteritems():
+            if isinstance(value, (tuple, list)):
+                for value in value:
+                    yield key, value
+            else:
+                yield key, value
+    else:
+        for item in mapping:
+            yield item
+
+
+class ImmutableListMixin(object):
+    """Makes a :class:`list` immutable.
+
+    .. versionadded:: 0.5
+
+    :private:
+    """
+
+    def __reduce_ex__(self, protocol):
+        return type(self), (list(self),)
+
+    def __delitem__(self, key):
+        is_immutable(self)
+
+    def __delslice__(self, i, j):
+        is_immutable(self)
+
+    def __iadd__(self, other):
+        is_immutable(self)
+    __imul__ = __iadd__
+
+    def __setitem__(self, key, value):
+        is_immutable(self)
+
+    def __setslice__(self, i, j, value):
+        is_immutable(self)
+
+    def append(self, item):
+        is_immutable(self)
+    remove = append
+
+    def extend(self, iterable):
+        is_immutable(self)
+
+    def insert(self, pos, value):
+        is_immutable(self)
+
+    def pop(self, index=-1):
+        is_immutable(self)
+
+    def reverse(self):
+        is_immutable(self)
+
+    def sort(self, cmp=None, key=None, reverse=None):
+        is_immutable(self)
+
+
+class ImmutableList(ImmutableListMixin, list):
+    """An immutable :class:`list`.
+
+    .. versionadded:: 0.5
+
+    :private:
+    """
+
+    __repr__ = _proxy_repr(list)
+
+
+class ImmutableDictMixin(object):
+    """Makes a :class:`dict` immutable.
+
+    .. versionadded:: 0.5
+
+    :private:
+    """
+
+    def __reduce_ex__(self, protocol):
+        return type(self), (dict(self),)
+
+    def setdefault(self, key, default=None):
+        is_immutable(self)
+
+    def update(self, *args, **kwargs):
+        is_immutable(self)
+
+    def pop(self, key, default=None):
+        is_immutable(self)
+
+    def popitem(self):
+        is_immutable(self)
+
+    def __setitem__(self, key, value):
+        is_immutable(self)
+
+    def __delitem__(self, key):
+        is_immutable(self)
+
+    def clear(self):
+        is_immutable(self)
+
+
+class ImmutableMultiDictMixin(ImmutableDictMixin):
+    """Makes a :class:`MultiDict` immutable.
+
+    .. versionadded:: 0.5
+
+    :private:
+    """
+
+    def __reduce_ex__(self, protocol):
+        return type(self), (self.items(multi=True),)
+
+    def add(self, key, value):
+        is_immutable(self)
+
+    def popitemlist(self):
+        is_immutable(self)
+
+    def poplist(self, key):
+        is_immutable(self)
+
+    def setlist(self, key, new_list):
+        is_immutable(self)
+
+    def setlistdefault(self, key, default_list=None):
+        is_immutable(self)
+
+
+class UpdateDictMixin(object):
+    """Makes dicts call `self.on_update` on modifications.
+
+    .. versionadded:: 0.5
+
+    :private:
+    """
+
+    on_update = None
+
+    def calls_update(name):
+        def oncall(self, *args, **kw):
+            rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw)
+            if self.on_update is not None:
+                self.on_update(self)
+            return rv
+        oncall.__name__ = name
+        return oncall
+
+    __setitem__ = calls_update('__setitem__')
+    __delitem__ = calls_update('__delitem__')
+    clear = calls_update('clear')
+    pop = calls_update('pop')
+    popitem = calls_update('popitem')
+    setdefault = calls_update('setdefault')
+    update = calls_update('update')
+    del calls_update
+
+
+class TypeConversionDict(dict):
+    """Works like a regular dict but the :meth:`get` method can perform
+    type conversions.  :class:`MultiDict` and :class:`CombinedMultiDict`
+    are subclasses of this class and provide the same feature.
+
+    .. versionadded:: 0.5
+    """
+
+    def get(self, key, default=None, type=None):
+        """Return the default value if the requested data doesn't exist.
+        If `type` is provided and is a callable it should convert the value,
+        return it or raise a :exc:`ValueError` if that is not possible.  In
+        this case the function will return the default as if the value was not
+        found:
+
+        >>> d = TypeConversionDict(foo='42', bar='blub')
+        >>> d.get('foo', type=int)
+        42
+        >>> d.get('bar', -1, type=int)
+        -1
+
+        :param key: The key to be looked up.
+        :param default: The default value to be returned if the key can't
+                        be looked up.  If not further specified `None` is
+                        returned.
+        :param type: A callable that is used to cast the value in the
+                     :class:`MultiDict`.  If a :exc:`ValueError` is raised
+                     by this callable the default value is returned.
+        """
+        try:
+            rv = self[key]
+            if type is not None:
+                rv = type(rv)
+        except (KeyError, ValueError):
+            rv = default
+        return rv
+
+
+class ImmutableTypeConversionDict(ImmutableDictMixin, TypeConversionDict):
+    """Works like a :class:`TypeConversionDict` but does not support
+    modifications.
+
+    .. versionadded:: 0.5
+    """
+
+    def copy(self):
+        """Return a shallow mutable copy of this object.  Keep in mind that
+        the standard library's :func:`copy` function is a no-op for this class
+        like for any other python immutable type (eg: :class:`tuple`).
+        """
+        return TypeConversionDict(self)
+
+    def __copy__(self):
+        return self
+
+
+class MultiDict(TypeConversionDict):
+    """A :class:`MultiDict` is a dictionary subclass customized to deal with
+    multiple values for the same key which is for example used by the parsing
+    functions in the wrappers.  This is necessary because some HTML form
+    elements pass multiple values for the same key.
+
+    :class:`MultiDict` implements all standard dictionary methods.
+    Internally, it saves all values for a key as a list, but the standard dict
+    access methods will only return the first value for a key. If you want to
+    gain access to the other values, too, you have to use the `list` methods as
+    explained below.
+
+    Basic Usage:
+
+    >>> d = MultiDict([('a', 'b'), ('a', 'c')])
+    >>> d
+    MultiDict([('a', 'b'), ('a', 'c')])
+    >>> d['a']
+    'b'
+    >>> d.getlist('a')
+    ['b', 'c']
+    >>> 'a' in d
+    True
+
+    It behaves like a normal dict thus all dict functions will only return the
+    first value when multiple values for one key are found.
+
+    From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
+    subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
+    render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
+    exceptions.
+
+    A :class:`MultiDict` can be constructed from an iterable of
+    ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
+    onwards some keyword parameters.
+
+    :param mapping: the initial value for the :class:`MultiDict`.  Either a
+                    regular dict, an iterable of ``(key, value)`` tuples
+                    or `None`.
+    """
+
+    # the key error this class raises.  Because of circular dependencies
+    # with the http exception module this class is created at the end of
+    # this module.
+    KeyError = None
+
+    def __init__(self, mapping=None):
+        if isinstance(mapping, MultiDict):
+            dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
+        elif isinstance(mapping, dict):
+            tmp = {}
+            for key, value in mapping.iteritems():
+                if isinstance(value, (tuple, list)):
+                    value = list(value)
+                else:
+                    value = [value]
+                tmp[key] = value
+            dict.__init__(self, tmp)
+        else:
+            tmp = {}
+            for key, value in mapping or ():
+                tmp.setdefault(key, []).append(value)
+            dict.__init__(self, tmp)
+
+    def __getstate__(self):
+        return dict(self.lists())
+
+    def __setstate__(self, value):
+        dict.clear(self)
+        dict.update(self, value)
+
+    def __iter__(self):
+        return self.iterkeys()
+
+    def __getitem__(self, key):
+        """Return the first data value for this key;
+        raises KeyError if not found.
+
+        :param key: The key to be looked up.
+        :raise KeyError: if the key does not exist.
+        """
+        if key in self:
+            return dict.__getitem__(self, key)[0]
+        raise self.KeyError(key)
+
+    def __setitem__(self, key, value):
+        """Like :meth:`add` but removes an existing key first.
+
+        :param key: the key for the value.
+        :param value: the value to set.
+        """
+        dict.__setitem__(self, key, [value])
+
+    def add(self, key, value):
+        """Adds a new value for the key.
+
+        .. versionadded:: 0.6
+
+        :param key: the key for the value.
+        :param value: the value to add.
+        """
+        dict.setdefault(self, key, []).append(value)
+
+    def getlist(self, key, type=None):
+        """Return the list of items for a given key. If that key is not in the
+        `MultiDict`, the return value will be an empty list.  Just as `get`
+        `getlist` accepts a `type` parameter.  All items will be converted
+        with the callable defined there.
+
+        :param key: The key to be looked up.
+        :param type: A callable that is used to cast the value in the
+                     :class:`MultiDict`.  If a :exc:`ValueError` is raised
+                     by this callable the value will be removed from the list.
+        :return: a :class:`list` of all the values for the key.
+        """
+        try:
+            rv = dict.__getitem__(self, key)
+        except KeyError:
+            return []
+        if type is None:
+            return list(rv)
+        result = []
+        for item in rv:
+            try:
+                result.append(type(item))
+            except ValueError:
+                pass
+        return result
+
+    def setlist(self, key, new_list):
+        """Remove the old values for a key and add new ones.  Note that the list
+        you pass the values in will be shallow-copied before it is inserted in
+        the dictionary.
+
+        >>> d = MultiDict()
+        >>> d.setlist('foo', ['1', '2'])
+        >>> d['foo']
+        '1'
+        >>> d.getlist('foo')
+        ['1', '2']
+
+        :param key: The key for which the values are set.
+        :param new_list: An iterable with the new values for the key.  Old values
+                         are removed first.
+        """
+        dict.__setitem__(self, key, list(new_list))
+
+    def setdefault(self, key, default=None):
+        """Returns the value for the key if it is in the dict, otherwise it
+        returns `default` and sets that value for `key`.
+
+        :param key: The key to be looked up.
+        :param default: The default value to be returned if the key is not
+                        in the dict.  If not further specified it's `None`.
+        """
+        if key not in self:
+            self[key] = default
+        else:
+            default = self[key]
+        return default
+
+    def setlistdefault(self, key, default_list=None):
+        """Like `setdefault` but sets multiple values.  The list returned
+        is not a copy, but the list that is actually used internally.  This
+        means that you can put new values into the dict by appending items
+        to the list:
+
+        >>> d = MultiDict({"foo": 1})
+        >>> d.setlistdefault("foo").extend([2, 3])
+        >>> d.getlist("foo")
+        [1, 2, 3]
+
+        :param key: The key to be looked up.
+        :param default: An iterable of default values.  It is either copied
+                        (in case it was a list) or converted into a list
+                        before returned.
+        :return: a :class:`list`
+        """
+        if key not in self:
+            default_list = list(default_list or ())
+            dict.__setitem__(self, key, default_list)
+        else:
+            default_list = dict.__getitem__(self, key)
+        return default_list
+
+    def items(self, multi=False):
+        """Return a list of ``(key, value)`` pairs.
+
+        :param multi: If set to `True` the list returned will have a
+                      pair for each value of each key.  Otherwise it
+                      will only contain pairs for the first value of
+                      each key.
+
+        :return: a :class:`list`
+        """
+        return list(self.iteritems(multi))
+
+    def lists(self):
+        """Return a list of ``(key, value)`` pairs, where values is the list of
+        all values associated with the key.
+
+        :return: a :class:`list`
+        """
+        return list(self.iterlists())
+
+    def values(self):
+        """Returns a list of the first value on every key's value list.
+
+        :return: a :class:`list`.
+        """
+        return [self[key] for key in self.iterkeys()]
+
+    def listvalues(self):
+        """Return a list of all values associated with a key.  Zipping
+        :meth:`keys` and this is the same as calling :meth:`lists`:
+
+        >>> d = MultiDict({"foo": [1, 2, 3]})
+        >>> zip(d.keys(), d.listvalues()) == d.lists()
+        True
+
+        :return: a :class:`list`
+        """
+        return list(self.iterlistvalues())
+
+    def iteritems(self, multi=False):
+        """Like :meth:`items` but returns an iterator."""
+        for key, values in dict.iteritems(self):
+            if multi:
+                for value in values:
+                    yield key, value
+            else:
+                yield key, values[0]
+
+    def iterlists(self):
+        """Return a list of all values associated with a key.
+
+        :return: a class:`list`
+        """
+        for key, values in dict.iteritems(self):
+            yield key, list(values)
+
+    def itervalues(self):
+        """Like :meth:`values` but returns an iterator."""
+        for values in dict.itervalues(self):
+            yield values[0]
+
+    def iterlistvalues(self):
+        """like :meth:`listvalues` but returns an iterator."""
+        for values in dict.itervalues(self):
+            yield list(values)
+
+    def copy(self):
+        """Return a shallow copy of this object."""
+        return self.__class__(self)
+
+    def to_dict(self, flat=True):
+        """Return the contents as regular dict.  If `flat` is `True` the
+        returned dict will only have the first item present, if `flat` is
+        `False` all values will be returned as lists.
+
+        :param flat: If set to `False` the dict returned will have lists
+                     with all the values in it.  Otherwise it will only
+                     contain the first value for each key.
+        :return: a :class:`dict`
+        """
+        if flat:
+            return dict(self.iteritems())
+        return dict(self.lists())
+
+    def update(self, other_dict):
+        """update() extends rather than replaces existing key lists."""
+        for key, value in iter_multi_items(other_dict):
+            MultiDict.add(self, key, value)
+
+    def pop(self, key, default=_missing):
+        """Pop the first item for a list on the dict.  Afterwards the
+        key is removed from the dict, so additional values are discarded:
+
+        >>> d = MultiDict({"foo": [1, 2, 3]})
+        >>> d.pop("foo")
+        1
+        >>> "foo" in d
+        False
+
+        :param key: the key to pop.
+        :param default: if provided the value to return if the key was
+                        not in the dictionary.
+        """
+        try:
+            return dict.pop(self, key)[0]
+        except KeyError, e:
+            if default is not _missing:
+                return default
+            raise self.KeyError(str(e))
+
+    def popitem(self):
+        """Pop an item from the dict."""
+        try:
+            item = dict.popitem(self)
+            return (item[0], item[1][0])
+        except KeyError, e:
+            raise self.KeyError(str(e))
+
+    def poplist(self, key):
+        """Pop the list for a key from the dict.  If the key is not in the dict
+        an empty list is returned.
+
+        .. versionchanged:: 0.5
+           If the key does no longer exist a list is returned instead of
+           raising an error.
+        """
+        return dict.pop(self, key, [])
+
+    def popitemlist(self):
+        """Pop a ``(key, list)`` tuple from the dict."""
+        try:
+            return dict.popitem(self)
+        except KeyError, e:
+            raise self.KeyError(str(e))
+
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
+
+
+class _omd_bucket(object):
+    """Wraps values in the :class:`OrderedMultiDict`.  This makes it
+    possible to keep an order over multiple different keys.  It requires
+    a lot of extra memory and slows down access a lot, but makes it
+    possible to access elements in O(1) and iterate in O(n).
+    """
+    __slots__ = ('prev', 'key', 'value', 'next')
+
+    def __init__(self, omd, key, value):
+        self.prev = omd._last_bucket
+        self.key = key
+        self.value = value
+        self.next = None
+
+        if omd._first_bucket is None:
+            omd._first_bucket = self
+        if omd._last_bucket is not None:
+            omd._last_bucket.next = self
+        omd._last_bucket = self
+
+    def unlink(self, omd):
+        if self.prev:
+            self.prev.next = self.next
+        if self.next:
+            self.next.prev = self.prev
+        if omd._first_bucket is self:
+            omd._first_bucket = self.next
+        if omd._last_bucket is self:
+            omd._last_bucket = self.prev
+
+
+class OrderedMultiDict(MultiDict):
+    """Works like a regular :class:`MultiDict` but preserves the
+    order of the fields.  To convert the ordered multi dict into a
+    list you can use the :meth:`items` method and pass it ``multi=True``.
+
+    In general an :class:`OrderedMultiDict` is an order of magnitude
+    slower than a :class:`MultiDict`.
+
+    .. admonition:: note
+
+       Due to a limitation in Python you cannot convert an ordered
+       multi dict into a regular dict by using ``dict(multidict)``.
+       Instead you have to use the :meth:`to_dict` method, otherwise
+       the internal bucket objects are exposed.
+    """
+
+    # the key error this class raises.  Because of circular dependencies
+    # with the http exception module this class is created at the end of
+    # this module.
+    KeyError = None
+
+    def __init__(self, mapping=None):
+        dict.__init__(self)
+        self._first_bucket = self._last_bucket = None
+        if mapping is not None:
+            OrderedMultiDict.update(self, mapping)
+
+    def __eq__(self, other):
+        if not isinstance(other, MultiDict):
+            return NotImplemented
+        if isinstance(other, OrderedMultiDict):
+            iter1 = self.iteritems(multi=True)
+            iter2 = other.iteritems(multi=True)
+            try:
+                for k1, v1 in iter1:
+                    k2, v2 = iter2.next()
+                    if k1 != k2 or v1 != v2:
+                        return False
+            except StopIteration:
+                return False
+            try:
+                iter2.next()
+            except StopIteration:
+                return True
+            return False
+        if len(self) != len(other):
+            return False
+        for key, values in self.iterlists():
+            if other.getlist(key) != values:
+                return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __reduce_ex__(self, protocol):
+        return type(self), (self.items(multi=True),)
+
+    def __getstate__(self):
+        return self.items(multi=True)
+
+    def __setstate__(self, values):
+        dict.clear(self)
+        for key, value in values:
+            self.add(key, value)
+
+    def __getitem__(self, key):
+        if key in self:
+            return dict.__getitem__(self, key)[0].value
+        raise self.KeyError(key)
+
+    def __setitem__(self, key, value):
+        self.poplist(key)
+        self.add(key, value)
+
+    def __delitem__(self, key):
+        self.pop(key)
+
+    def iterkeys(self):
+        return (key for key, value in self.iteritems())
+
+    def itervalues(self):
+        return (value for key, value in self.iteritems())
+
+    def iteritems(self, multi=False):
+        ptr = self._first_bucket
+        if multi:
+            while ptr is not None:
+                yield ptr.key, ptr.value
+                ptr = ptr.next
+        else:
+            returned_keys = set()
+            while ptr is not None:
+                if ptr.key not in returned_keys:
+                    returned_keys.add(ptr.key)
+                    yield ptr.key, ptr.value
+                ptr = ptr.next
+
+    def iterlists(self):
+        returned_keys = set()
+        ptr = self._first_bucket
+        while ptr is not None:
+            if ptr.key not in returned_keys:
+                yield ptr.key, self.getlist(ptr.key)
+                returned_keys.add(ptr.key)
+            ptr = ptr.next
+
+    def iterlistvalues(self):
+        for key, values in self.iterlists():
+            yield values
+
+    def add(self, key, value):
+        dict.setdefault(self, key, []).append(_omd_bucket(self, key, value))
+
+    def getlist(self, key, type=None):
+        try:
+            rv = dict.__getitem__(self, key)
+        except KeyError:
+            return []
+        if type is None:
+            return [x.value for x in rv]
+        result = []
+        for item in rv:
+            try:
+                result.append(type(item.value))
+            except ValueError:
+                pass
+        return result
+
+    def setlist(self, key, new_list):
+        self.poplist(key)
+        for value in new_list:
+            self.add(key, value)
+
+    def setlistdefault(self, key, default_list=None):
+        raise TypeError('setlistdefault is unsupported for '
+                        'ordered multi dicts')
+
+    def update(self, mapping):
+        for key, value in iter_multi_items(mapping):
+            OrderedMultiDict.add(self, key, value)
+
+    def poplist(self, key):
+        buckets = dict.pop(self, key, ())
+        for bucket in buckets:
+            bucket.unlink(self)
+        return [x.value for x in buckets]
+
+    def pop(self, key, default=_missing):
+        try:
+            buckets = dict.pop(self, key)
+        except KeyError, e:
+            if default is not _missing:
+                return default
+            raise self.KeyError(str(e))
+        for bucket in buckets:
+            bucket.unlink(self)
+        return buckets[0].value
+
+    def popitem(self):
+        try:
+            key, buckets = dict.popitem(self)
+        except KeyError, e:
+            raise self.KeyError(str(e))
+        for bucket in buckets:
+            bucket.unlink(self)
+        return key, buckets[0].value
+
+    def popitemlist(self):
+        try:
+            key, buckets = dict.popitem(self)
+        except KeyError, e:
+            raise self.KeyError(str(e))
+        for bucket in buckets:
+            bucket.unlink(self)
+        return key, [x.value for x in buckets]
+
+
+def _options_header_vkw(value, kw):
+    if not kw:
+        return value
+    return dump_options_header(value, dict((k.replace('_', '-'), v)
+                                            for k, v in kw.items()))
+
+
+class Headers(object):
+    """An object that stores some headers.  It has a dict-like interface
+    but is ordered and can store the same keys multiple times.
+
+    This data structure is useful if you want a nicer way to handle WSGI
+    headers which are stored as tuples in a list.
+
+    From Werkzeug 0.3 onwards, the :exc:`KeyError` raised by this class is
+    also a subclass of the :class:`~exceptions.BadRequest` HTTP exception
+    and will render a page for a ``400 BAD REQUEST`` if caught in a
+    catch-all for HTTP exceptions.
+
+    Headers is mostly compatible with the Python :class:`wsgiref.headers.Headers`
+    class, with the exception of `__getitem__`.  :mod:`wsgiref` will return
+    `None` for ``headers['missing']``, whereas :class:`Headers` will raise
+    a :class:`KeyError`.
+
+    To create a new :class:`Headers` object pass it a list or dict of headers
+    which are used as default values.  This does not reuse the list passed
+    to the constructor for internal usage.  To create a :class:`Headers`
+    object that uses as internal storage the list or list-like object you
+    can use the :meth:`linked` class method.
+
+    :param defaults: The list of default values for the :class:`Headers`.
+    """
+
+    # the key error this class raises.  Because of circular dependencies
+    # with the http exception module this class is created at the end of
+    # this module.
+    KeyError = None
+
+    def __init__(self, defaults=None, _list=None):
+        if _list is None:
+            _list = []
+        self._list = _list
+        if defaults is not None:
+            if isinstance(defaults, (list, Headers)):
+                self._list.extend(defaults)
+            else:
+                self.extend(defaults)
+
+    @classmethod
+    def linked(cls, headerlist):
+        """Create a new :class:`Headers` object that uses the list of headers
+        passed as internal storage:
+
+        >>> headerlist = [('Content-Length', '40')]
+        >>> headers = Headers.linked(headerlist)
+        >>> headers['Content-Type'] = 'text/html'
+        >>> headerlist
+        [('Content-Length', '40'), ('Content-Type', 'text/html')]
+
+        :param headerlist: The list of headers the class is linked to.
+        :return: new linked :class:`Headers` object.
+        """
+        return cls(_list=headerlist)
+
+    def __getitem__(self, key, _index_operation=True):
+        if _index_operation:
+            if isinstance(key, (int, long)):
+                return self._list[key]
+            elif isinstance(key, slice):
+                return self.__class__(self._list[key])
+        ikey = key.lower()
+        for k, v in self._list:
+            if k.lower() == ikey:
+                return v
+        raise self.KeyError(key)
+
+    def __eq__(self, other):
+        return other.__class__ is self.__class__ and \
+               set(other._list) == set(self._list)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def get(self, key, default=None, type=None):
+        """Return the default value if the requested data doesn't exist.
+        If `type` is provided and is a callable it should convert the value,
+        return it or raise a :exc:`ValueError` if that is not possible.  In
+        this case the function will return the default as if the value was not
+        found:
+
+        >>> d = Headers([('Content-Length', '42')])
+        >>> d.get('Content-Length', type=int)
+        42
+
+        If a headers object is bound you must not add unicode strings
+        because no encoding takes place.
+
+        :param key: The key to be looked up.
+        :param default: The default value to be returned if the key can't
+                        be looked up.  If not further specified `None` is
+                        returned.
+        :param type: A callable that is used to cast the value in the
+                     :class:`Headers`.  If a :exc:`ValueError` is raised
+                     by this callable the default value is returned.
+        """
+        try:
+            rv = self.__getitem__(key, _index_operation=False)
+        except KeyError:
+            return default
+        if type is None:
+            return rv
+        try:
+            return type(rv)
+        except ValueError:
+            return default
+
+    def getlist(self, key, type=None):
+        """Return the list of items for a given key. If that key is not in the
+        :class:`Headers`, the return value will be an empty list.  Just as
+        :meth:`get` :meth:`getlist` accepts a `type` parameter.  All items will
+        be converted with the callable defined there.
+
+        :param key: The key to be looked up.
+        :param type: A callable that is used to cast the value in the
+                     :class:`Headers`.  If a :exc:`ValueError` is raised
+                     by this callable the value will be removed from the list.
+        :return: a :class:`list` of all the values for the key.
+        """
+        ikey = key.lower()
+        result = []
+        for k, v in self:
+            if k.lower() == ikey:
+                if type is not None:
+                    try:
+                        v = type(v)
+                    except ValueError:
+                        continue
+                result.append(v)
+        return result
+
+    def get_all(self, name):
+        """Return a list of all the values for the named field.
+
+        This method is compatible with the :mod:`wsgiref`
+        :meth:`~wsgiref.headers.Headers.get_all` method.
+        """
+        return self.getlist(name)
+
+    def iteritems(self, lower=False):
+        for key, value in self:
+            if lower:
+                key = key.lower()
+            yield key, value
+
+    def iterkeys(self, lower=False):
+        for key, _ in self.iteritems(lower):
+            yield key
+
+    def itervalues(self):
+        for _, value in self.iteritems():
+            yield value
+
+    def keys(self, lower=False):
+        return list(self.iterkeys(lower))
+
+    def values(self):
+        return list(self.itervalues())
+
+    def items(self, lower=False):
+        return list(self.iteritems(lower))
+
+    def extend(self, iterable):
+        """Extend the headers with a dict or an iterable yielding keys and
+        values.
+        """
+        if isinstance(iterable, dict):
+            for key, value in iterable.iteritems():
+                if isinstance(value, (tuple, list)):
+                    for v in value:
+                        self.add(key, v)
+                else:
+                    self.add(key, value)
+        else:
+            for key, value in iterable:
+                self.add(key, value)
+
+    def __delitem__(self, key, _index_operation=True):
+        if _index_operation and isinstance(key, (int, long, slice)):
+            del self._list[key]
+            return
+        key = key.lower()
+        new = []
+        for k, v in self._list:
+            if k.lower() != key:
+                new.append((k, v))
+        self._list[:] = new
+
+    def remove(self, key):
+        """Remove a key.
+
+        :param key: The key to be removed.
+        """
+        return self.__delitem__(key, _index_operation=False)
+
+    def pop(self, key=None, default=_missing):
+        """Removes and returns a key or index.
+
+        :param key: The key to be popped.  If this is an integer the item at
+                    that position is removed, if it's a string the value for
+                    that key is.  If the key is omitted or `None` the last
+                    item is removed.
+        :return: an item.
+        """
+        if key is None:
+            return self._list.pop()
+        if isinstance(key, (int, long)):
+            return self._list.pop(key)
+        try:
+            rv = self[key]
+            self.remove(key)
+        except KeyError:
+            if default is not _missing:
+                return default
+            raise
+        return rv
+
+    def popitem(self):
+        """Removes a key or index and returns a (key, value) item."""
+        return self.pop()
+
+    def __contains__(self, key):
+        """Check if a key is present."""
+        try:
+            self.__getitem__(key, _index_operation=False)
+        except KeyError:
+            return False
+        return True
+
+    has_key = __contains__
+
+    def __iter__(self):
+        """Yield ``(key, value)`` tuples."""
+        return iter(self._list)
+
+    def __len__(self):
+        return len(self._list)
+
+    def add(self, _key, _value, **kw):
+        """Add a new header tuple to the list.
+
+        Keyword arguments can specify additional parameters for the header
+        value, with underscores converted to dashes::
+
+        >>> d = Headers()
+        >>> d.add('Content-Type', 'text/plain')
+        >>> d.add('Content-Disposition', 'attachment', filename='foo.png')
+
+        The keyword argument dumping uses :func:`dump_options_header`
+        behind the scenes.
+
+        .. versionadded:: 0.4.1
+            keyword arguments were added for :mod:`wsgiref` compatibility.
+        """
+        self._list.append((_key, _options_header_vkw(_value, kw)))
+
+    def add_header(self, _key, _value, **_kw):
+        """Add a new header tuple to the list.
+
+        An alias for :meth:`add` for compatibility with the :mod:`wsgiref`
+        :meth:`~wsgiref.headers.Headers.add_header` method.
+        """
+        self.add(_key, _value, **_kw)
+
+    def clear(self):
+        """Clears all headers."""
+        del self._list[:]
+
+    def set(self, _key, _value, **kw):
+        """Remove all header tuples for `key` and add a new one.  The newly
+        added key either appears at the end of the list if there was no
+        entry or replaces the first one.
+
+        Keyword arguments can specify additional parameters for the header
+        value, with underscores converted to dashes.  See :meth:`add` for
+        more information.
+
+        .. versionchanged:: 0.6.1
+           :meth:`set` now accepts the same arguments as :meth:`add`.
+
+        :param key: The key to be inserted.
+        :param value: The value to be inserted.
+        """
+        lc_key = _key.lower()
+        _value = _options_header_vkw(_value, kw)
+        for idx, (old_key, old_value) in enumerate(self._list):
+            if old_key.lower() == lc_key:
+                # replace first ocurrence
+                self._list[idx] = (_key, _value)
+                break
+        else:
+            return self.add(_key, _value)
+        self._list[idx + 1:] = [(k, v) for k, v in self._list[idx + 1:]
+                                if k.lower() != lc_key]
+
+    def setdefault(self, key, value):
+        """Returns the value for the key if it is in the dict, otherwise it
+        returns `default` and sets that value for `key`.
+
+        :param key: The key to be looked up.
+        :param default: The default value to be returned if the key is not
+                        in the dict.  If not further specified it's `None`.
+        """
+        if key in self:
+            return self[key]
+        self.set(key, value)
+        return value
+
+    def __setitem__(self, key, value):
+        """Like :meth:`set` but also supports index/slice based setting."""
+        if isinstance(key, (slice, int, long)):
+            self._list[key] = value
+        else:
+            self.set(key, value)
+
+    def to_list(self, charset='utf-8'):
+        """Convert the headers into a list and converts the unicode header
+        items to the specified charset.
+
+        :return: list
+        """
+        result = []
+        for k, v in self:
+            if isinstance(v, unicode):
+                v = v.encode(charset)
+            else:
+                v = str(v)
+            result.append((k, v))
+        return result
+
+    def copy(self):
+        return self.__class__(self._list)
+
+    def __copy__(self):
+        return self.copy()
+
+    def __str__(self, charset='utf-8'):
+        """Returns formatted headers suitable for HTTP transmission."""
+        strs = []
+        for key, value in self.to_list(charset):
+            strs.append('%s: %s' % (key, value))
+        strs.append('\r\n')
+        return '\r\n'.join(strs)
+
+    def __repr__(self):
+        return '%s(%r)' % (
+            self.__class__.__name__,
+            list(self)
+        )
+
+
+class ImmutableHeadersMixin(object):
+    """Makes a :class:`Headers` immutable.
+
+    .. versionadded:: 0.5
+
+    :private:
+    """
+
+    def __delitem__(self, key):
+        is_immutable(self)
+
+    def __setitem__(self, key, value):
+        is_immutable(self)
+    set = __setitem__
+
+    def add(self, item):
+        is_immutable(self)
+    remove = add_header = add
+
+    def extend(self, iterable):
+        is_immutable(self)
+
+    def insert(self, pos, value):
+        is_immutable(self)
+
+    def pop(self, index=-1):
+        is_immutable(self)
+
+    def popitem(self):
+        is_immutable(self)
+
+    def setdefault(self, key, default):
+        is_immutable(self)
+
+
+class EnvironHeaders(ImmutableHeadersMixin, Headers):
+    """Read only version of the headers from a WSGI environment.  This
+    provides the same interface as `Headers` and is constructed from
+    a WSGI environment.
+
+    From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
+    subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
+    render a page for a ``400 BAD REQUEST`` if caught in a catch-all for
+    HTTP exceptions.
+    """
+
+    def __init__(self, environ):
+        self.environ = environ
+
+    @classmethod
+    def linked(cls, environ):
+        raise TypeError('%r object is always linked to environment, '
+                        'no separate initializer' % cls.__name__)
+
+    def __eq__(self, other):
+        return self.environ is other.environ
+
+    def __getitem__(self, key, _index_operation=False):
+        # _index_operation is a no-op for this class as there is no index but
+        # used because get() calls it.
+        key = key.upper().replace('-', '_')
+        if key in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
+            return self.environ[key]
+        return self.environ['HTTP_' + key]
+
+    def __len__(self):
+        # the iter is necessary because otherwise list calls our
+        # len which would call list again and so forth.
+        return len(list(iter(self)))
+
+    def __iter__(self):
+        for key, value in self.environ.iteritems():
+            if key.startswith('HTTP_') and key not in \
+               ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
+                yield key[5:].replace('_', '-').title(), value
+            elif key in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
+                yield key.replace('_', '-').title(), value
+
+    def copy(self):
+        raise TypeError('cannot create %r copies' % self.__class__.__name__)
+
+
+class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict):
+    """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict`
+    instances as sequence and it will combine the return values of all wrapped
+    dicts:
+
+    >>> from werkzeug import MultiDict, CombinedMultiDict
+    >>> post = MultiDict([('foo', 'bar')])
+    >>> get = MultiDict([('blub', 'blah')])
+    >>> combined = CombinedMultiDict([get, post])
+    >>> combined['foo']
+    'bar'
+    >>> combined['blub']
+    'blah'
+
+    This works for all read operations and will raise a `TypeError` for
+    methods that usually change data which isn't possible.
+
+    From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
+    subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
+    render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
+    exceptions.
+    """
+
+    def __reduce_ex__(self, protocol):
+        return type(self), (self.dicts,)
+
+    def __init__(self, dicts=None):
+        self.dicts = dicts or []
+
+    @classmethod
+    def fromkeys(cls):
+        raise TypeError('cannot create %r instances by fromkeys' %
+                        cls.__name__)
+
+    def __getitem__(self, key):
+        for d in self.dicts:
+            if key in d:
+                return d[key]
+        raise self.KeyError(key)
+
+    def get(self, key, default=None, type=None):
+        for d in self.dicts:
+            if key in d:
+                if type is not None:
+                    try:
+                        return type(d[key])
+                    except ValueError:
+                        continue
+                return d[key]
+        return default
+
+    def getlist(self, key, type=None):
+        rv = []
+        for d in self.dicts:
+            rv.extend(d.getlist(key, type))
+        return rv
+
+    def keys(self):
+        rv = set()
+        for d in self.dicts:
+            rv.update(d.keys())
+        return list(rv)
+
+    def iteritems(self, multi=False):
+        found = set()
+        for d in self.dicts:
+            for key, value in d.iteritems(multi):
+                if multi:
+                    yield key, value
+                elif key not in found:
+                    found.add(key)
+                    yield key, value
+
+    def itervalues(self):
+        for key, value in self.iteritems():
+            yield value
+
+    def values(self):
+        return list(self.itervalues())
+
+    def items(self, multi=False):
+        return list(self.iteritems(multi))
+
+    def iterlists(self):
+        rv = {}
+        for d in self.dicts:
+            for key, values in d.iterlists():
+                rv.setdefault(key, []).extend(values)
+        return rv.iteritems()
+
+    def lists(self):
+        return list(self.iterlists())
+
+    def iterlistvalues(self):
+        return (x[0] for x in self.lists())
+
+    def listvalues(self):
+        return list(self.iterlistvalues())
+
+    def iterkeys(self):
+        return iter(self.keys())
+
+    __iter__ = iterkeys
+
+    def copy(self):
+        """Return a shallow copy of this object."""
+        return self.__class__(self.dicts[:])
+
+    def to_dict(self, flat=True):
+        """Return the contents as regular dict.  If `flat` is `True` the
+        returned dict will only have the first item present, if `flat` is
+        `False` all values will be returned as lists.
+
+        :param flat: If set to `False` the dict returned will have lists
+                     with all the values in it.  Otherwise it will only
+                     contain the first item for each key.
+        :return: a :class:`dict`
+        """
+        rv = {}
+        for d in reversed(self.dicts):
+            rv.update(d.to_dict(flat))
+        return rv
+
+    def __len__(self):
+        return len(self.keys())
+
+    def __contains__(self, key):
+        for d in self.dicts:
+            if key in d:
+                return True
+        return False
+
+    has_key = __contains__
+
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, self.dicts)
+
+
+class FileMultiDict(MultiDict):
+    """A special :class:`MultiDict` that has convenience methods to add
+    files to it.  This is used for :class:`EnvironBuilder` and generally
+    useful for unittesting.
+
+    .. versionadded:: 0.5
+    """
+
+    def add_file(self, name, file, filename=None, content_type=None):
+        """Adds a new file to the dict.  `file` can be a file name or
+        a :class:`file`-like or a :class:`FileStorage` object.
+
+        :param name: the name of the field.
+        :param file: a filename or :class:`file`-like object
+        :param filename: an optional filename
+        :param content_type: an optional content type
+        """
+        if isinstance(file, FileStorage):
+            self[name] = file
+            return
+        if isinstance(file, basestring):
+            if filename is None:
+                filename = file
+            file = open(file, 'rb')
+        if filename and content_type is None:
+            content_type = mimetypes.guess_type(filename)[0] or \
+                           'application/octet-stream'
+        self[name] = FileStorage(file, filename, name, content_type)
+
+
+class ImmutableDict(ImmutableDictMixin, dict):
+    """An immutable :class:`dict`.
+
+    .. versionadded:: 0.5
+    """
+
+    __repr__ = _proxy_repr(dict)
+
+    def copy(self):
+        """Return a shallow mutable copy of this object.  Keep in mind that
+        the standard library's :func:`copy` function is a no-op for this class
+        like for any other python immutable type (eg: :class:`tuple`).
+        """
+        return dict(self)
+
+    def __copy__(self):
+        return self
+
+
+class ImmutableMultiDict(ImmutableMultiDictMixin, MultiDict):
+    """An immutable :class:`MultiDict`.
+
+    .. versionadded:: 0.5
+    """
+
+    def copy(self):
+        """Return a shallow mutable copy of this object.  Keep in mind that
+        the standard library's :func:`copy` function is a no-op for this class
+        like for any other python immutable type (eg: :class:`tuple`).
+        """
+        return MultiDict(self)
+
+    def __copy__(self):
+        return self
+
+
+class ImmutableOrderedMultiDict(ImmutableMultiDictMixin, OrderedMultiDict):
+    """An immutable :class:`OrderedMultiDict`.
+
+    .. versionadded:: 0.6
+    """
+
+    def copy(self):
+        """Return a shallow mutable copy of this object.  Keep in mind that
+        the standard library's :func:`copy` function is a no-op for this class
+        like for any other python immutable type (eg: :class:`tuple`).
+        """
+        return OrderedMultiDict(self)
+
+    def __copy__(self):
+        return self
+
+
+class Accept(ImmutableList):
+    """An :class:`Accept` object is just a list subclass for lists of
+    ``(value, quality)`` tuples.  It is automatically sorted by quality.
+
+    All :class:`Accept` objects work similar to a list but provide extra
+    functionality for working with the data.  Containment checks are
+    normalized to the rules of that header:
+
+    >>> a = CharsetAccept([('ISO-8859-1', 1), ('utf-8', 0.7)])
+    >>> a.best
+    'ISO-8859-1'
+    >>> 'iso-8859-1' in a
+    True
+    >>> 'UTF8' in a
+    True
+    >>> 'utf7' in a
+    False
+
+    To get the quality for an item you can use normal item lookup:
+
+    >>> print a['utf-8']
+    0.7
+    >>> a['utf7']
+    0
+
+    .. versionchanged:: 0.5
+       :class:`Accept` objects are forced immutable now.
+    """
+
+    def __init__(self, values=()):
+        if values is None:
+            list.__init__(self)
+            self.provided = False
+        elif isinstance(values, Accept):
+            self.provided = values.provided
+            list.__init__(self, values)
+        else:
+            self.provided = True
+            values = [(a, b) for b, a in values]
+            values.sort()
+            values.reverse()
+            list.__init__(self, [(a, b) for b, a in values])
+
+    def _value_matches(self, value, item):
+        """Check if a value matches a given accept item."""
+        return item == '*' or item.lower() == value.lower()
+
+    def __getitem__(self, key):
+        """Besides index lookup (getting item n) you can also pass it a string
+        to get the quality for the item.  If the item is not in the list, the
+        returned quality is ``0``.
+        """
+        if isinstance(key, basestring):
+            return self.quality(key)
+        return list.__getitem__(self, key)
+
+    def quality(self, key):
+        """Returns the quality of the key.
+
+        .. versionadded:: 0.6
+           In previous versions you had to use the item-lookup syntax
+           (eg: ``obj[key]`` instead of ``obj.quality(key)``)
+        """
+        for item, quality in self:
+            if self._value_matches(key, item):
+                return quality
+        return 0
+
+    def __contains__(self, value):
+        for item, quality in self:
+            if self._value_matches(value, item):
+                return True
+        return False
+
+    def __repr__(self):
+        return '%s([%s])' % (
+            self.__class__.__name__,
+            ', '.join('(%r, %s)' % (x, y) for x, y in self)
+        )
+
+    def index(self, key):
+        """Get the position of an entry or raise :exc:`ValueError`.
+
+        :param key: The key to be looked up.
+
+        .. versionchanged:: 0.5
+           This used to raise :exc:`IndexError`, which was inconsistent
+           with the list API.
+        """
+        if isinstance(key, basestring):
+            for idx, (item, quality) in enumerate(self):
+                if self._value_matches(key, item):
+                    return idx
+            raise ValueError(key)
+        return list.index(self, key)
+
+    def find(self, key):
+        """Get the position of an entry or return -1.
+
+        :param key: The key to be looked up.
+        """
+        try:
+            return self.index(key)
+        except ValueError:
+            return -1
+
+    def values(self):
+        """Return a list of the values, not the qualities."""
+        return list(self.itervalues())
+
+    def itervalues(self):
+        """Iterate over all values."""
+        for item in self:
+            yield item[0]
+
+    def to_header(self):
+        """Convert the header set into an HTTP header string."""
+        result = []
+        for value, quality in self:
+            if quality != 1:
+                value = '%s;q=%s' % (value, quality)
+            result.append(value)
+        return ','.join(result)
+
+    def __str__(self):
+        return self.to_header()
+
+    def best_match(self, matches, default=None):
+        """Returns the best match from a list of possible matches based
+        on the quality of the client.  If two items have the same quality,
+        the one is returned that comes first.
+
+        :param matches: a list of matches to check for
+        :param default: the value that is returned if none match
+        """
+        best_quality = -1
+        result = default
+        for server_item in matches:
+            for client_item, quality in self:
+                if quality <= best_quality:
+                    break
+                if self._value_matches(client_item, server_item):
+                    best_quality = quality
+                    result = server_item
+        return result
+
+    @property
+    def best(self):
+        """The best match as value."""
+        if self:
+            return self[0][0]
+
+
+class MIMEAccept(Accept):
+    """Like :class:`Accept` but with special methods and behavior for
+    mimetypes.
+    """
+
+    def _value_matches(self, value, item):
+        def _normalize(x):
+            x = x.lower()
+            return x == '*' and ('*', '*') or x.split('/', 1)
+
+        # this is from the application which is trusted.  to avoid developer
+        # frustration we actually check these for valid values
+        if '/' not in value:
+            raise ValueError('invalid mimetype %r' % value)
+        value_type, value_subtype = _normalize(value)
+        if value_type == '*' and value_subtype != '*':
+            raise ValueError('invalid mimetype %r' % value)
+
+        if '/' not in item:
+            return False
+        item_type, item_subtype = _normalize(item)
+        if item_type == '*' and item_subtype != '*':
+            return False
+        return (
+            (item_type == item_subtype == '*' or
+             value_type == value_subtype == '*') or
+            (item_type == value_type and (item_subtype == '*' or
+                                          value_subtype == '*' or
+                                          item_subtype == value_subtype))
+        )
+
+    @property
+    def accept_html(self):
+        """True if this object accepts HTML."""
+        return (
+            'text/html' in self or
+            'application/xhtml+xml' in self or
+            self.accept_xhtml
+        )
+
+    @property
+    def accept_xhtml(self):
+        """True if this object accepts XHTML."""
+        return (
+            'application/xhtml+xml' in self or
+            'application/xml' in self
+        )
+
+
+class LanguageAccept(Accept):
+    """Like :class:`Accept` but with normalization for languages."""
+
+    def _value_matches(self, value, item):
+        def _normalize(language):
+            return _locale_delim_re.split(language.lower())
+        return item == '*' or _normalize(value) == _normalize(item)
+
+
+class CharsetAccept(Accept):
+    """Like :class:`Accept` but with normalization for charsets."""
+
+    def _value_matches(self, value, item):
+        def _normalize(name):
+            try:
+                return codecs.lookup(name).name
+            except LookupError:
+                return name.lower()
+        return item == '*' or _normalize(value) == _normalize(item)
+
+
+def cache_property(key, empty, type):
+    """Return a new property object for a cache header.  Useful if you
+    want to add support for a cache extension in a subclass."""
+    return property(lambda x: x._get_cache_value(key, empty, type),
+                    lambda x, v: x._set_cache_value(key, v, type),
+                    lambda x: x._del_cache_value(key),
+                    'accessor for %r' % key)
+
+
+class _CacheControl(UpdateDictMixin, dict):
+    """Subclass of a dict that stores values for a Cache-Control header.  It
+    has accessors for all the cache-control directives specified in RFC 2616.
+    The class does not differentiate between request and response directives.
+
+    Because the cache-control directives in the HTTP header use dashes the
+    python descriptors use underscores for that.
+
+    To get a header of the :class:`CacheControl` object again you can convert
+    the object into a string or call the :meth:`to_header` method.  If you plan
+    to subclass it and add your own items have a look at the sourcecode for
+    that class.
+
+    .. versionchanged:: 0.4
+
+       Setting `no_cache` or `private` to boolean `True` will set the implicit
+       none-value which is ``*``:
+
+       >>> cc = ResponseCacheControl()
+       >>> cc.no_cache = True
+       >>> cc
+       <ResponseCacheControl 'no-cache'>
+       >>> cc.no_cache
+       '*'
+       >>> cc.no_cache = None
+       >>> cc
+       <ResponseCacheControl ''>
+
+       In versions before 0.5 the behavior documented here affected the now
+       no longer existing `CacheControl` class.
+    """
+
+    no_cache = cache_property('no-cache', '*', None)
+    no_store = cache_property('no-store', None, bool)
+    max_age = cache_property('max-age', -1, int)
+    no_transform = cache_property('no-transform', None, None)
+
+    def __init__(self, values=(), on_update=None):
+        dict.__init__(self, values or ())
+        self.on_update = on_update
+        self.provided = values is not None
+
+    def _get_cache_value(self, key, empty, type):
+        """Used internally by the accessor properties."""
+        if type is bool:
+            return key in self
+        if key in self:
+            value = self[key]
+            if value is None:
+                return empty
+            elif type is not None:
+                try:
+                    value = type(value)
+                except ValueError:
+                    pass
+            return value
+
+    def _set_cache_value(self, key, value, type):
+        """Used internally by the accessor properties."""
+        if type is bool:
+            if value:
+                self[key] = None
+            else:
+                self.pop(key, None)
+        else:
+            if value is None:
+                self.pop(key)
+            elif value is True:
+                self[key] = None
+            else:
+                self[key] = value
+
+    def _del_cache_value(self, key):
+        """Used internally by the accessor properties."""
+        if key in self:
+            del self[key]
+
+    def to_header(self):
+        """Convert the stored values into a cache control header."""
+        return dump_header(self)
+
+    def __str__(self):
+        return self.to_header()
+
+    def __repr__(self):
+        return '<%s %r>' % (
+            self.__class__.__name__,
+            self.to_header()
+        )
+
+
+class RequestCacheControl(ImmutableDictMixin, _CacheControl):
+    """A cache control for requests.  This is immutable and gives access
+    to all the request-relevant cache control headers.
+
+    To get a header of the :class:`RequestCacheControl` object again you can
+    convert the object into a string or call the :meth:`to_header` method.  If
+    you plan to subclass it and add your own items have a look at the sourcecode
+    for that class.
+
+    .. versionadded:: 0.5
+       In previous versions a `CacheControl` class existed that was used
+       both for request and response.
+    """
+
+    max_stale = cache_property('max-stale', '*', int)
+    min_fresh = cache_property('min-fresh', '*', int)
+    no_transform = cache_property('no-transform', None, None)
+    only_if_cached = cache_property('only-if-cached', None, bool)
+
+
+class ResponseCacheControl(_CacheControl):
+    """A cache control for responses.  Unlike :class:`RequestCacheControl`
+    this is mutable and gives access to response-relevant cache control
+    headers.
+
+    To get a header of the :class:`ResponseCacheControl` object again you can
+    convert the object into a string or call the :meth:`to_header` method.  If
+    you plan to subclass it and add your own items have a look at the sourcecode
+    for that class.
+
+    .. versionadded:: 0.5
+       In previous versions a `CacheControl` class existed that was used
+       both for request and response.
+    """
+
+    public = cache_property('public', None, bool)
+    private = cache_property('private', '*', None)
+    must_revalidate = cache_property('must-revalidate', None, bool)
+    proxy_revalidate = cache_property('proxy-revalidate', None, bool)
+    s_maxage = cache_property('s-maxage', None, None)
+
+
+# attach cache_property to the _CacheControl as staticmethod
+# so that others can reuse it.
+_CacheControl.cache_property = staticmethod(cache_property)
+
+
+class CallbackDict(UpdateDictMixin, dict):
+    """A dict that calls a function passed every time something is changed.
+    The function is passed the dict instance.
+    """
+
+    def __init__(self, initial=None, on_update=None):
+        dict.__init__(self, initial or ())
+        self.on_update = on_update
+
+    def __repr__(self):
+        return '<%s %s>' % (
+            self.__class__.__name__,
+            dict.__repr__(self)
+        )
+
+
+class HeaderSet(object):
+    """Similar to the :class:`ETags` class this implements a set-like structure.
+    Unlike :class:`ETags` this is case insensitive and used for vary, allow, and
+    content-language headers.
+
+    If not constructed using the :func:`parse_set_header` function the
+    instantiation works like this:
+
+    >>> hs = HeaderSet(['foo', 'bar', 'baz'])
+    >>> hs
+    HeaderSet(['foo', 'bar', 'baz'])
+    """
+
+    def __init__(self, headers=None, on_update=None):
+        self._headers = list(headers or ())
+        self._set = set([x.lower() for x in self._headers])
+        self.on_update = on_update
+
+    def add(self, header):
+        """Add a new header to the set."""
+        self.update((header,))
+
+    def remove(self, header):
+        """Remove a header from the set.  This raises an :exc:`KeyError` if the
+        header is not in the set.
+
+        .. versionchanged:: 0.5
+            In older versions a :exc:`IndexError` was raised instead of a
+            :exc:`KeyError` if the object was missing.
+
+        :param header: the header to be removed.
+        """
+        key = header.lower()
+        if key not in self._set:
+            raise KeyError(header)
+        self._set.remove(key)
+        for idx, key in enumerate(self._headers):
+            if key.lower() == header:
+                del self._headers[idx]
+                break
+        if self.on_update is not None:
+            self.on_update(self)
+
+    def update(self, iterable):
+        """Add all the headers from the iterable to the set.
+
+        :param iterable: updates the set with the items from the iterable.
+        """
+        inserted_any = False
+        for header in iterable:
+            key = header.lower()
+            if key not in self._set:
+                self._headers.append(header)
+                self._set.add(key)
+                inserted_any = True
+        if inserted_any and self.on_update is not None:
+            self.on_update(self)
+
+    def discard(self, header):
+        """Like :meth:`remove` but ignores errors.
+
+        :param header: the header to be discarded.
+        """
+        try:
+            return self.remove(header)
+        except KeyError:
+            pass
+
+    def find(self, header):
+        """Return the index of the header in the set or return -1 if not found.
+
+        :param header: the header to be looked up.
+        """
+        header = header.lower()
+        for idx, item in enumerate(self._headers):
+            if item.lower() == header:
+                return idx
+        return -1
+
+    def index(self, header):
+        """Return the index of the header in the set or raise an
+        :exc:`IndexError`.
+
+        :param header: the header to be looked up.
+        """
+        rv = self.find(header)
+        if rv < 0:
+            raise IndexError(header)
+        return rv
+
+    def clear(self):
+        """Clear the set."""
+        self._set.clear()
+        del self._headers[:]
+        if self.on_update is not None:
+            self.on_update(self)
+
+    def as_set(self, preserve_casing=False):
+        """Return the set as real python set type.  When calling this, all
+        the items are converted to lowercase and the ordering is lost.
+
+        :param preserve_casing: if set to `True` the items in the set returned
+                                will have the original case like in the
+                                :class:`HeaderSet`, otherwise they will
+                                be lowercase.
+        """
+        if preserve_casing:
+            return set(self._headers)
+        return set(self._set)
+
+    def to_header(self):
+        """Convert the header set into an HTTP header string."""
+        return ', '.join(map(quote_header_value, self._headers))
+
+    def __getitem__(self, idx):
+        return self._headers[idx]
+
+    def __delitem__(self, idx):
+        rv = self._headers.pop(idx)
+        self._set.remove(rv.lower())
+        if self.on_update is not None:
+            self.on_update(self)
+
+    def __setitem__(self, idx, value):
+        old = self._headers[idx]
+        self._set.remove(old.lower())
+        self._headers[idx] = value
+        self._set.add(value.lower())
+        if self.on_update is not None:
+            self.on_update(self)
+
+    def __contains__(self, header):
+        return header.lower() in self._set
+
+    def __len__(self):
+        return len(self._set)
+
+    def __iter__(self):
+        return iter(self._headers)
+
+    def __nonzero__(self):
+        return bool(self._set)
+
+    def __str__(self):
+        return self.to_header()
+
+    def __repr__(self):
+        return '%s(%r)' % (
+            self.__class__.__name__,
+            self._headers
+        )
+
+
+class ETags(object):
+    """A set that can be used to check if one etag is present in a collection
+    of etags.
+    """
+
+    def __init__(self, strong_etags=None, weak_etags=None, star_tag=False):
+        self._strong = frozenset(not star_tag and strong_etags or ())
+        self._weak = frozenset(weak_etags or ())
+        self.star_tag = star_tag
+
+    def as_set(self, include_weak=False):
+        """Convert the `ETags` object into a python set.  Per default all the
+        weak etags are not part of this set."""
+        rv = set(self._strong)
+        if include_weak:
+            rv.update(self._weak)
+        return rv
+
+    def is_weak(self, etag):
+        """Check if an etag is weak."""
+        return etag in self._weak
+
+    def contains_weak(self, etag):
+        """Check if an etag is part of the set including weak and strong tags."""
+        return self.is_weak(etag) or self.contains(etag)
+
+    def contains(self, etag):
+        """Check if an etag is part of the set ignoring weak tags."""
+        if self.star_tag:
+            return True
+        return etag in self._strong
+
+    def contains_raw(self, etag):
+        """When passed a quoted tag it will check if this tag is part of the
+        set.  If the tag is weak it is checked against weak and strong tags,
+        otherwise strong only."""
+        etag, weak = unquote_etag(etag)
+        if weak:
+            return self.contains_weak(etag)
+        return self.contains(etag)
+
+    def to_header(self):
+        """Convert the etags set into a HTTP header string."""
+        if self.star_tag:
+            return '*'
+        return ', '.join(
+            ['"%s"' % x for x in self._strong] +
+            ['w/"%s"' % x for x in self._weak]
+        )
+
+    def __call__(self, etag=None, data=None, include_weak=False):
+        if [etag, data].count(None) != 1:
+            raise TypeError('either tag or data required, but at least one')
+        if etag is None:
+            etag = generate_etag(data)
+        if include_weak:
+            if etag in self._weak:
+                return True
+        return etag in self._strong
+
+    def __nonzero__(self):
+        return bool(self.star_tag or self._strong)
+
+    def __str__(self):
+        return self.to_header()
+
+    def __iter__(self):
+        return iter(self._strong)
+
+    def __contains__(self, etag):
+        return self.contains(etag)
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, str(self))
+
+
+class Authorization(ImmutableDictMixin, dict):
+    """Represents an `Authorization` header sent by the client.  You should
+    not create this kind of object yourself but use it when it's returned by
+    the `parse_authorization_header` function.
+
+    This object is a dict subclass and can be altered by setting dict items
+    but it should be considered immutable as it's returned by the client and
+    not meant for modifications.
+
+    .. versionchanged:: 0.5
+       This object became immutable.
+    """
+
+    def __init__(self, auth_type, data=None):
+        dict.__init__(self, data or {})
+        self.type = auth_type
+
+    username = property(lambda x: x.get('username'), doc='''
+        The username transmitted.  This is set for both basic and digest
+        auth all the time.''')
+    password = property(lambda x: x.get('password'), doc='''
+        When the authentication type is basic this is the password
+        transmitted by the client, else `None`.''')
+    realm = property(lambda x: x.get('realm'), doc='''
+        This is the server realm sent back for HTTP digest auth.''')
+    nonce = property(lambda x: x.get('nonce'), doc='''
+        The nonce the server sent for digest auth, sent back by the client.
+        A nonce should be unique for every 401 response for HTTP digest
+        auth.''')
+    uri = property(lambda x: x.get('uri'), doc='''
+        The URI from Request-URI of the Request-Line; duplicated because
+        proxies are allowed to change the Request-Line in transit.  HTTP
+        digest auth only.''')
+    nc = property(lambda x: x.get('nc'), doc='''
+        The nonce count value transmitted by clients if a qop-header is
+        also transmitted.  HTTP digest auth only.''')
+    cnonce = property(lambda x: x.get('cnonce'), doc='''
+        If the server sent a qop-header in the ``WWW-Authenticate``
+        header, the client has to provide this value for HTTP digest auth.
+        See the RFC for more details.''')
+    response = property(lambda x: x.get('response'), doc='''
+        A string of 32 hex digits computed as defined in RFC 2617, which
+        proves that the user knows a password.  Digest auth only.''')
+    opaque = property(lambda x: x.get('opaque'), doc='''
+        The opaque header from the server returned unchanged by the client.
+        It is recommended that this string be base64 or hexadecimal data.
+        Digest auth only.''')
+
+    @property
+    def qop(self):
+        """Indicates what "quality of protection" the client has applied to
+        the message for HTTP digest auth."""
+        def on_update(header_set):
+            if not header_set and 'qop' in self:
+                del self['qop']
+            elif header_set:
+                self['qop'] = header_set.to_header()
+        return parse_set_header(self.get('qop'), on_update)
+
+
+class WWWAuthenticate(UpdateDictMixin, dict):
+    """Provides simple access to `WWW-Authenticate` headers."""
+
+    #: list of keys that require quoting in the generated header
+    _require_quoting = frozenset(['domain', 'nonce', 'opaque', 'realm'])
+
+    def __init__(self, auth_type=None, values=None, on_update=None):
+        dict.__init__(self, values or ())
+        if auth_type:
+            self['__auth_type__'] = auth_type
+        self.on_update = on_update
+
+    def set_basic(self, realm='authentication required'):
+        """Clear the auth info and enable basic auth."""
+        dict.clear(self)
+        dict.update(self, {'__auth_type__': 'basic', 'realm': realm})
+        if self.on_update:
+            self.on_update(self)
+
+    def set_digest(self, realm, nonce, qop=('auth',), opaque=None,
+                   algorithm=None, stale=False):
+        """Clear the auth info and enable digest auth."""
+        d = {
+            '__auth_type__':    'digest',
+            'realm':            realm,
+            'nonce':            nonce,
+            'qop':              dump_header(qop)
+        }
+        if stale:
+            d['stale'] = 'TRUE'
+        if opaque is not None:
+            d['opaque'] = opaque
+        if algorithm is not None:
+            d['algorithm'] = algorithm
+        dict.clear(self)
+        dict.update(self, d)
+        if self.on_update:
+            self.on_update(self)
+
+    def to_header(self):
+        """Convert the stored values into a WWW-Authenticate header."""
+        d = dict(self)
+        auth_type = d.pop('__auth_type__', None) or 'basic'
+        return '%s %s' % (auth_type.title(), ', '.join([
+            '%s=%s' % (key, quote_header_value(value,
+                       allow_token=key not in self._require_quoting))
+            for key, value in d.iteritems()
+        ]))
+
+    def __str__(self):
+        return self.to_header()
+
+    def __repr__(self):
+        return '<%s %r>' % (
+            self.__class__.__name__,
+            self.to_header()
+        )
+
+    def auth_property(name, doc=None):
+        """A static helper function for subclasses to add extra authentication
+        system properties onto a class::
+
+            class FooAuthenticate(WWWAuthenticate):
+                special_realm = auth_property('special_realm')
+
+        For more information have a look at the sourcecode to see how the
+        regular properties (:attr:`realm` etc.) are implemented.
+        """
+        def _set_value(self, value):
+            if value is None:
+                self.pop(name, None)
+            else:
+                self[name] = str(value)
+        return property(lambda x: x.get(name), _set_value, doc=doc)
+
+    def _set_property(name, doc=None):
+        def fget(self):
+            def on_update(header_set):
+                if not header_set and name in self:
+                    del self[name]
+                elif header_set:
+                    self[name] = header_set.to_header()
+            return parse_set_header(self.get(name), on_update)
+        return property(fget, doc=doc)
+
+    type = auth_property('__auth_type__', doc='''
+        The type of the auth mechanism.  HTTP currently specifies
+        `Basic` and `Digest`.''')
+    realm = auth_property('realm', doc='''
+        A string to be displayed to users so they know which username and
+        password to use.  This string should contain at least the name of
+        the host performing the authentication and might additionally
+        indicate the collection of users who might have access.''')
+    domain = _set_property('domain', doc='''
+        A list of URIs that define the protection space.  If a URI is an
+        absolute path, it is relative to the canonical root URL of the
+        server being accessed.''')
+    nonce = auth_property('nonce', doc='''
+        A server-specified data string which should be uniquely generated
+        each time a 401 response is made.  It is recommended that this
+        string be base64 or hexadecimal data.''')
+    opaque = auth_property('opaque', doc='''
+        A string of data, specified by the server, which should be returned
+        by the client unchanged in the Authorization header of subsequent
+        requests with URIs in the same protection space.  It is recommended
+        that this string be base64 or hexadecimal data.''')
+    algorithm = auth_property('algorithm', doc='''
+        A string indicating a pair of algorithms used to produce the digest
+        and a checksum.  If this is not present it is assumed to be "MD5".
+        If the algorithm is not understood, the challenge should be ignored
+        (and a different one used, if there is more than one).''')
+    qop = _set_property('qop', doc='''
+        A set of quality-of-privacy directives such as auth and auth-int.''')
+
+    def _get_stale(self):
+        val = self.get('stale')
+        if val is not None:
+            return val.lower() == 'true'
+    def _set_stale(self, value):
+        if value is None:
+            self.pop('stale', None)
+        else:
+            self['stale'] = value and 'TRUE' or 'FALSE'
+    stale = property(_get_stale, _set_stale, doc='''
+        A flag, indicating that the previous request from the client was
+        rejected because the nonce value was stale.''')
+    del _get_stale, _set_stale
+
+    # make auth_property a staticmethod so that subclasses of
+    # `WWWAuthenticate` can use it for new properties.
+    auth_property = staticmethod(auth_property)
+    del _set_property
+
+
+class FileStorage(object):
+    """The :class:`FileStorage` class is a thin wrapper over incoming files.
+    It is used by the request object to represent uploaded files.  All the
+    attributes of the wrapper stream are proxied by the file storage so
+    it's possible to do ``storage.read()`` instead of the long form
+    ``storage.stream.read()``.
+    """
+
+    def __init__(self, stream=None, filename=None, name=None,
+                 content_type='application/octet-stream', content_length=-1,
+                 headers=None):
+        self.name = name
+        self.stream = stream or _empty_stream
+        self.filename = filename or getattr(stream, 'name', None)
+        self.content_type = content_type
+        self.content_length = content_length
+        if headers is None:
+            headers = Headers()
+        self.headers = headers
+
+    def save(self, dst, buffer_size=16384):
+        """Save the file to a destination path or file object.  If the
+        destination is a file object you have to close it yourself after the
+        call.  The buffer size is the number of bytes held in memory during
+        the copy process.  It defaults to 16KB.
+
+        For secure file saving also have a look at :func:`secure_filename`.
+
+        :param dst: a filename or open file object the uploaded file
+                    is saved to.
+        :param buffer_size: the size of the buffer.  This works the same as
+                            the `length` parameter of
+                            :func:`shutil.copyfileobj`.
+        """
+        from shutil import copyfileobj
+        close_dst = False
+        if isinstance(dst, basestring):
+            dst = file(dst, 'wb')
+            close_dst = True
+        try:
+            copyfileobj(self.stream, dst, buffer_size)
+        finally:
+            if close_dst:
+                dst.close()
+
+    def close(self):
+        """Close the underlying file if possible."""
+        try:
+            self.stream.close()
+        except:
+            pass
+
+    def __nonzero__(self):
+        return bool(self.filename)
+
+    def __getattr__(self, name):
+        return getattr(self.stream, name)
+
+    def __iter__(self):
+        return iter(self.readline, '')
+
+    def __repr__(self):
+        return '<%s: %r (%r)>' % (
+            self.__class__.__name__,
+            self.filename,
+            self.content_type
+        )
+
+
+# circular dependencies
+from werkzeug.http import dump_options_header, dump_header, generate_etag, \
+     quote_header_value, parse_set_header, unquote_etag
+
+
+# create all the special key errors now that the classes are defined.
+from werkzeug.exceptions import BadRequest
+for _cls in MultiDict, OrderedMultiDict, CombinedMultiDict, Headers, \
+            EnvironHeaders:
+    _cls.KeyError = BadRequest.wrap(KeyError, _cls.__name__ + '.KeyError')
+del _cls
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/__init__.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.debug
+    ~~~~~~~~~~~~~~
+
+    WSGI application traceback debugger.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import mimetypes
+from os.path import join, dirname, basename, isfile
+from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response
+from werkzeug.debug.repr import debug_repr
+from werkzeug.debug.tbtools import get_current_traceback
+from werkzeug.debug.console import Console
+from werkzeug.debug.utils import render_template
+
+
+class _ConsoleFrame(object):
+    """Helper class so that we can reuse the frame console code for the
+    standalone console.
+    """
+
+    def __init__(self, namespace):
+        self.console = Console(namespace)
+        self.id = 0
+
+
+class DebuggedApplication(object):
+    """Enables debugging support for a given application::
+
+        from werkzeug.debug import DebuggedApplication
+        from myapp import app
+        app = DebuggedApplication(app, evalex=True)
+
+    The `evalex` keyword argument allows evaluating expressions in a
+    traceback's frame context.
+
+    :param app: the WSGI application to run debugged.
+    :param evalex: enable exception evaluation feature (interactive
+                   debugging).  This requires a non-forking server.
+    :param request_key: The key that points to the request object in ths
+                        environment.  This parameter is ignored in current
+                        versions.
+    :param console_path: the URL for a general purpose console.
+    :param console_init_func: the function that is executed before starting
+                              the general purpose console.  The return value
+                              is used as initial namespace.
+    :param show_hidden_frames: by default hidden traceback frames are skipped.
+                               You can show them by setting this parameter
+                               to `True`.
+    """
+
+    # this class is public
+    __module__ = 'werkzeug'
+
+    def __init__(self, app, evalex=False, request_key='werkzeug.request',
+                 console_path='/console', console_init_func=None,
+                 show_hidden_frames=False):
+        if not console_init_func:
+            console_init_func = dict
+        self.app = app
+        self.evalex = evalex
+        self.frames = {}
+        self.tracebacks = {}
+        self.request_key = request_key
+        self.console_path = console_path
+        self.console_init_func = console_init_func
+        self.show_hidden_frames = show_hidden_frames
+
+    def debug_application(self, environ, start_response):
+        """Run the application and conserve the traceback frames."""
+        app_iter = None
+        try:
+            app_iter = self.app(environ, start_response)
+            for item in app_iter:
+                yield item
+            if hasattr(app_iter, 'close'):
+                app_iter.close()
+        except:
+            if hasattr(app_iter, 'close'):
+                app_iter.close()
+            traceback = get_current_traceback(skip=1, show_hidden_frames=
+                                              self.show_hidden_frames,
+                                              ignore_system_exceptions=True)
+            for frame in traceback.frames:
+                self.frames[frame.id] = frame
+            self.tracebacks[traceback.id] = traceback
+
+            try:
+                start_response('500 INTERNAL SERVER ERROR', [
+                    ('Content-Type', 'text/html; charset=utf-8')
+                ])
+            except:
+                # if we end up here there has been output but an error
+                # occurred.  in that situation we can do nothing fancy any
+                # more, better log something into the error log and fall
+                # back gracefully.
+                environ['wsgi.errors'].write(
+                    'Debugging middleware caught exception in streamed '
+                    'response at a point where response headers were already '
+                    'sent.\n')
+            else:
+                yield traceback.render_full(evalex=self.evalex) \
+                               .encode('utf-8', 'replace')
+
+            traceback.log(environ['wsgi.errors'])
+
+    def execute_command(self, request, command, frame):
+        """Execute a command in a console."""
+        return Response(frame.console.eval(command), mimetype='text/html')
+
+    def display_console(self, request):
+        """Display a standalone shell."""
+        if 0 not in self.frames:
+            self.frames[0] = _ConsoleFrame(self.console_init_func())
+        return Response(render_template('console.html'), mimetype='text/html')
+
+    def paste_traceback(self, request, traceback):
+        """Paste the traceback and return a JSON response."""
+        paste_id = traceback.paste()
+        return Response('{"url": "http://paste.pocoo.org/show/%s/", "id": %s}'
+                        % (paste_id, paste_id), mimetype='application/json')
+
+    def get_source(self, request, frame):
+        """Render the source viewer."""
+        return Response(frame.render_source(), mimetype='text/html')
+
+    def get_resource(self, request, filename):
+        """Return a static resource from the shared folder."""
+        filename = join(dirname(__file__), 'shared', basename(filename))
+        if isfile(filename):
+            mimetype = mimetypes.guess_type(filename)[0] \
+                or 'application/octet-stream'
+            f = file(filename, 'rb')
+            try:
+                return Response(f.read(), mimetype=mimetype)
+            finally:
+                f.close()
+        return Response('Not Found', status=404)
+
+    def __call__(self, environ, start_response):
+        """Dispatch the requests."""
+        # important: don't ever access a function here that reads the incoming
+        # form data!  Otherwise the application won't have access to that data
+        # any more!
+        request = Request(environ)
+        response = self.debug_application
+        if self.evalex and self.console_path is not None and \
+           request.path == self.console_path:
+            response = self.display_console(request)
+        elif request.path.rstrip('/').endswith('/__debugger__'):
+            cmd = request.args.get('cmd')
+            arg = request.args.get('f')
+            traceback = self.tracebacks.get(request.args.get('tb', type=int))
+            frame = self.frames.get(request.args.get('frm', type=int))
+            if cmd == 'resource' and arg:
+                response = self.get_resource(request, arg)
+            elif cmd == 'paste' and traceback is not None:
+                response = self.paste_traceback(request, traceback)
+            elif cmd == 'source' and frame:
+                response = self.get_source(request, frame)
+            elif self.evalex and cmd is not None and frame is not None:
+                response = self.execute_command(request, cmd, frame)
+        return response(environ, start_response)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/console.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,201 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.debug.console
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Interactive console support.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD.
+"""
+import sys
+import code
+from types import CodeType
+from werkzeug.utils import escape
+from werkzeug.local import Local
+from werkzeug.debug.repr import debug_repr, dump, helper
+from werkzeug.debug.utils import render_template
+
+
+_local = Local()
+
+
+class HTMLStringO(object):
+    """A StringO version that HTML escapes on write."""
+
+    def __init__(self):
+        self._buffer = []
+
+    def isatty(self):
+        return False
+
+    def close(self):
+        pass
+
+    def flush(self):
+        pass
+
+    def seek(self, n, mode=0):
+        raise IOError('Bad file descriptor')
+
+    def readline(self):
+        raise IOError('Bad file descriptor')
+
+    def reset(self):
+        val = ''.join(self._buffer)
+        del self._buffer[:]
+        return val
+
+    def _write(self, x):
+        if isinstance(x, str):
+            x = x.decode('utf-8', 'replace')
+        self._buffer.append(x)
+
+    def write(self, x):
+        self._write(escape(x))
+
+    def writelines(self, x):
+        self._write(escape(''.join(x)))
+
+
+class ThreadedStream(object):
+    """Thread-local wrapper for sys.stdout for the interactive console."""
+
+    def push():
+        if not isinstance(sys.stdout, ThreadedStream):
+            sys.stdout = ThreadedStream()
+        _local.stream = HTMLStringO()
+    push = staticmethod(push)
+
+    def fetch():
+        try:
+            stream = _local.stream
+        except AttributeError:
+            return ''
+        return stream.reset()
+    fetch = staticmethod(fetch)
+
+    def displayhook(obj):
+        try:
+            stream = _local.stream
+        except AttributeError:
+            return _displayhook(obj)
+        # stream._write bypasses escaping as debug_repr is
+        # already generating HTML for us.
+        if obj is not None:
+            stream._write(debug_repr(obj))
+    displayhook = staticmethod(displayhook)
+
+    def __setattr__(self, name, value):
+        raise AttributeError('read only attribute %s' % name)
+
+    def __dir__(self):
+        return dir(sys.__stdout__)
+
+    def __getattribute__(self, name):
+        if name == '__members__':
+            return dir(sys.__stdout__)
+        try:
+            stream = _local.stream
+        except AttributeError:
+            stream = sys.__stdout__
+        return getattr(stream, name)
+
+    def __repr__(self):
+        return repr(sys.__stdout__)
+
+
+# add the threaded stream as display hook
+_displayhook = sys.displayhook
+sys.displayhook = ThreadedStream.displayhook
+
+
+class _ConsoleLoader(object):
+
+    def __init__(self):
+        self._storage = {}
+
+    def register(self, code, source):
+        self._storage[id(code)] = source
+        # register code objects of wrapped functions too.
+        for var in code.co_consts:
+            if isinstance(var, CodeType):
+                self._storage[id(var)] = source
+
+    def get_source_by_code(self, code):
+        try:
+            return self._storage[id(code)]
+        except KeyError:
+            pass
+
+
+def _wrap_compiler(console):
+    compile = console.compile
+    def func(source, filename, symbol):
+        code = compile(source, filename, symbol)
+        console.loader.register(code, source)
+        return code
+    console.compile = func
+
+
+class _InteractiveConsole(code.InteractiveInterpreter):
+
+    def __init__(self, globals, locals):
+        code.InteractiveInterpreter.__init__(self, locals)
+        self.globals = dict(globals)
+        self.globals['dump'] = dump
+        self.globals['help'] = helper
+        self.globals['__loader__'] = self.loader = _ConsoleLoader()
+        self.more = False
+        self.buffer = []
+        _wrap_compiler(self)
+
+    def runsource(self, source):
+        source = source.rstrip() + '\n'
+        ThreadedStream.push()
+        prompt = self.more and '... ' or '>>> '
+        try:
+            source_to_eval = ''.join(self.buffer + [source])
+            if code.InteractiveInterpreter.runsource(self,
+               source_to_eval, '<debugger>', 'single'):
+                self.more = True
+                self.buffer.append(source)
+            else:
+                self.more = False
+                del self.buffer[:]
+        finally:
+            output = ThreadedStream.fetch()
+        return prompt + source + output
+
+    def runcode(self, code):
+        try:
+            exec code in self.globals, self.locals
+        except:
+            self.showtraceback()
+
+    def showtraceback(self):
+        from werkzeug.debug.tbtools import get_current_traceback
+        tb = get_current_traceback(skip=1)
+        sys.stdout._write(tb.render_summary())
+
+    def showsyntaxerror(self, filename=None):
+        from werkzeug.debug.tbtools import get_current_traceback
+        tb = get_current_traceback(skip=4)
+        sys.stdout._write(tb.render_summary())
+
+    def write(self, data):
+        sys.stdout.write(data)
+
+
+class Console(object):
+    """An interactive console."""
+
+    def __init__(self, globals=None, locals=None):
+        if locals is None:
+            locals = {}
+        if globals is None:
+            globals = {}
+        self._ipy = _InteractiveConsole(globals, locals)
+
+    def eval(self, code):
+        return self._ipy.runsource(code)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/render.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.debug.render
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Render the traceback debugging page.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import pprint
+from os.path import dirname, join
+
+from werkzeug.templates import Template
+
+
+def get_template(name):
+    return Template.from_file(join(dirname(__file__), 'shared', name),
+                              unicode_mode=False, errors='ignore')
+
+
+def load_resource(res):
+    try:
+        f = file(join(dirname(__file__), 'shared', res))
+    except IOError:
+        return ''
+    try:
+        return f.read()
+    finally:
+        f.close()
+
+
+t_body = get_template('body.tmpl')
+t_codetable = get_template('codetable.tmpl')
+t_vartable = get_template('vartable.tmpl')
+
+
+def code_table(frame):
+    from werkzeug.debug.util import Namespace
+    lines = []
+    lineno = frame['context_lineno']
+    if lineno is not None:
+        lineno += 1
+        for l in frame['pre_context']:
+            lines.append(Namespace(mode='pre', lineno=lineno, code=l))
+            lineno += 1
+        lines.append(Namespace(mode='cur', lineno=lineno,
+                               code=frame['context_line']))
+        lineno += 1
+        for l in frame['post_context']:
+            lines.append(Namespace(mode='post', lineno=lineno, code=l))
+            lineno += 1
+    else:
+        lines.append(Namespace(mode='cur', lineno=1,
+                               code='Sourcecode not available'))
+
+    return t_codetable.render(lines=lines)
+
+
+def var_table(var):
+    def safe_pformat(x):
+        try:
+            lines = pprint.pformat(x).splitlines()
+        except:
+            return '?'
+        tmp = []
+        for line in lines:
+            if len(line) > 79:
+                line = line[:79] + '...'
+            tmp.append(line)
+        return '\n'.join(tmp)
+
+    # dicts
+    if isinstance(var, dict) or hasattr(var, 'items'):
+        value = var.items()
+        if not value:
+            typ = 'empty'
+        else:
+            typ = 'dict'
+            value.sort()
+            value = [(repr(key), safe_pformat(val)) for key, val in value]
+
+    # lists
+    elif isinstance(var, list):
+        if not var:
+            typ = 'empty'
+        else:
+            typ = 'list'
+        value = [safe_pformat(item) for item in var]
+
+    # others
+    else:
+        typ = 'simple'
+        value = repr(var)
+
+    return t_vartable.render(type=typ, value=value)
+
+
+def debug_page(context):
+    tc = context.to_dict()
+    tc['var_table'] = var_table
+    tc['code_table'] = code_table
+    return t_body.render(tc)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/repr.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.debug.repr
+    ~~~~~~~~~~~~~~~~~~~
+
+    This module implements object representations for debugging purposes.
+    Unlike the default repr these reprs expose a lot more information and
+    produce HTML instead of ASCII.
+
+    Together with the CSS and JavaScript files of the debugger this gives
+    a colorful and more compact output.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD.
+"""
+import sys
+import re
+from traceback import format_exception_only
+try:
+    from collections import deque
+except ImportError: # pragma: no cover
+    deque = None
+from werkzeug.utils import escape
+from werkzeug.debug.utils import render_template
+
+
+missing = object()
+_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
+RegexType = type(_paragraph_re)
+
+
+def debug_repr(obj):
+    """Creates a debug repr of an object as HTML unicode string."""
+    return DebugReprGenerator().repr(obj)
+
+
+def dump(obj=missing):
+    """Print the object details to stdout._write (for the interactive
+    console of the web debugger.
+    """
+    gen = DebugReprGenerator()
+    if obj is missing:
+        rv = gen.dump_locals(sys._getframe(1).f_locals)
+    else:
+        rv = gen.dump_object(obj)
+    sys.stdout._write(rv)
+
+
+class _Helper(object):
+    """Displays an HTML version of the normal help, for the interactive
+    debugger only because it requires a patched sys.stdout.
+    """
+
+    def __call__(self, topic=None):
+        title = text = None
+        if topic is not None:
+            import pydoc
+            pydoc.help(topic)
+            rv = sys.stdout.reset().decode('utf-8', 'ignore')
+            paragraphs = _paragraph_re.split(rv)
+            if len(paragraphs) > 1:
+                title = paragraphs[0]
+                text = '\n\n'.join(paragraphs[1:])
+            else: # pragma: no cover
+                title = 'Help'
+                text = paragraphs[0]
+        rv = render_template('help_command.html', title=title, text=text)
+        sys.stdout._write(rv)
+
+helper = _Helper()
+
+
+def _add_subclass_info(inner, obj, base):
+    if isinstance(base, tuple):
+        for base in base:
+            if type(obj) is base:
+                return inner
+    elif type(obj) is base:
+        return inner
+    module = ''
+    if obj.__class__.__module__ not in ('__builtin__', 'exceptions'):
+        module = '<span class="module">%s.</span>' % obj.__class__.__module__
+    return '%s%s(%s)' % (module, obj.__class__.__name__, inner)
+
+
+class DebugReprGenerator(object):
+
+    def __init__(self):
+        self._stack = []
+
+    def _sequence_repr_maker(left, right, base=object(), limit=8):
+        def proxy(self, obj, recursive):
+            if recursive:
+                return _add_subclass_info(left + '...' + right, obj, base)
+            buf = [left]
+            have_extended_section = False
+            for idx, item in enumerate(obj):
+                if idx:
+                    buf.append(', ')
+                if idx == limit:
+                    buf.append('<span class="extended">')
+                    have_extended_section = True
+                buf.append(self.repr(item))
+            if have_extended_section:
+                buf.append('</span>')
+            buf.append(right)
+            return _add_subclass_info(u''.join(buf), obj, base)
+        return proxy
+
+    list_repr = _sequence_repr_maker('[', ']', list)
+    tuple_repr = _sequence_repr_maker('(', ')', tuple)
+    set_repr = _sequence_repr_maker('set([', '])', set)
+    frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset)
+    if deque is not None:
+        deque_repr = _sequence_repr_maker('<span class="module">collections.'
+                                          '</span>deque([', '])', deque)
+    del _sequence_repr_maker
+
+    def regex_repr(self, obj):
+        pattern = repr(obj.pattern).decode('string-escape', 'ignore')
+        if pattern[:1] == 'u':
+            pattern = 'ur' + pattern[1:]
+        else:
+            pattern = 'r' + pattern
+        return u're.compile(<span class="string regex">%s</span>)' % pattern
+
+    def string_repr(self, obj, limit=70):
+        buf = ['<span class="string">']
+        escaped = escape(obj)
+        a = repr(escaped[:limit])
+        b = repr(escaped[limit:])
+        if isinstance(obj, unicode):
+            buf.append('u')
+            a = a[1:]
+            b = b[1:]
+        if b != "''":
+            buf.extend((a[:-1], '<span class="extended">', b[1:], '</span>'))
+        else:
+            buf.append(a)
+        buf.append('</span>')
+        return _add_subclass_info(u''.join(buf), obj, (str, unicode))
+
+    def dict_repr(self, d, recursive, limit=5):
+        if recursive:
+            return _add_subclass_info(u'{...}', d, dict)
+        buf = ['{']
+        have_extended_section = False
+        for idx, (key, value) in enumerate(d.iteritems()):
+            if idx:
+                buf.append(', ')
+            if idx == limit - 1:
+                buf.append('<span class="extended">')
+                have_extended_section = True
+            buf.append('<span class="pair"><span class="key">%s</span>: '
+                       '<span class="value">%s</span></span>' %
+                       (self.repr(key), self.repr(value)))
+        if have_extended_section:
+            buf.append('</span>')
+        buf.append('}')
+        return _add_subclass_info(u''.join(buf), d, dict)
+
+    def object_repr(self, obj):
+        return u'<span class="object">%s</span>' % \
+               escape(repr(obj).decode('utf-8', 'replace'))
+
+    def dispatch_repr(self, obj, recursive):
+        if obj is helper:
+            return helper.get_help(None)
+        if isinstance(obj, (int, long, float, complex)):
+            return u'<span class="number">%r</span>' % obj
+        if isinstance(obj, basestring):
+            return self.string_repr(obj)
+        if isinstance(obj, RegexType):
+            return self.regex_repr(obj)
+        if isinstance(obj, list):
+            return self.list_repr(obj, recursive)
+        if isinstance(obj, tuple):
+            return self.tuple_repr(obj, recursive)
+        if isinstance(obj, set):
+            return self.set_repr(obj, recursive)
+        if isinstance(obj, frozenset):
+            return self.frozenset_repr(obj, recursive)
+        if isinstance(obj, dict):
+            return self.dict_repr(obj, recursive)
+        if deque is not None and isinstance(obj, deque):
+            return self.deque_repr(obj, recursive)
+        return self.object_repr(obj)
+
+    def fallback_repr(self):
+        try:
+            info = ''.join(format_exception_only(*sys.exc_info()[:2]))
+        except: # pragma: no cover
+            info = '?'
+        return u'<span class="brokenrepr">&lt;broken repr (%s)&gt;' \
+               u'</span>' % escape(info.decode('utf-8', 'ignore').strip())
+
+    def repr(self, obj):
+        recursive = False
+        for item in self._stack:
+            if item is obj:
+                recursive = True
+                break
+        self._stack.append(obj)
+        try:
+            try:
+                return self.dispatch_repr(obj, recursive)
+            except:
+                return self.fallback_repr()
+        finally:
+            self._stack.pop()
+
+    def dump_object(self, obj):
+        repr = items = None
+        if isinstance(obj, dict):
+            title = 'Contents of'
+            items = []
+            for key, value in obj.iteritems():
+                if not isinstance(key, basestring):
+                    items = None
+                    break
+                items.append((key, self.repr(value)))
+        if items is None:
+            items = []
+            repr = self.repr(obj)
+            for key in dir(obj):
+                try:
+                    items.append((key, self.repr(getattr(obj, key))))
+                except:
+                    pass
+            title = 'Details for'
+        title += ' ' + object.__repr__(obj)[1:-1]
+        return render_template('dump_object.html', items=items,
+                               title=title, repr=repr)
+
+    def dump_locals(self, d):
+        items = [(key, self.repr(value)) for key, value in d.items()]
+        return render_template('dump_object.html', items=items,
+                               title='Local variables in frame', repr=None)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/shared/body.tmpl	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>$escape(exception_type) in $escape(last_frame['basename']) (Werkzeug Debugger)</title>
+    <link rel="stylesheet" href="__traceback__?resource=style.css&amp;mimetype=text/css" type="text/css">
+    <script type="text/javascript" src="__traceback__?resource=jquery.js&amp;mimetype=text/javascript"></script>
+    <script type="text/javascript" src="__traceback__?resource=debugger.js&amp;mimetype=text/javascript"></script>
+  </head>
+  <body>
+    <div class="traceback_wrapper">
+      <h1>$escape(exception_type)</h1>
+      <p class="errormsg">$escape(exception_value)</p>
+
+      <p class="errorline">
+        $escape(last_frame['filename']) in
+        $escape(last_frame['function']),
+        line $last_frame['lineno']
+      </p>
+
+      <h2 onclick="changeTB()" class="tb">Traceback <span>(toggle raw view)</span></h2>
+      <div id="interactive">
+        <p class="text">A problem occurred in your Python WSGI application.
+          Here is the sequence of function calls leading up to the error, in the order
+          they occurred. Activate a code line to toggle context lines.</p>
+
+      <% for num, frame in enumerate(frames) %>
+        <div class="frame" id="frame-$num">
+          <h3 class="fn"><em>$escape(frame['function'])</em> in <tt>$escape(frame['filename'])</tt></h3>
+          <a class="locals" href="javascript:toggleFrameVars($num)">[inspect]</a>
+          <% if evalex %><a class="eval" href="javascript:toggleInterpreter($num)">[console]</a><% endif %>
+          $code_table(frame)
+          $var_table(frame['vars'])
+          <% if evalex %>
+            <form class="exec_code" action="">
+              <pre class="output">[console ready]</pre>
+              <input type="hidden" name="tb" value="$tb_uid">
+              <input type="hidden" name="frame" value="$frame['frame_uid']">
+              <input type="text" name="cmd" class="input" value="">
+            </form>
+          <% endif %>
+        </div>
+      <% endfor %>
+      </div>
+
+      <div id="plain">
+        <p class="text">Here is the plain Python traceback for copy and paste:</p>
+        <pre class="plain">$escape(plaintb)</pre>
+        <p class="text pastebininfo">
+          <a href="javascript:pasteIt()">Create a new Paste</a> with
+          this traceback in the lodgeit pastebin.
+        </p>
+      </div>
+
+      <% if req_vars %>
+        <h2>Request Data</h2>
+        <p class="text">The following list contains all important request variables.
+          Select a header to expand the list.</p>
+        <% for num, (key, info) in enumerate(req_vars) %>
+          <dl>
+            <dt onclick="toggleTableVars($num)">$escape(key)</dt>
+            <dd id="tvar-$num">$var_table(info)</dd>
+          </dl>
+        <% endfor %>
+      <% endif %>
+    </div>
+
+    <div id="footer">
+      Brought to you by <span class="arthur">DON'T PANIC</span>, your friendly
+      Werkzeug powered traceback interpreter.
+    </div>
+  </body>
+</html>
+
+<!-- Plain traceback:
+
+<%py
+  import re
+  print re.sub('-{2,}', '-', plaintb)
+%>
+-->
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/shared/codetable.tmpl	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,8 @@
+<table class="code">
+<% for line in lines %>
+  <tr class="$line.mode">
+    <td class="lineno">$line.lineno</td>
+    <td class="code">$line.code</td>
+  </tr>
+<% endfor %>
+</table>
Binary file bundled/werkzeug/werkzeug/debug/shared/console.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/shared/debugger.js	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,196 @@
+$(function() {
+  var sourceView = null;
+
+  /**
+   * if we are in console mode, show the console.
+   */
+  if (CONSOLE_MODE && EVALEX) {
+    openShell(null, $('div.console div.inner').empty(), 0);
+  }
+
+  $('div.traceback div.frame').each(function() {
+    var
+      target = $('pre', this)
+        .click(function() {
+          sourceButton.click();
+        }),
+      consoleNode = null, source = null,
+      frameID = this.id.substring(6);
+
+    /**
+     * Add an interactive console to the frames
+     */
+    if (EVALEX)
+      $('<img src="./__debugger__?cmd=resource&f=console.png">')
+        .attr('title', 'Open an interactive python shell in this frame')
+        .click(function() {
+          consoleNode = openShell(consoleNode, target, frameID);
+          return false;
+        })
+        .prependTo(target);
+
+    /**
+     * Show sourcecode
+     */
+    var sourceButton = $('<img src="./__debugger__?cmd=resource&f=source.png">')
+      .attr('title', 'Display the sourcecode for this frame')
+      .click(function() {
+        if (!sourceView)
+          $('h2', sourceView =
+            $('<div class="box"><h2>View Source</h2><div class="sourceview">' +
+              '<table></table></div>')
+              .insertBefore('div.explanation'))
+            .css('cursor', 'pointer')
+            .click(function() {
+              sourceView.slideUp('fast');
+            });
+        $.get('./__debugger__', {cmd: 'source', frm: frameID}, function(data) {
+          $('table', sourceView)
+            .replaceWith(data);
+          if (!sourceView.is(':visible'))
+            sourceView.slideDown('fast', function() {
+              focusSourceBlock();
+            });
+          else
+            focusSourceBlock();
+        });
+        return false;
+      })
+      .prependTo(target);
+  });
+
+  /**
+   * toggle traceback types on click.
+   */
+  $('h2.traceback').click(function() {
+    $(this).next().slideToggle('fast');
+    $('div.plain').slideToggle('fast');
+  }).css('cursor', 'pointer');
+  $('div.plain').hide();
+
+  /**
+   * Add extra info (this is here so that only users with JavaScript
+   * enabled see it.)
+   */
+  $('span.nojavascript')
+    .removeClass('nojavascript')
+    .html('<p>To switch between the interactive traceback and the plaintext ' +
+          'one, you can click on the "Traceback" headline.  From the text ' +
+          'traceback you can also create a paste of it. ' + (!EVALEX ? '' :
+          'For code execution mouse-over the frame you want to debug and ' +
+          'click on the console icon on the right side.' +
+          '<p>You can execute arbitrary Python code in the stack frames and ' +
+          'there are some extra helpers available for introspection:' +
+          '<ul><li><code>dump()</code> shows all variables in the frame' +
+          '<li><code>dump(obj)</code> dumps all that\'s known about the object</ul>'));
+
+  /**
+   * Add the pastebin feature
+   */
+  $('div.plain form')
+    .submit(function() {
+      var label = $('input[type="submit"]', this);
+      var old_val = label.val();
+      label.val('submitting...');
+      $.ajax({
+        dataType:     'json',
+        url:          './__debugger__',
+        data:         {tb: TRACEBACK, cmd: 'paste'},
+        success:      function(data) {
+          $('div.plain span.pastemessage')
+            .removeClass('pastemessage')
+            .text('Paste created: ')
+            .append($('<a>#' + data.id + '</a>').attr('href', data.url));
+        },
+        error:        function() {
+          alert('Error: Could not submit paste.  No network connection?');
+          label.val(old_val);
+        }
+      });
+      return false;
+    });
+
+  // if we have javascript we submit by ajax anyways, so no need for the
+  // not scaling textarea.
+  var plainTraceback = $('div.plain textarea');
+  plainTraceback.replaceWith($('<pre>').text(plainTraceback.text()));
+});
+
+
+/**
+ * Helper function for shell initialization
+ */
+function openShell(consoleNode, target, frameID) {
+  if (consoleNode)
+    return consoleNode.slideToggle('fast');
+  consoleNode = $('<pre class="console">')
+    .appendTo(target.parent())
+    .hide()
+  var historyPos = 0, history = [''];
+  var output = $('<div class="output">[console ready]</div>')
+    .appendTo(consoleNode);
+  var form = $('<form>&gt;&gt;&gt; </form>')
+    .submit(function() {
+      var cmd = command.val();
+      $.get('./__debugger__', {cmd: cmd, frm: frameID}, function(data) {
+        var tmp = $('<div>').html(data);
+        $('span.extended', tmp).each(function() {
+          var hidden = $(this).wrap('<span>').hide();
+          hidden
+            .parent()
+            .append($('<a href="#" class="toggle">&nbsp;&nbsp;</a>')
+              .click(function() {
+                hidden.toggle();
+                $(this).toggleClass('open')
+                return false;
+              }));
+        });
+        output.append(tmp);
+        command.focus();
+        var old = history.pop();
+        history.push(cmd);
+        if (typeof old != 'undefined')
+          history.push(old);
+        historyPos = history.length - 1;
+      });
+      command.val('');
+      return false;
+    }).
+    appendTo(consoleNode);
+
+  var command = $('<input type="text">')
+    .appendTo(form)
+    .keydown(function(e) {
+      if (e.charCode == 100 && e.ctrlKey) {
+        output.text('--- screen cleared ---');
+        return false;
+      }
+      else if (e.charCode == 0 && (e.keyCode == 38 || e.keyCode == 40)) {
+        if (e.keyCode == 38 && historyPos > 0)
+          historyPos--;
+        else if (e.keyCode == 40 && historyPos < history.length)
+          historyPos++;
+        command.val(history[historyPos]);
+        return false;
+      }
+    });
+    
+  return consoleNode.slideDown('fast', function() {
+    command.focus();
+  });
+}
+
+/**
+ * Focus the current block in the source view.
+ */
+function focusSourceBlock() {
+  var tmp, line = $('table.source tr.current');
+  for (var i = 0; i < 7; i++) {
+    tmp = line.prev();
+    if (!(tmp && tmp.is('.in-frame')))
+      break
+    line = tmp;
+  }
+  var container = $('div.sourceview')[0];
+  container.scrollTop = line.offset().top - container.offsetTop;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/shared/jquery.js	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,19 @@
+/*
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML='   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file
Binary file bundled/werkzeug/werkzeug/debug/shared/less.png has changed
Binary file bundled/werkzeug/werkzeug/debug/shared/more.png has changed
Binary file bundled/werkzeug/werkzeug/debug/shared/source.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/shared/style.css	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,93 @@
+body, input  { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+               'Verdana', sans-serif; background-color: #AFC1C4; color: #000;
+               text-align: center; margin: 1em; padding: 0; font-size: 15px; }
+input        { background-color: #fff; margin: 0; text-align: left; }
+a            { color: #11557C; }
+a:hover      { color: #177199; }
+pre, code, table.source,
+textarea     { font-family: 'Consolas', 'Deja Vu Sans Mono',
+               'Bitstream Vera Sans Mono', monospace; font-size: 13px; }
+
+div.debugger { text-align: left; padding: 12px; margin: auto;
+               border: 1px solid #aaa; background-color: white; }
+h1           { color: #11557C; font-size: 30px; margin: 0 0 0.3em 0; }
+div.detail p { margin: 0 0 8px 13px; font-size: 14px; }
+div.explanation { margin: 13px; font-size: 11px; }
+div.footer   { background-color: #E3EFF1; font-size: 0.8em; text-align: right;
+               padding: 6px 8px 6px 0; margin: 30px -12px -12px -12px;
+               color: #86989B; }
+
+h2           { font-size: 16px; margin: 1.3em 0 0.0 0; padding: 5px;
+               background-color: #11557C; color: white; }
+h2 em        { font-style: normal; color: #A5D6D9; font-weight: normal; }
+
+div.traceback, div.plain { background-color: #eee!important; border: 1px solid #ccc;
+                           margin: 0 0 1em 0; padding: 10px; }
+div.plain p      { margin: 0; }
+div.plain textarea,
+div.plain pre { margin: 10px 0 0 0; padding: 4px; background-color: #333;
+                border: 1px solid #111; color: white; }
+div.plain textarea { width: 99%; height: 300px; }
+div.traceback h3 { font-size: 1em; margin: 0 0 0.8em 0; }
+div.traceback ul { list-style: none; margin: 0; padding: 0 0 0 1em; }
+div.traceback h4 { font-size: 13px; font-weight: normal; margin: 0.7em 0 0.1em 0; }
+div.traceback pre { margin: 0; padding: 5px 0 3px 15px;
+                    background-color: #ccc; border-top: 1px solid #aaa;
+                    border-left: 1px solid #aaa; border-right: 1px solid #fafafa;
+                    border-bottom: 1px solid #fafafa; }
+div.traceback pre,
+div.box table.source { white-space: pre-wrap;       /* css-3 should we be so lucky... */
+                       white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+                       white-space: -pre-wrap;      /* Opera 4-6 ?? */
+                       white-space: -o-pre-wrap;    /* Opera 7 ?? */
+                       word-wrap: break-word;       /* Internet Explorer 5.5+ */
+                       _white-space: pre;           /* IE only hack to re-specify in
+                                                    addition to word-wrap  */ }
+div.traceback pre:hover { background-color: #fafafa; color: black; cursor: pointer; }
+div.traceback blockquote { margin: 1em 0 0 0; padding: 0; }
+div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; }
+div.traceback img:hover { background-color: #ddd; cursor: pointer; }
+div.traceback pre:hover img { display: block; }
+
+pre.console { background-color: #fafafa!important; color: black; padding: 5px!important;
+              margin: 3px 0 0 0!important; cursor: default!important;
+              max-height: 400px; overflow: auto; }
+pre.console form { color: #555; }
+pre.console input { background-color: #fafafa; color: #555;
+                    width: 90%; font-family: 'Consolas', 'Deja Vu Sans Mono',
+                    'Bitstream Vera Sans Mono', monospace; font-size: 13px;
+                     border: none!important; }
+
+span.string { color: #30799B; }
+span.number { color: #9C1A1C; }
+span.object { color: #485F6E; }
+span.extended { opacity: 0.5; }
+span.extended:hover { opacity: 1; }
+a.toggle { text-decoration: none; background-repeat: no-repeat;
+           background-position: center center;
+           background-image: url(./__debugger__?cmd=resource&f=more.png); }
+a.toggle:hover { background-color: #444; }
+a.open { background-image: url(./__debugger__?cmd=resource&f=less.png); }
+
+pre.console div.traceback { margin: 5px 0 5px 25px; white-space: normal; }
+pre.console div.traceback h3 { background-color: #555; color: white;
+                               margin: -10px -10px 5px -10px; padding: 5px; }
+pre.console div.traceback pre:hover { background-color: #ccc; cursor: default; }
+
+pre.console div.box { margin: 5px 0 5px 25px; white-space: normal;
+                      border: 1px solid #ddd; }
+pre.console div.box h3 { background-color: #555; color: white;
+                         margin: 0; padding: 5px; }
+pre.console div.box div.repr { padding: 8px; background-color: white; }
+pre.console div.box table { margin-top: 6px; }
+pre.console div.box pre.help { background-color: white; font-size: 12px; }
+pre.console div.box pre.help:hover { cursor: default; }
+pre.console table tr { vertical-align: top; }
+div.box table.source { background-color: #fafafa; font-size: 12px;
+                       border-collapse: collapse; width: 100%; }
+div.box table.source td { border-top: 1px solid #eee; padding: 4px 0 4px 10px; }
+div.box table.source td.lineno { color: #999; padding-right: 10px; width: 1px; }
+div.box table.source tr.in-frame { background-color: #D6EEFF; }
+div.box table.source tr.current { background-color: white; }
+div.sourceview { max-height: 400px; overflow: auto; border: 1px solid #ccc; }
+div.console { border: 1px solid #ccc; padding: 4px; background-color: #fafafa; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/shared/vartable.tmpl	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,16 @@
+<table class="vars">
+<% if type == 'empty' %>
+  <tr><th>no data given</th></tr>
+<% elif type == 'simple' %>
+  <tr><td class="value">$escape(value)</td></tr>
+<% elif type == 'dict' %>
+  <tr><th>Name</th><th>Value</th></tr>
+  <% for key, item in value %>
+  <tr><td class="name">$escape(key)</td><td class="value">$escape(item)</td></tr>
+  <% endfor %>
+<% elif type == 'list' %>
+  <% for item in value %>
+  <tr><td class="value">$escape(item)</td></tr>
+  <% endfor %>
+<% endif %>
+</table>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/tbtools.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,297 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.debug.tbtools
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    This module provides various traceback related utility functions.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD.
+"""
+import re
+import os
+import sys
+import inspect
+import traceback
+import codecs
+from tokenize import TokenError
+from werkzeug.utils import cached_property
+from werkzeug.debug.console import Console
+from werkzeug.debug.utils import render_template
+
+_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
+_line_re = re.compile(r'^(.*?)$(?m)')
+_funcdef_re = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
+UTF8_COOKIE = '\xef\xbb\xbf'
+
+system_exceptions = (SystemExit, KeyboardInterrupt)
+try:
+    system_exceptions += (GeneratorExit,)
+except NameError:
+    pass
+
+
+def get_current_traceback(ignore_system_exceptions=False,
+                          show_hidden_frames=False, skip=0):
+    """Get the current exception info as `Traceback` object.  Per default
+    calling this method will reraise system exceptions such as generator exit,
+    system exit or others.  This behavior can be disabled by passing `False`
+    to the function as first parameter.
+    """
+    exc_type, exc_value, tb = sys.exc_info()
+    if ignore_system_exceptions and exc_type in system_exceptions:
+        raise
+    for x in xrange(skip):
+        if tb.tb_next is None:
+            break
+        tb = tb.tb_next
+    tb = Traceback(exc_type, exc_value, tb)
+    if not show_hidden_frames:
+        tb.filter_hidden_frames()
+    return tb
+
+
+class Line(object):
+    """Helper for the source renderer."""
+    __slots__ = ('lineno', 'code', 'in_frame', 'current')
+
+    def __init__(self, lineno, code):
+        self.lineno = lineno
+        self.code = code
+        self.in_frame = False
+        self.current = False
+
+    def classes(self):
+        rv = ['line']
+        if self.in_frame:
+            rv.append('in-frame')
+        if self.current:
+            rv.append('current')
+        return rv
+    classes = property(classes)
+
+
+class Traceback(object):
+    """Wraps a traceback."""
+
+    def __init__(self, exc_type, exc_value, tb):
+        self.exc_type = exc_type
+        self.exc_value = exc_value
+        if not isinstance(exc_type, str):
+            exception_type = exc_type.__name__
+            if exc_type.__module__ not in ('__builtin__', 'exceptions'):
+                exception_type = exc_type.__module__ + '.' + exception_type
+        else:
+            exception_type = exc_type
+        self.exception_type = exception_type
+
+        # we only add frames to the list that are not hidden.  This follows
+        # the the magic variables as defined by paste.exceptions.collector
+        self.frames = []
+        while tb:
+            self.frames.append(Frame(exc_type, exc_value, tb))
+            tb = tb.tb_next
+
+    def filter_hidden_frames(self):
+        """Remove the frames according to the paste spec."""
+        new_frames = []
+        hidden = False
+        for frame in self.frames:
+            hide = frame.hide
+            if hide in ('before', 'before_and_this'):
+                new_frames = []
+                hidden = False
+                if hide == 'before_and_this':
+                    continue
+            elif hide in ('reset', 'reset_and_this'):
+                hidden = False
+                if hide == 'reset_and_this':
+                    continue
+            elif hide in ('after', 'after_and_this'):
+                hidden = True
+                if hide == 'after_and_this':
+                    continue
+            elif hide or hidden:
+                continue
+            new_frames.append(frame)
+
+        # if the last frame is missing something went terrible wrong :(
+        if self.frames[-1] in new_frames:
+            self.frames[:] = new_frames
+
+    def is_syntax_error(self):
+        """Is it a syntax error?"""
+        return isinstance(self.exc_value, SyntaxError)
+    is_syntax_error = property(is_syntax_error)
+
+    def exception(self):
+        """String representation of the exception."""
+        buf = traceback.format_exception_only(self.exc_type, self.exc_value)
+        return ''.join(buf).strip().decode('utf-8', 'replace')
+    exception = property(exception)
+
+    def log(self, logfile=None):
+        """Log the ASCII traceback into a file object."""
+        if logfile is None:
+            logfile = sys.stderr
+        tb = self.plaintext.encode('utf-8', 'replace').rstrip() + '\n'
+        logfile.write(tb)
+
+    def paste(self):
+        """Create a paste and return the paste id."""
+        from xmlrpclib import ServerProxy
+        srv = ServerProxy('http://paste.pocoo.org/xmlrpc/')
+        return srv.pastes.newPaste('pytb', self.plaintext)
+
+    def render_summary(self, include_title=True):
+        """Render the traceback for the interactive console."""
+        return render_template('traceback_summary.html', traceback=self,
+                               include_title=include_title)
+
+    def render_full(self, evalex=False):
+        """Render the Full HTML page with the traceback info."""
+        return render_template('traceback_full.html', traceback=self,
+                               evalex=evalex)
+
+    def plaintext(self):
+        return render_template('traceback_plaintext.html', traceback=self)
+    plaintext = cached_property(plaintext)
+
+    id = property(lambda x: id(x))
+
+
+class Frame(object):
+    """A single frame in a traceback."""
+
+    def __init__(self, exc_type, exc_value, tb):
+        self.lineno = tb.tb_lineno
+        self.function_name = tb.tb_frame.f_code.co_name
+        self.locals = tb.tb_frame.f_locals
+        self.globals = tb.tb_frame.f_globals
+
+        fn = inspect.getsourcefile(tb) or inspect.getfile(tb)
+        if fn[-4:] in ('.pyo', '.pyc'):
+            fn = fn[:-1]
+        # if it's a file on the file system resolve the real filename.
+        if os.path.isfile(fn):
+            fn = os.path.realpath(fn)
+        self.filename = fn
+        self.module = self.globals.get('__name__')
+        self.loader = self.globals.get('__loader__')
+        self.code = tb.tb_frame.f_code
+
+        # support for paste's traceback extensions
+        self.hide = self.locals.get('__traceback_hide__', False)
+        info = self.locals.get('__traceback_info__')
+        if info is not None:
+            try:
+                info = unicode(info)
+            except UnicodeError:
+                info = str(info).decode('utf-8', 'replace')
+        self.info = info
+
+    def render(self):
+        """Render a single frame in a traceback."""
+        return render_template('frame.html', frame=self)
+
+    def render_source(self):
+        """Render the sourcecode."""
+        lines = [Line(idx + 1, x) for idx, x in enumerate(self.sourcelines)]
+
+        # find function definition and mark lines
+        if hasattr(self.code, 'co_firstlineno'):
+            lineno = self.code.co_firstlineno - 1
+            while lineno > 0:
+                if _funcdef_re.match(lines[lineno].code):
+                    break
+                lineno -= 1
+            try:
+                offset = len(inspect.getblock([x.code + '\n' for x
+                                               in lines[lineno:]]))
+            except TokenError:
+                offset = 0
+            for line in lines[lineno:lineno + offset]:
+                line.in_frame = True
+
+        # mark current line
+        try:
+            lines[self.lineno - 1].current = True
+        except IndexError:
+            pass
+
+        return render_template('source.html', frame=self, lines=lines)
+
+    def eval(self, code, mode='single'):
+        """Evaluate code in the context of the frame."""
+        if isinstance(code, basestring):
+            if isinstance(code, unicode):
+                code = UTF8_COOKIE + code.encode('utf-8')
+            code = compile(code, '<interactive>', mode)
+        if mode != 'exec':
+            return eval(code, self.globals, self.locals)
+        exec code in self.globals, self.locals
+
+    @cached_property
+    def sourcelines(self):
+        """The sourcecode of the file as list of unicode strings."""
+        # get sourcecode from loader or file
+        source = None
+        if self.loader is not None:
+            try:
+                if hasattr(self.loader, 'get_source'):
+                    source = self.loader.get_source(self.module)
+                elif hasattr(self.loader, 'get_source_by_code'):
+                    source = self.loader.get_source_by_code(self.code)
+            except:
+                # we munch the exception so that we don't cause troubles
+                # if the loader is broken.
+                pass
+
+        if source is None:
+            try:
+                f = file(self.filename)
+            except IOError:
+                return []
+            try:
+                source = f.read()
+            finally:
+                f.close()
+
+        # already unicode?  return right away
+        if isinstance(source, unicode):
+            return source.splitlines()
+
+        # yes. it should be ascii, but we don't want to reject too many
+        # characters in the debugger if something breaks
+        charset = 'utf-8'
+        if source.startswith(UTF8_COOKIE):
+            source = source[3:]
+        else:
+            for idx, match in enumerate(_line_re.finditer(source)):
+                match = _line_re.search(match.group())
+                if match is not None:
+                    charset = match.group(1)
+                    break
+                if idx > 1:
+                    break
+
+        # on broken cookies we fall back to utf-8 too
+        try:
+            codecs.lookup(charset)
+        except LookupError:
+            charset = 'utf-8'
+
+        return source.decode(charset, 'replace').splitlines()
+
+    @property
+    def current_line(self):
+        try:
+            return self.sourcelines[self.lineno - 1]
+        except IndexError:
+            return u''
+
+    @cached_property
+    def console(self):
+        return Console(self.globals, self.locals)
+
+    id = property(lambda x: id(x))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/console.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>Console // Werkzeug Debugger</title>
+    <link rel="stylesheet" href="./__debugger__?cmd=resource&amp;f=style.css" type="text/css">
+    <script type="text/javascript" src="./__debugger__?cmd=resource&amp;f=jquery.js"></script>
+    <script type="text/javascript" src="./__debugger__?cmd=resource&amp;f=debugger.js"></script>
+    <script type="text/javascript">
+      var EVALEX = true,
+          CONSOLE_MODE = true;
+    </script>
+  </head>
+  <body>
+    <div class="debugger">
+      <h1>Interactive Console</h1>
+      <div class="explanation">
+        In this console you can execute Python expressions in the context of the
+        application.  The initial namespace was created by the debugger automatically.
+      </div>
+      <div class="console"><div class="inner">The Console requires JavaScript.</div></div>
+      <div class="footer">
+        Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
+        friendly Werkzeug powered traceback interpreter.
+      </div>
+    </div>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/dump_object.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,14 @@
+<div class="box">
+  <h3>$escape(title)</h3>
+  <% if repr %>
+    <div class="repr">$repr</div>
+  <% endif %>
+  <table>
+  <% for key, value in items %>
+    <tr>
+      <th>$escape(key)</th>
+      <td>$value</td>
+    </tr>
+  <% endfor %>
+  </table>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/frame.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,6 @@
+<div class="frame" id="frame-$frame.id">
+  <h4>File <cite class="filename">"$escape(frame.filename)"</cite>,
+      line <em class="line">$frame.lineno</em>,
+      in <code class="function">$escape(frame.function_name)</code></h4>
+  <pre>${escape(frame.current_line.strip())}</pre>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/help_command.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,10 @@
+<%py missing = object() %>
+<div class="box">
+  <% if title and text %>
+    <h3>$title</h3>
+    <pre class="help">$text</pre>
+  <% else %>
+    <h3>Help</h3>
+    <p>Type help(object) for help about object.</p>
+  <% endif %>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/source.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,8 @@
+<table class="source">
+<% for line in lines %>
+  <tr class="${' '.join(line.classes)}">
+    <td class="lineno">${line.lineno}</td>
+    <td>$escape(line.code)</td>
+  </tr>
+<% endfor %>
+</table>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/traceback_full.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>$escape(traceback.exception) // Werkzeug Debugger</title>
+    <link rel="stylesheet" href="./__debugger__?cmd=resource&amp;f=style.css" type="text/css">
+    <script type="text/javascript" src="./__debugger__?cmd=resource&amp;f=jquery.js"></script>
+    <script type="text/javascript" src="./__debugger__?cmd=resource&amp;f=debugger.js"></script>
+    <script type="text/javascript">
+      var TRACEBACK = $traceback.id,
+          CONSOLE_MODE = false,
+          EVALEX = ${evalex and 'true' or 'false'};
+    </script>
+  </head>
+  <body>
+    <div class="debugger">
+      <h1>$escape(traceback.exception_type)</h1>
+      <div class="detail">
+        <p class="errormsg">$escape(traceback.exception)</p>
+      </div>
+      <h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
+      $traceback.render_summary(include_title=False)
+      <div class="plain">
+        <form action="http://paste.pocoo.org/" method="post">
+          <p>
+            <input type="hidden" name="language" value="pytb">
+            This is the Copy/Paste friendly version of the traceback.  <span
+            class="pastemessage">You can also paste this traceback into the public
+            lodgeit pastebin: <input type="submit" value="create paste"></span>
+          </p>
+          <textarea cols="50" rows="10" name="code" readonly>$escape(traceback.plaintext)</textarea>
+        </form>
+      </div>
+      <div class="explanation">
+        The debugger caught an exception in your WSGI application.  You can now
+        look at the traceback which led to the error.  <span class="nojavascript">
+        If you enable JavaScript you can also use additional features such as code
+        execution (if the evalex feature is enabled), automatic pasting of the
+        exceptions and much more.</span>
+      </div>
+      <div class="footer">
+        Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
+        friendly Werkzeug powered traceback interpreter.
+      </div>
+    </div>
+  </body>
+</html>
+<!--
+
+<%py
+  import re
+  print re.sub('-{2,}', '-', traceback.plaintext)
+%>
+
+-->
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/traceback_plaintext.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,6 @@
+Traceback (most recent call last):
+<% for frame in traceback.frames %>
+  File "$frame.filename", line $frame.lineno, in $frame.function_name
+    $frame.current_line.strip()
+<% endfor %>
+$traceback.exception
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/templates/traceback_summary.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,23 @@
+<div class="traceback">
+  <% if traceback.is_syntax_error %>
+    <% if include_title %>
+      <h3>Syntax Error</h3>
+    <% endif %>
+    <ul>
+    <% for frame in traceback.frames %>
+      <li>$frame.render()</li>
+    <% endfor %>
+    </ul>
+    <pre>$escape(traceback.exception)</pre>
+  <% else %>
+    <% if include_title %>
+      <h3>Traceback <em>(most recent call last)</em>:</h3>
+    <% endif %>
+    <ul>
+    <% for frame in traceback.frames %>
+      <li<% if frame.info %> title="$escape(frame.info, True)"<% endif %>>$frame.render()</li>
+    <% endfor %>
+    </ul>
+    <blockquote>$escape(traceback.exception)</blockquote>
+  <% endif %>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/debug/utils.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.debug.utils
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Various other utilities.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD.
+"""
+from os.path import join, dirname
+from werkzeug.templates import Template
+
+
+def get_template(filename):
+    return Template.from_file(join(dirname(__file__), 'templates', filename))
+
+
+def render_template(template_filename, **context):
+    return get_template(template_filename).render(**context)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/exceptions.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,459 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.exceptions
+    ~~~~~~~~~~~~~~~~~~~
+
+    This module implements a number of Python exceptions you can raise from
+    within your views to trigger a standard non-200 response.
+
+
+    Usage Example
+    -------------
+
+    ::
+
+        from werkzeug import BaseRequest, responder
+        from werkzeug.exceptions import HTTPException, NotFound
+
+        def view(request):
+            raise NotFound()
+
+        @responder
+        def application(environ, start_response):
+            request = BaseRequest(environ)
+            try:
+                return view(request)
+            except HTTPException, e:
+                return e
+
+
+    As you can see from this example those exceptions are callable WSGI
+    applications.  Because of Python 2.4 compatibility those do not extend
+    from the response objects but only from the python exception class.
+
+    As a matter of fact they are not Werkzeug response objects.  However you
+    can get a response object by calling ``get_response()`` on a HTTP
+    exception.
+
+    Keep in mind that you have to pass an environment to ``get_response()``
+    because some errors fetch additional information from the WSGI
+    environment.
+
+    If you want to hook in a different exception page to say, a 404 status
+    code, you can add a second except for a specific subclass of an error::
+
+        @responder
+        def application(environ, start_response):
+            request = BaseRequest(environ)
+            try:
+                return view(request)
+            except NotFound, e:
+                return not_found(request)
+            except HTTPException, e:
+                return e
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import sys
+from werkzeug._internal import HTTP_STATUS_CODES, _get_environ
+
+
+class HTTPException(Exception):
+    """
+    Baseclass for all HTTP exceptions.  This exception can be called as WSGI
+    application to render a default error page or you can catch the subclasses
+    of it independently and render nicer error messages.
+    """
+
+    code = None
+    description = None
+
+    def __init__(self, description=None):
+        Exception.__init__(self, '%d %s' % (self.code, self.name))
+        if description is not None:
+            self.description = description
+
+    @classmethod
+    def wrap(cls, exception, name=None):
+        """This method returns a new subclass of the exception provided that
+        also is a subclass of `BadRequest`.
+        """
+        class newcls(cls, exception):
+            def __init__(self, arg=None, description=None):
+                cls.__init__(self, description)
+                exception.__init__(self, arg)
+        newcls.__module__ = sys._getframe(1).f_globals.get('__name__')
+        newcls.__name__ = name or cls.__name__ + exception.__name__
+        return newcls
+
+    @property
+    def name(self):
+        """The status name."""
+        return HTTP_STATUS_CODES[self.code]
+
+    def get_description(self, environ):
+        """Get the description."""
+        environ = _get_environ(environ)
+        return self.description
+
+    def get_body(self, environ):
+        """Get the HTML body."""
+        return (
+            '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
+            '<title>%(code)s %(name)s</title>\n'
+            '<h1>%(name)s</h1>\n'
+            '%(description)s\n'
+        ) % {
+            'code':         self.code,
+            'name':         escape(self.name),
+            'description':  self.get_description(environ)
+        }
+
+    def get_headers(self, environ):
+        """Get a list of headers."""
+        return [('Content-Type', 'text/html')]
+
+    def get_response(self, environ):
+        """Get a response object.
+
+        :param environ: the environ for the request.
+        :return: a :class:`BaseResponse` object or a subclass thereof.
+        """
+        # lazily imported for various reasons.  For one, we can use the exceptions
+        # with custom responses (testing exception instances against types) and
+        # so we don't ever have to import the wrappers, but also because there
+        # are circular dependencies when bootstrapping the module.
+        environ = _get_environ(environ)
+        from werkzeug.wrappers import BaseResponse
+        headers = self.get_headers(environ)
+        return BaseResponse(self.get_body(environ), self.code, headers)
+
+    def __call__(self, environ, start_response):
+        """Call the exception as WSGI application.
+
+        :param environ: the WSGI environment.
+        :param start_response: the response callable provided by the WSGI
+                               server.
+        """
+        response = self.get_response(environ)
+        return response(environ, start_response)
+
+    def __str__(self):
+        return unicode(self).encode('utf-8')
+
+    def __unicode__(self):
+        if 'description' in self.__dict__:
+            txt = self.description
+        else:
+            txt = self.name
+        return '%d: %s' % (self.code, txt)
+
+    def __repr__(self):
+        return '<%s \'%s\'>' % (self.__class__.__name__, self)
+
+
+class _ProxyException(HTTPException):
+    """An HTTP exception that expands renders a WSGI application on error."""
+
+    def __init__(self, response):
+        Exception.__init__(self, 'proxy exception for %r' % response)
+        self.response = response
+
+    def get_response(self, environ):
+        return self.response
+
+
+class BadRequest(HTTPException):
+    """*400* `Bad Request`
+
+    Raise if the browser sends something to the application the application
+    or server cannot handle.
+    """
+    code = 400
+    description = (
+        '<p>The browser (or proxy) sent a request that this server could '
+        'not understand.</p>'
+    )
+
+
+class Unauthorized(HTTPException):
+    """*401* `Unauthorized`
+
+    Raise if the user is not authorized.  Also used if you want to use HTTP
+    basic auth.
+    """
+    code = 401
+    description = (
+        '<p>The server could not verify that you are authorized to access '
+        'the URL requested.  You either supplied the wrong credentials (e.g. '
+        'a bad password), or your browser doesn\'t understand how to supply '
+        'the credentials required.</p><p>In case you are allowed to request '
+        'the document, please check your user-id and password and try '
+        'again.</p>'
+    )
+
+
+class Forbidden(HTTPException):
+    """*403* `Forbidden`
+
+    Raise if the user doesn't have the permission for the requested resource
+    but was authenticated.
+    """
+    code = 403
+    description = (
+        '<p>You don\'t have the permission to access the requested resource. '
+        'It is either read-protected or not readable by the server.</p>'
+    )
+
+
+class NotFound(HTTPException):
+    """*404* `Not Found`
+
+    Raise if a resource does not exist and never existed.
+    """
+    code = 404
+    description = (
+        '<p>The requested URL was not found on the server.</p>'
+        '<p>If you entered the URL manually please check your spelling and '
+        'try again.</p>'
+    )
+
+
+class MethodNotAllowed(HTTPException):
+    """*405* `Method Not Allowed`
+
+    Raise if the server used a method the resource does not handle.  For
+    example `POST` if the resource is view only.  Especially useful for REST.
+
+    The first argument for this exception should be a list of allowed methods.
+    Strictly speaking the response would be invalid if you don't provide valid
+    methods in the header which you can do with that list.
+    """
+    code = 405
+
+    def __init__(self, valid_methods=None, description=None):
+        """Takes an optional list of valid http methods
+        starting with werkzeug 0.3 the list will be mandatory."""
+        HTTPException.__init__(self, description)
+        self.valid_methods = valid_methods
+
+    def get_headers(self, environ):
+        headers = HTTPException.get_headers(self, environ)
+        if self.valid_methods:
+            headers.append(('Allow', ', '.join(self.valid_methods)))
+        return headers
+
+    def get_description(self, environ):
+        m = escape(environ.get('REQUEST_METHOD', 'GET'))
+        return '<p>The method %s is not allowed for the requested URL.</p>' % m
+
+
+class NotAcceptable(HTTPException):
+    """*406* `Not Acceptable`
+
+    Raise if the server can't return any content conforming to the
+    `Accept` headers of the client.
+    """
+    code = 406
+
+    description = (
+        '<p>The resource identified by the request is only capable of '
+        'generating response entities which have content characteristics '
+        'not acceptable according to the accept headers sent in the '
+        'request.</p>'
+        )
+
+
+class RequestTimeout(HTTPException):
+    """*408* `Request Timeout`
+
+    Raise to signalize a timeout.
+    """
+    code = 408
+    description = (
+        '<p>The server closed the network connection because the browser '
+        'didn\'t finish the request within the specified time.</p>'
+    )
+
+
+class Gone(HTTPException):
+    """*410* `Gone`
+
+    Raise if a resource existed previously and went away without new location.
+    """
+    code = 410
+    description = (
+        '<p>The requested URL is no longer available on this server and '
+        'there is no forwarding address.</p><p>If you followed a link '
+        'from a foreign page, please contact the author of this page.'
+    )
+
+
+class LengthRequired(HTTPException):
+    """*411* `Length Required`
+
+    Raise if the browser submitted data but no ``Content-Length`` header which
+    is required for the kind of processing the server does.
+    """
+    code = 411
+    description = (
+        '<p>A request with this method requires a valid <code>Content-'
+        'Length</code> header.</p>'
+    )
+
+
+class PreconditionFailed(HTTPException):
+    """*412* `Precondition Failed`
+
+    Status code used in combination with ``If-Match``, ``If-None-Match``, or
+    ``If-Unmodified-Since``.
+    """
+    code = 412
+    description = (
+        '<p>The precondition on the request for the URL failed positive '
+        'evaluation.</p>'
+    )
+
+
+class RequestEntityTooLarge(HTTPException):
+    """*413* `Request Entity Too Large`
+
+    The status code one should return if the data submitted exceeded a given
+    limit.
+    """
+    code = 413
+    description = (
+        '<p>The data value transmitted exceeds the capacity limit.</p>'
+    )
+
+
+class RequestURITooLarge(HTTPException):
+    """*414* `Request URI Too Large`
+
+    Like *413* but for too long URLs.
+    """
+    code = 414
+    description = (
+        '<p>The length of the requested URL exceeds the capacity limit '
+        'for this server.  The request cannot be processed.</p>'
+    )
+
+
+class UnsupportedMediaType(HTTPException):
+    """*415* `Unsupported Media Type`
+
+    The status code returned if the server is unable to handle the media type
+    the client transmitted.
+    """
+    code = 415
+    description = (
+        '<p>The server does not support the media type transmitted in '
+        'the request.</p>'
+    )
+
+
+class InternalServerError(HTTPException):
+    """*500* `Internal Server Error`
+
+    Raise if an internal server error occurred.  This is a good fallback if an
+    unknown error occurred in the dispatcher.
+    """
+    code = 500
+    description = (
+        '<p>The server encountered an internal error and was unable to '
+        'complete your request.  Either the server is overloaded or there '
+        'is an error in the application.</p>'
+    )
+
+
+class NotImplemented(HTTPException):
+    """*501* `Not Implemented`
+
+    Raise if the application does not support the action requested by the
+    browser.
+    """
+    code = 501
+    description = (
+        '<p>The server does not support the action requested by the '
+        'browser.</p>'
+    )
+
+
+class BadGateway(HTTPException):
+    """*502* `Bad Gateway`
+
+    If you do proxying in your application you should return this status code
+    if you received an invalid response from the upstream server it accessed
+    in attempting to fulfill the request.
+    """
+    code = 502
+    description = (
+        '<p>The proxy server received an invalid response from an upstream '
+        'server.</p>'
+    )
+
+
+class ServiceUnavailable(HTTPException):
+    """*503* `Service Unavailable`
+
+    Status code you should return if a service is temporarily unavailable.
+    """
+    code = 503
+    description = (
+        '<p>The server is temporarily unable to service your request due to '
+        'maintenance downtime or capacity problems.  Please try again '
+        'later.</p>'
+    )
+
+
+default_exceptions = {}
+__all__ = ['HTTPException']
+
+def _find_exceptions():
+    for name, obj in globals().iteritems():
+        try:
+            if getattr(obj, 'code', None) is not None:
+                default_exceptions[obj.code] = obj
+                __all__.append(obj.__name__)
+        except TypeError: # pragma: no cover
+            continue
+_find_exceptions()
+del _find_exceptions
+
+
+#: raised by the request functions if they were unable to decode the
+#: incoming data properly.
+HTTPUnicodeError = BadRequest.wrap(UnicodeError, 'HTTPUnicodeError')
+
+
+class Aborter(object):
+    """
+    When passed a dict of code -> exception items it can be used as
+    callable that raises exceptions.  If the first argument to the
+    callable is an integer it will be looked up in the mapping, if it's
+    a WSGI application it will be raised in a proxy exception.
+
+    The rest of the arguments are forwarded to the exception constructor.
+    """
+
+    def __init__(self, mapping=None, extra=None):
+        if mapping is None:
+            mapping = default_exceptions
+        self.mapping = dict(mapping)
+        if extra is not None:
+            self.mapping.update(extra)
+
+    def __call__(self, code, *args, **kwargs):
+        if not args and not kwargs and not isinstance(code, (int, long)):
+            raise _ProxyException(code)
+        if code not in self.mapping:
+            raise LookupError('no exception for %r' % code)
+        raise self.mapping[code](*args, **kwargs)
+
+abort = Aborter()
+
+
+# imported here because of circular dependencies of werkzeug.utils
+from werkzeug.utils import escape
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/formparser.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,349 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.formparser
+    ~~~~~~~~~~~~~~~~~~~
+
+    This module implements the form parsing.  It supports url-encoded forms
+    as well as non-nested multipart uploads.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+from cStringIO import StringIO
+from tempfile import TemporaryFile
+from itertools import chain, repeat
+
+from werkzeug._internal import _decode_unicode, _empty_stream
+from werkzeug.urls import url_decode
+from werkzeug.wsgi import LimitedStream, make_line_iter
+from werkzeug.exceptions import RequestEntityTooLarge
+from werkzeug.datastructures import Headers, FileStorage, MultiDict
+from werkzeug.http import parse_options_header
+
+
+#: an iterator that yields empty strings
+_empty_string_iter = repeat('')
+
+#: a regular expression for multipart boundaries
+_multipart_boundary_re = re.compile('^[ -~]{0,200}[!-~]$')
+
+#: supported http encodings that are also available in python we support
+#: for multipart messages.
+_supported_multipart_encodings = frozenset(['base64', 'quoted-printable'])
+
+
+def default_stream_factory(total_content_length, filename, content_type,
+                           content_length=None):
+    """The stream factory that is used per default."""
+    if total_content_length > 1024 * 500:
+        return TemporaryFile('wb+')
+    return StringIO()
+
+
+def parse_form_data(environ, stream_factory=None, charset='utf-8',
+                    errors='ignore', max_form_memory_size=None,
+                    max_content_length=None, cls=None,
+                    silent=True):
+    """Parse the form data in the environ and return it as tuple in the form
+    ``(stream, form, files)``.  You should only call this method if the
+    transport method is `POST` or `PUT`.
+
+    If the mimetype of the data transmitted is `multipart/form-data` the
+    files multidict will be filled with `FileStorage` objects.  If the
+    mimetype is unknown the input stream is wrapped and returned as first
+    argument, else the stream is empty.
+
+    This function does not raise exceptions, even if the input data is
+    malformed.
+
+    Have a look at :ref:`dealing-with-request-data` for more details.
+
+    .. versionadded:: 0.5
+       The `max_form_memory_size`, `max_content_length` and
+       `cls` parameters were added.
+
+    .. versionadded:: 0.5.1
+       The optional `silent` flag was added.
+
+    :param environ: the WSGI environment to be used for parsing.
+    :param stream_factory: An optional callable that returns a new read and
+                           writeable file descriptor.  This callable works
+                           the same as :meth:`~BaseResponse._get_file_stream`.
+    :param charset: The character set for URL and url encoded form data.
+    :param errors: The encoding error behavior.
+    :param max_form_memory_size: the maximum number of bytes to be accepted for
+                           in-memory stored form data.  If the data
+                           exceeds the value specified an
+                           :exc:`~exceptions.RequestURITooLarge`
+                           exception is raised.
+    :param max_content_length: If this is provided and the transmitted data
+                               is longer than this value an
+                               :exc:`~exceptions.RequestEntityTooLarge`
+                               exception is raised.
+    :param cls: an optional dict class to use.  If this is not specified
+                       or `None` the default :class:`MultiDict` is used.
+    :param silent: If set to False parsing errors will not be caught.
+    :return: A tuple in the form ``(stream, form, files)``.
+    """
+    content_type, extra = parse_options_header(environ.get('CONTENT_TYPE', ''))
+    try:
+        content_length = int(environ['CONTENT_LENGTH'])
+    except (KeyError, ValueError):
+        content_length = 0
+
+    if cls is None:
+        cls = MultiDict
+
+    if max_content_length is not None and content_length > max_content_length:
+        raise RequestEntityTooLarge()
+
+    stream = _empty_stream
+    files = ()
+
+    if content_type == 'multipart/form-data':
+        try:
+            form, files = parse_multipart(environ['wsgi.input'],
+                                          extra.get('boundary'),
+                                          content_length, stream_factory,
+                                          charset, errors,
+                                          max_form_memory_size=max_form_memory_size)
+        except ValueError, e:
+            if not silent:
+                raise
+            form = cls()
+        else:
+            form = cls(form)
+    elif content_type == 'application/x-www-form-urlencoded' or \
+         content_type == 'application/x-url-encoded':
+        if max_form_memory_size is not None and \
+           content_length > max_form_memory_size:
+            raise RequestEntityTooLarge()
+        form = url_decode(environ['wsgi.input'].read(content_length),
+                          charset, errors=errors, cls=cls)
+    else:
+        form = cls()
+        stream = LimitedStream(environ['wsgi.input'], content_length)
+
+    return stream, form, cls(files)
+
+
+def _fix_ie_filename(filename):
+    """Internet Explorer 6 transmits the full file name if a file is
+    uploaded.  This function strips the full path if it thinks the
+    filename is Windows-like absolute.
+    """
+    if filename[1:3] == ':\\' or filename[:2] == '\\\\':
+        return filename.split('\\')[-1]
+    return filename
+
+
+def _line_parse(line):
+    """Removes line ending characters and returns a tuple (`stripped_line`,
+    `is_terminated`).
+    """
+    if line[-2:] == '\r\n':
+        return line[:-2], True
+    elif line[-1:] in '\r\n':
+        return line[:-1], True
+    return line, False
+
+
+def _find_terminator(iterator):
+    """The terminator might have some additional newlines before it.
+    There is at least one application that sends additional newlines
+    before headers (the python setuptools package).
+    """
+    for line in iterator:
+        if not line:
+            break
+        line = line.strip()
+        if line:
+            return line
+    return ''
+
+
+def is_valid_multipart_boundary(boundary):
+    """Checks if the string given is a valid multipart boundary."""
+    return _multipart_boundary_re.match(boundary) is not None
+
+
+def parse_multipart(file, boundary, content_length, stream_factory=None,
+                    charset='utf-8', errors='ignore', buffer_size=10 * 1024,
+                    max_form_memory_size=None):
+    """Parse a multipart/form-data stream.  This is invoked by
+    :func:`utils.parse_form_data` if the content type matches.  Currently it
+    exists for internal usage only, but could be exposed as separate
+    function if it turns out to be useful and if we consider the API stable.
+    """
+    # XXX: this function does not support multipart/mixed.  I don't know of
+    #      any browser that supports this, but it should be implemented
+    #      nonetheless.
+
+    # make sure the buffer size is divisible by four so that we can base64
+    # decode chunk by chunk
+    assert buffer_size % 4 == 0, 'buffer size has to be divisible by 4'
+    # also the buffer size has to be at least 1024 bytes long or long headers
+    # will freak out the system
+    assert buffer_size >= 1024, 'buffer size has to be at least 1KB'
+
+    if stream_factory is None:
+        stream_factory = default_stream_factory
+
+    if not boundary:
+        raise ValueError('Missing boundary')
+    if not is_valid_multipart_boundary(boundary):
+        raise ValueError('Invalid boundary: %s' % boundary)
+    if len(boundary) > buffer_size: # pragma: no cover
+        # this should never happen because we check for a minimum size
+        # of 1024 and boundaries may not be longer than 200.  The only
+        # situation when this happen is for non debug builds where
+        # the assert i skipped.
+        raise ValueError('Boundary longer than buffer size')
+
+    total_content_length = content_length
+    next_part = '--' + boundary
+    last_part = next_part + '--'
+
+    form = []
+    files = []
+    in_memory = 0
+
+    # convert the file into a limited stream with iteration capabilities
+    file = LimitedStream(file, content_length)
+    iterator = chain(make_line_iter(file, buffer_size=buffer_size),
+                     _empty_string_iter)
+
+    try:
+        terminator = _find_terminator(iterator)
+        if terminator != next_part:
+            raise ValueError('Expected boundary at start of multipart data')
+
+        while terminator != last_part:
+            headers = parse_multipart_headers(iterator)
+            disposition = headers.get('content-disposition')
+            if disposition is None:
+                raise ValueError('Missing Content-Disposition header')
+            disposition, extra = parse_options_header(disposition)
+            name = extra.get('name')
+
+            transfer_encoding = headers.get('content-transfer-encoding')
+            try_decode = transfer_encoding is not None and \
+                         transfer_encoding in _supported_multipart_encodings
+
+            filename = extra.get('filename')
+
+            # if no content type is given we stream into memory.  A list is
+            # used as a temporary container.
+            if filename is None:
+                is_file = False
+                container = []
+                _write = container.append
+                guard_memory = max_form_memory_size is not None
+
+            # otherwise we parse the rest of the headers and ask the stream
+            # factory for something we can write in.
+            else:
+                content_type = headers.get('content-type')
+                content_type = parse_options_header(content_type)[0] \
+                    or 'text/plain'
+                is_file = True
+                guard_memory = False
+                if filename is not None:
+                    filename = _fix_ie_filename(_decode_unicode(filename,
+                                                                charset,
+                                                                errors))
+                try:
+                    content_length = int(headers['content-length'])
+                except (KeyError, ValueError):
+                    content_length = 0
+                container = stream_factory(total_content_length, content_type,
+                                           filename, content_length)
+                _write = container.write
+
+            buf = ''
+            for line in iterator:
+                if not line:
+                    raise ValueError('unexpected end of stream')
+
+                if line[:2] == '--':
+                    terminator = line.rstrip()
+                    if terminator in (next_part, last_part):
+                        break
+
+                if try_decode:
+                    try:
+                        line = line.decode(transfer_encoding)
+                    except:
+                        raise ValueError('could not decode transfer '
+                                         'encoded chunk')
+
+                # we have something in the buffer from the last iteration.
+                # this is usually a newline delimiter.
+                if buf:
+                    _write(buf)
+                    buf = ''
+
+                # If the line ends with windows CRLF we write everything except
+                # the last two bytes.  In all other cases however we write
+                # everything except the last byte.  If it was a newline, that's
+                # fine, otherwise it does not matter because we will write it
+                # the next iteration.  this ensures we do not write the
+                # final newline into the stream.  That way we do not have to
+                # truncate the stream.
+                if line[-2:] == '\r\n':
+                    buf = '\r\n'
+                    cutoff = -2
+                else:
+                    buf = line[-1]
+                    cutoff = -1
+                _write(line[:cutoff])
+
+                # if we write into memory and there is a memory size limit we
+                # count the number of bytes in memory and raise an exception if
+                # there is too much data in memory.
+                if guard_memory:
+                    in_memory += len(line)
+                    if in_memory > max_form_memory_size:
+                        from werkzeug.exceptions import RequestEntityTooLarge
+                        raise RequestEntityTooLarge()
+            else: # pragma: no cover
+                raise ValueError('unexpected end of part')
+
+            if is_file:
+                container.seek(0)
+                files.append((name, FileStorage(container, filename, name,
+                                                content_type,
+                                                content_length, headers)))
+            else:
+                form.append((name, _decode_unicode(''.join(container),
+                                                   charset, errors)))
+    finally:
+        # make sure the whole input stream is read
+        file.exhaust()
+
+    return form, files
+
+
+def parse_multipart_headers(iterable):
+    """Parses multipart headers from an iterable that yields lines (including
+    the trailing newline symbol.
+    """
+    result = []
+    for line in iterable:
+        line, line_terminated = _line_parse(line)
+        if not line_terminated:
+            raise ValueError('unexpected end of line in multipart header')
+        if not line:
+            break
+        elif line[0] in ' \t' and result:
+            key, value = result[-1]
+            result[-1] = (key, value + '\n ' + line[1:])
+        else:
+            parts = line.split(':', 1)
+            if len(parts) == 2:
+                result.append((parts[0].strip(), parts[1].strip()))
+
+    # we link the list to the headers, no need to create a copy, the
+    # list was not shared anyways.
+    return Headers.linked(result)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/http.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,578 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.http
+    ~~~~~~~~~~~~~
+
+    Werkzeug comes with a bunch of utilities that help Werkzeug to deal with
+    HTTP data.  Most of the classes and functions provided by this module are
+    used by the wrappers, but they are useful on their own, too, especially if
+    the response and request objects are not used.
+
+    This covers some of the more HTTP centric features of WSGI, some other
+    utilities such as cookie handling are documented in the `werkzeug.utils`
+    module.
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import inspect
+try:
+    from email.utils import parsedate_tz
+except ImportError: # pragma: no cover
+    from email.Utils import parsedate_tz
+from urllib2 import parse_http_list as _parse_list_header
+from datetime import datetime, timedelta
+try:
+    from hashlib import md5
+except ImportError: # pragma: no cover
+    from md5 import new as md5
+
+
+#: HTTP_STATUS_CODES is "exported" from this module.
+#: XXX: move to werkzeug.consts or something
+from werkzeug._internal import HTTP_STATUS_CODES
+
+
+_accept_re = re.compile(r'([^\s;,]+)(?:[^,]*?;\s*q=(\d*(?:\.\d+)?))?')
+_token_chars = frozenset("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                         '^_`abcdefghijklmnopqrstuvwxyz|~')
+_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
+_unsafe_header_chars = set('()<>@,;:\"/[]?={} \t')
+_quoted_string_re = r'"[^"\\]*(?:\\.[^"\\]*)*"'
+_option_header_piece_re = re.compile(r';\s*([^\s;=]+|%s)\s*(?:=\s*([^;]+|%s))?\s*' %
+    (_quoted_string_re, _quoted_string_re))
+
+_entity_headers = frozenset([
+    'allow', 'content-encoding', 'content-language', 'content-length',
+    'content-location', 'content-md5', 'content-range', 'content-type',
+    'expires', 'last-modified'
+])
+_hop_by_pop_headers = frozenset([
+    'connection', 'keep-alive', 'proxy-authenticate',
+    'proxy-authorization', 'te', 'trailers', 'transfer-encoding',
+    'upgrade'
+])
+
+
+def quote_header_value(value, extra_chars='', allow_token=True):
+    """Quote a header value if necessary.
+
+    .. versionadded:: 0.5
+
+    :param value: the value to quote.
+    :param extra_chars: a list of extra characters to skip quoting.
+    :param allow_token: if this is enabled token values are returned
+                        unchanged.
+    """
+    value = str(value)
+    if allow_token:
+        token_chars = _token_chars | set(extra_chars)
+        if set(value).issubset(token_chars):
+            return value
+    return '"%s"' % value.replace('\\', '\\\\').replace('"', '\\"')
+
+
+def unquote_header_value(value, is_filename=False):
+    r"""Unquotes a header value.  (Reversal of :func:`quote_header_value`).
+    This does not use the real unquoting but what browsers are actually
+    using for quoting.
+
+    .. versionadded:: 0.5
+
+    :param value: the header value to unquote.
+    """
+    if value and value[0] == value[-1] == '"':
+        # this is not the real unquoting, but fixing this so that the
+        # RFC is met will result in bugs with internet explorer and
+        # probably some other browsers as well.  IE for example is
+        # uploading files with "C:\foo\bar.txt" as filename
+        value = value[1:-1]
+
+        # if this is a filename and the starting characters look like
+        # a UNC path, then just return the value without quotes.  Using the
+        # replace sequence below on a UNC path has the effect of turning
+        # the leading double slash into a single slash and then
+        # _fix_ie_filename() doesn't work correctly.  See #458.
+        if not is_filename or value[:2] != '\\\\':
+            return value.replace('\\\\', '\\').replace('\\"', '"')
+    return value
+
+
+def dump_options_header(header, options):
+    """The reverse function to :func:`parse_options_header`.
+
+    :param header: the header to dump
+    :param options: a dict of options to append.
+    """
+    segments = []
+    if header is not None:
+        segments.append(header)
+    for key, value in options.iteritems():
+        if value is None:
+            segments.append(key)
+        else:
+            segments.append('%s=%s' % (key, quote_header_value(value)))
+    return '; '.join(segments)
+
+
+def dump_header(iterable, allow_token=True):
+    """Dump an HTTP header again.  This is the reversal of
+    :func:`parse_list_header`, :func:`parse_set_header` and
+    :func:`parse_dict_header`.  This also quotes strings that include an
+    equals sign unless you pass it as dict of key, value pairs.
+
+    >>> dump_header({'foo': 'bar baz'})
+    'foo="bar baz"'
+    >>> dump_header(('foo', 'bar baz'))
+    'foo, "bar baz"'
+
+    :param iterable: the iterable or dict of values to quote.
+    :param allow_token: if set to `False` tokens as values are disallowed.
+                        See :func:`quote_header_value` for more details.
+    """
+    if isinstance(iterable, dict):
+        items = []
+        for key, value in iterable.iteritems():
+            if value is None:
+                items.append(key)
+            else:
+                items.append('%s=%s' % (
+                    key,
+                    quote_header_value(value, allow_token=allow_token)
+                ))
+    else:
+        items = [quote_header_value(x, allow_token=allow_token)
+                 for x in iterable]
+    return ', '.join(items)
+
+
+def parse_list_header(value):
+    """Parse lists as described by RFC 2068 Section 2.
+
+    In particular, parse comma-separated lists where the elements of
+    the list may include quoted-strings.  A quoted-string could
+    contain a comma.  A non-quoted string could have quotes in the
+    middle.  Quotes are removed automatically after parsing.
+
+    It basically works like :func:`parse_set_header` just that items
+    may appear multiple times and case sensitivity is preserved.
+
+    The return value is a standard :class:`list`:
+
+    >>> parse_list_header('token, "quoted value"')
+    ['token', 'quoted value']
+
+    To create a header from the :class:`list` again, use the
+    :func:`dump_header` function.
+
+    :param value: a string with a list header.
+    :return: :class:`list`
+    """
+    result = []
+    for item in _parse_list_header(value):
+        if item[:1] == item[-1:] == '"':
+            item = unquote_header_value(item[1:-1])
+        result.append(item)
+    return result
+
+
+def parse_dict_header(value):
+    """Parse lists of key, value pairs as described by RFC 2068 Section 2 and
+    convert them into a python dict:
+
+    >>> d = parse_dict_header('foo="is a fish", bar="as well"')
+    >>> type(d) is dict
+    True
+    >>> sorted(d.items())
+    [('bar', 'as well'), ('foo', 'is a fish')]
+
+    If there is no value for a key it will be `None`:
+
+    >>> parse_dict_header('key_without_value')
+    {'key_without_value': None}
+
+    To create a header from the :class:`dict` again, use the
+    :func:`dump_header` function.
+
+    :param value: a string with a dict header.
+    :return: :class:`dict`
+    """
+    result = {}
+    for item in _parse_list_header(value):
+        if '=' not in item:
+            result[item] = None
+            continue
+        name, value = item.split('=', 1)
+        if value[:1] == value[-1:] == '"':
+            value = unquote_header_value(value[1:-1])
+        result[name] = value
+    return result
+
+
+def parse_options_header(value):
+    """Parse a ``Content-Type`` like header into a tuple with the content
+    type and the options:
+
+    >>> parse_options_header('Content-Type: text/html; mimetype=text/html')
+    ('Content-Type: text/html', {'mimetype': 'text/html'})
+
+    This should not be used to parse ``Cache-Control`` like headers that use
+    a slightly different format.  For these headers use the
+    :func:`parse_dict_header` function.
+
+    .. versionadded:: 0.5
+
+    :param value: the header to parse.
+    :return: (str, options)
+    """
+    def _tokenize(string):
+        for match in _option_header_piece_re.finditer(string):
+            key, value = match.groups()
+            key = unquote_header_value(key)
+            if value is not None:
+                value = unquote_header_value(value, key == 'filename')
+            yield key, value
+
+    if not value:
+        return '', {}
+
+    parts = _tokenize(';' + value)
+    name = parts.next()[0]
+    extra = dict(parts)
+    return name, extra
+
+
+def parse_accept_header(value, cls=None):
+    """Parses an HTTP Accept-* header.  This does not implement a complete
+    valid algorithm but one that supports at least value and quality
+    extraction.
+
+    Returns a new :class:`Accept` object (basically a list of ``(value, quality)``
+    tuples sorted by the quality with some additional accessor methods).
+
+    The second parameter can be a subclass of :class:`Accept` that is created
+    with the parsed values and returned.
+
+    :param value: the accept header string to be parsed.
+    :param cls: the wrapper class for the return value (can be
+                         :class:`Accept` or a subclass thereof)
+    :return: an instance of `cls`.
+    """
+    if cls is None:
+        cls = Accept
+
+    if not value:
+        return cls(None)
+
+    result = []
+    for match in _accept_re.finditer(value):
+        quality = match.group(2)
+        if not quality:
+            quality = 1
+        else:
+            quality = max(min(float(quality), 1), 0)
+        result.append((match.group(1), quality))
+    return cls(result)
+
+
+def parse_cache_control_header(value, on_update=None, cls=None):
+    """Parse a cache control header.  The RFC differs between response and
+    request cache control, this method does not.  It's your responsibility
+    to not use the wrong control statements.
+
+    .. versionadded:: 0.5
+       The `cls` was added.  If not specified an immutable
+       :class:`RequestCacheControl` is returned.
+
+    :param value: a cache control header to be parsed.
+    :param on_update: an optional callable that is called every time a
+                      value on the :class:`CacheControl` object is changed.
+    :param cls: the class for the returned object.  By default
+                                :class:`RequestCacheControl` is used.
+    :return: a `cls` object.
+    """
+    if cls is None:
+        cls = RequestCacheControl
+    if not value:
+        return cls(None, on_update)
+    return cls(parse_dict_header(value), on_update)
+
+
+def parse_set_header(value, on_update=None):
+    """Parse a set-like header and return a :class:`HeaderSet` object:
+
+    >>> hs = parse_set_header('token, "quoted value"')
+
+    The return value is an object that treats the items case-insensitively
+    and keeps the order of the items:
+
+    >>> 'TOKEN' in hs
+    True
+    >>> hs.index('quoted value')
+    1
+    >>> hs
+    HeaderSet(['token', 'quoted value'])
+
+    To create a header from the :class:`HeaderSet` again, use the
+    :func:`dump_header` function.
+
+    :param value: a set header to be parsed.
+    :param on_update: an optional callable that is called every time a
+                      value on the :class:`HeaderSet` object is changed.
+    :return: a :class:`HeaderSet`
+    """
+    if not value:
+        return HeaderSet(None, on_update)
+    return HeaderSet(parse_list_header(value), on_update)
+
+
+def parse_authorization_header(value):
+    """Parse an HTTP basic/digest authorization header transmitted by the web
+    browser.  The return value is either `None` if the header was invalid or
+    not given, otherwise an :class:`Authorization` object.
+
+    :param value: the authorization header to parse.
+    :return: a :class:`Authorization` object or `None`.
+    """
+    if not value:
+        return
+    try:
+        auth_type, auth_info = value.split(None, 1)
+        auth_type = auth_type.lower()
+    except ValueError:
+        return
+    if auth_type == 'basic':
+        try:
+            username, password = auth_info.decode('base64').split(':', 1)
+        except Exception, e:
+            return
+        return Authorization('basic', {'username': username,
+                                       'password': password})
+    elif auth_type == 'digest':
+        auth_map = parse_dict_header(auth_info)
+        for key in 'username', 'realm', 'nonce', 'uri', 'nc', 'cnonce', \
+                   'response':
+            if not key in auth_map:
+                return
+        return Authorization('digest', auth_map)
+
+
+def parse_www_authenticate_header(value, on_update=None):
+    """Parse an HTTP WWW-Authenticate header into a :class:`WWWAuthenticate`
+    object.
+
+    :param value: a WWW-Authenticate header to parse.
+    :param on_update: an optional callable that is called every time a
+                      value on the :class:`WWWAuthenticate` object is changed.
+    :return: a :class:`WWWAuthenticate` object.
+    """
+    if not value:
+        return WWWAuthenticate(on_update=on_update)
+    try:
+        auth_type, auth_info = value.split(None, 1)
+        auth_type = auth_type.lower()
+    except (ValueError, AttributeError):
+        return WWWAuthenticate(value.strip().lower(), on_update=on_update)
+    return WWWAuthenticate(auth_type, parse_dict_header(auth_info),
+                           on_update)
+
+
+def quote_etag(etag, weak=False):
+    """Quote an etag.
+
+    :param etag: the etag to quote.
+    :param weak: set to `True` to tag it "weak".
+    """
+    if '"' in etag:
+        raise ValueError('invalid etag')
+    etag = '"%s"' % etag
+    if weak:
+        etag = 'w/' + etag
+    return etag
+
+
+def unquote_etag(etag):
+    """Unquote a single etag:
+
+    >>> unquote_etag('w/"bar"')
+    ('bar', True)
+    >>> unquote_etag('"bar"')
+    ('bar', False)
+
+    :param etag: the etag identifier to unquote.
+    :return: a ``(etag, weak)`` tuple.
+    """
+    if not etag:
+        return None, None
+    etag = etag.strip()
+    weak = False
+    if etag[:2] in ('w/', 'W/'):
+        weak = True
+        etag = etag[2:]
+    if etag[:1] == etag[-1:] == '"':
+        etag = etag[1:-1]
+    return etag, weak
+
+
+def parse_etags(value):
+    """Parse an etag header.
+
+    :param value: the tag header to parse
+    :return: an :class:`ETags` object.
+    """
+    if not value:
+        return ETags()
+    strong = []
+    weak = []
+    end = len(value)
+    pos = 0
+    while pos < end:
+        match = _etag_re.match(value, pos)
+        if match is None:
+            break
+        is_weak, quoted, raw = match.groups()
+        if raw == '*':
+            return ETags(star_tag=True)
+        elif quoted:
+            raw = quoted
+        if is_weak:
+            weak.append(raw)
+        else:
+            strong.append(raw)
+        pos = match.end()
+    return ETags(strong, weak)
+
+
+def generate_etag(data):
+    """Generate an etag for some data."""
+    return md5(data).hexdigest()
+
+
+def parse_date(value):
+    """Parse one of the following date formats into a datetime object:
+
+    .. sourcecode:: text
+
+        Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
+        Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+        Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+
+    If parsing fails the return value is `None`.
+
+    :param value: a string with a supported date format.
+    :return: a :class:`datetime.datetime` object.
+    """
+    if value:
+        t = parsedate_tz(value.strip())
+        if t is not None:
+            try:
+                year = t[0]
+                # unfortunately that function does not tell us if two digit
+                # years were part of the string, or if they were prefixed
+                # with two zeroes.  So what we do is to assume that 69-99
+                # refer to 1900, and everything below to 2000
+                if year >= 0 and year <= 68:
+                    year += 2000
+                elif year >= 69 and year <= 99:
+                    year += 1900
+                return datetime(*((year,) + t[1:7])) - \
+                       timedelta(seconds=t[-1] or 0)
+            except (ValueError, OverflowError):
+                return None
+
+
+def is_resource_modified(environ, etag=None, data=None, last_modified=None):
+    """Convenience method for conditional requests.
+
+    :param environ: the WSGI environment of the request to be checked.
+    :param etag: the etag for the response for comparison.
+    :param data: or alternatively the data of the response to automatically
+                 generate an etag using :func:`generate_etag`.
+    :param last_modified: an optional date of the last modification.
+    :return: `True` if the resource was modified, otherwise `False`.
+    """
+    if etag is None and data is not None:
+        etag = generate_etag(data)
+    elif data is not None:
+        raise TypeError('both data and etag given')
+    if environ['REQUEST_METHOD'] not in ('GET', 'HEAD'):
+        return False
+
+    unmodified = False
+    if isinstance(last_modified, basestring):
+        last_modified = parse_date(last_modified)
+    modified_since = parse_date(environ.get('HTTP_IF_MODIFIED_SINCE'))
+
+    if modified_since and last_modified and last_modified <= modified_since:
+        unmodified = True
+    if etag:
+        if_none_match = parse_etags(environ.get('HTTP_IF_NONE_MATCH'))
+        if if_none_match:
+            unmodified = if_none_match.contains_raw(etag)
+
+    return not unmodified
+
+
+def remove_entity_headers(headers, allowed=('expires', 'content-location')):
+    """Remove all entity headers from a list or :class:`Headers` object.  This
+    operation works in-place.  `Expires` and `Content-Location` headers are
+    by default not removed.  The reason for this is :rfc:`2616` section
+    10.3.5 which specifies some entity headers that should be sent.
+
+    .. versionchanged:: 0.5
+       added `allowed` parameter.
+
+    :param headers: a list or :class:`Headers` object.
+    :param allowed: a list of headers that should still be allowed even though
+                    they are entity headers.
+    """
+    allowed = set(x.lower() for x in allowed)
+    headers[:] = [(key, value) for key, value in headers if
+                  not is_entity_header(key) or key.lower() in allowed]
+
+
+def remove_hop_by_hop_headers(headers):
+    """Remove all HTTP/1.1 "Hop-by-Hop" headers from a list or
+    :class:`Headers` object.  This operation works in-place.
+
+    .. versionadded:: 0.5
+
+    :param headers: a list or :class:`Headers` object.
+    """
+    headers[:] = [(key, value) for key, value in headers if
+                  not is_hop_by_hop_header(key)]
+
+
+def is_entity_header(header):
+    """Check if a header is an entity header.
+
+    .. versionadded:: 0.5
+
+    :param header: the header to test.
+    :return: `True` if it's an entity header, `False` otherwise.
+    """
+    return header.lower() in _entity_headers
+
+
+def is_hop_by_hop_header(header):
+    """Check if a header is an HTTP/1.1 "Hop-by-Hop" header.
+
+    .. versionadded:: 0.5
+
+    :param header: the header to test.
+    :return: `True` if it's an entity header, `False` otherwise.
+    """
+    return header.lower() in _hop_by_pop_headers
+
+
+# circular dependency fun
+from werkzeug.datastructures import Headers, Accept, RequestCacheControl, \
+     ResponseCacheControl, HeaderSet, ETags, Authorization, \
+     WWWAuthenticate
+
+
+# DEPRECATED
+# backwards compatible imports
+from werkzeug.datastructures import MIMEAccept, CharsetAccept, LanguageAccept
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/local.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,405 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.local
+    ~~~~~~~~~~~~~~
+
+    This module implements context-local objects.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+try:
+    from greenlet import getcurrent as get_current_greenlet
+except ImportError: # pragma: no cover
+    try:
+        from py.magic import greenlet
+        get_current_greenlet = greenlet.getcurrent
+        del greenlet
+    except:
+        # catch all, py.* fails with so many different errors.
+        get_current_greenlet = int
+try:
+    from thread import get_ident as get_current_thread, allocate_lock
+except ImportError: # pragma: no cover
+    from dummy_thread import get_ident as get_current_thread, allocate_lock
+
+from werkzeug.wsgi import ClosingIterator
+from werkzeug._internal import _patch_wrapper
+
+
+# get the best ident function.  if greenlets are not installed we can
+# safely just use the builtin thread function and save a python methodcall
+# and the cost of calculating a hash.
+if get_current_greenlet is int: # pragma: no cover
+    get_ident = get_current_thread
+else:
+    get_ident = lambda: (get_current_thread(), get_current_greenlet())
+
+
+def release_local(local):
+    """Releases the contents of the local for the current context.
+    This makes it possible to use locals without a manager.
+
+    Example::
+
+        >>> loc = Local()
+        >>> loc.foo = 42
+        >>> release_local(loc)
+        >>> hasattr(loc, 'foo')
+        False
+
+    With this function one can release :class:`Local` objects as well
+    as :class:`StackLocal` objects.  However it is not possible to
+    release data held by proxies that way, one always has to retain
+    a reference to the underlying local object in order to be able
+    to release it.
+
+    .. versionadded:: 0.6.1
+    """
+    local.__release_local__()
+
+
+class Local(object):
+    __slots__ = ('__storage__', '__lock__')
+
+    def __init__(self):
+        object.__setattr__(self, '__storage__', {})
+        object.__setattr__(self, '__lock__', allocate_lock())
+
+    def __iter__(self):
+        return self.__storage__.iteritems()
+
+    def __call__(self, proxy):
+        """Create a proxy for a name."""
+        return LocalProxy(self, proxy)
+
+    def __release_local__(self):
+        self.__storage__.pop(get_ident(), None)
+
+    def __getattr__(self, name):
+        self.__lock__.acquire()
+        try:
+            try:
+                return self.__storage__[get_ident()][name]
+            except KeyError:
+                raise AttributeError(name)
+        finally:
+            self.__lock__.release()
+
+    def __setattr__(self, name, value):
+        self.__lock__.acquire()
+        try:
+            ident = get_ident()
+            storage = self.__storage__
+            if ident in storage:
+                storage[ident][name] = value
+            else:
+                storage[ident] = {name: value}
+        finally:
+            self.__lock__.release()
+
+    def __delattr__(self, name):
+        self.__lock__.acquire()
+        try:
+            try:
+                del self.__storage__[get_ident()][name]
+            except KeyError:
+                raise AttributeError(name)
+        finally:
+            self.__lock__.release()
+
+
+class LocalStack(object):
+    """This class works similar to a :class:`Local` but keeps a stack
+    of objects instead.  This is best explained with an example::
+
+        >>> ls = LocalStack()
+        >>> ls.push(42)
+        >>> ls.top
+        42
+        >>> ls.push(23)
+        >>> ls.top
+        23
+        >>> ls.pop()
+        23
+        >>> ls.top
+        42
+
+    They can be force released by using a :class:`LocalManager` or with
+    the :func:`release_local` function but the correct way is to pop the
+    item from the stack after using.  When the stack is empty it will
+    no longer be bound to the current context (and as such released).
+
+    By calling the stack without arguments it returns a proxy that resolves to
+    the topmost item on the stack.
+
+    .. versionadded:: 0.6.1
+    """
+
+    def __init__(self):
+        self._local = Local()
+        self._lock = allocate_lock()
+
+    def __release_local__(self):
+        self._local.__release_local__()
+
+    def __call__(self):
+        def _lookup():
+            rv = self.top
+            if rv is None:
+                raise RuntimeError('object unbound')
+            return rv
+        return LocalProxy(_lookup)
+
+    def push(self, obj):
+        """Pushes a new item to the stack"""
+        self._lock.acquire()
+        try:
+            rv = getattr(self._local, 'stack', None)
+            if rv is None:
+                self._local.stack = rv = []
+            rv.append(obj)
+            return rv
+        finally:
+            self._lock.release()
+
+    def pop(self):
+        """Removes the topmost item from the stack, will return the
+        old value or `None` if the stack was already empty.
+        """
+        self._lock.acquire()
+        try:
+            stack = getattr(self._local, 'stack', None)
+            if stack is None:
+                return None
+            elif len(stack) == 1:
+                release_local(self._local)
+                return stack[-1]
+            else:
+                return stack.pop()
+        finally:
+            self._lock.release()
+
+    @property
+    def top(self):
+        """The topmost item on the stack.  If the stack is empty,
+        `None` is returned.
+        """
+        try:
+            return self._local.stack[-1]
+        except (AttributeError, IndexError):
+            return None
+
+
+class LocalManager(object):
+    """Local objects cannot manage themselves. For that you need a local
+    manager.  You can pass a local manager multiple locals or add them later
+    by appending them to `manager.locals`.  Everytime the manager cleans up
+    it, will clean up all the data left in the locals for this context.
+
+    .. versionchanged:: 0.6.1
+       Instead of a manager the :func:`release_local` function can be used
+       as well.
+    """
+
+    def __init__(self, locals=None):
+        if locals is None:
+            self.locals = []
+        elif isinstance(locals, Local):
+            self.locals = [locals]
+        else:
+            self.locals = list(locals)
+
+    def get_ident(self):
+        """Return the context identifier the local objects use internally for
+        this context.  You cannot override this method to change the behavior
+        but use it to link other context local objects (such as SQLAlchemy's
+        scoped sessions) to the Werkzeug locals.
+        """
+        return get_ident()
+
+    def cleanup(self):
+        """Manually clean up the data in the locals for this context.  Call
+        this at the end of the request or use `make_middleware()`.
+        """
+        ident = self.get_ident()
+        for local in self.locals:
+            release_local(local)
+
+    def make_middleware(self, app):
+        """Wrap a WSGI application so that cleaning up happens after
+        request end.
+        """
+        def application(environ, start_response):
+            return ClosingIterator(app(environ, start_response), self.cleanup)
+        return application
+
+    def middleware(self, func):
+        """Like `make_middleware` but for decorating functions.
+
+        Example usage::
+
+            @manager.middleware
+            def application(environ, start_response):
+                ...
+
+        The difference to `make_middleware` is that the function passed
+        will have all the arguments copied from the inner application
+        (name, docstring, module).
+        """
+        return _patch_wrapper(func, self.make_middleware(func))
+
+    def __repr__(self):
+        return '<%s storages: %d>' % (
+            self.__class__.__name__,
+            len(self.locals)
+        )
+
+
+class LocalProxy(object):
+    """Acts as a proxy for a werkzeug local.  Forwards all operations to
+    a proxied object.  The only operations not supported for forwarding
+    are right handed operands and any kind of assignment.
+
+    Example usage::
+
+        from werkzeug import Local
+        l = Local()
+
+        # these are proxies
+        request = l('request')
+        user = l('user')
+
+
+        from werkzeug import LocalStack
+        _response_local = LocalStack()
+
+        # this is a proxy
+        response = _response_local()
+
+    Whenever something is bound to l.user / l.request the proxy objects
+    will forward all operations.  If no object is bound a :exc:`RuntimeError`
+    will be raised.
+
+    To create proxies to :class:`Local` or :class:`LocalStack` objects,
+    call the object as shown above.  If you want to have a proxy to an
+    object looked up by a function, you can (as of Werkzeug 0.6.1) pass
+    a function to the :class:`LocalProxy` constructor::
+
+        session = LocalProxy(lambda: get_current_request().session)
+
+    .. versionchanged:: 0.6.1
+       The class can be instanciated with a callable as well now.
+    """
+    __slots__ = ('__local', '__dict__', '__name__')
+
+    def __init__(self, local, name=None):
+        object.__setattr__(self, '_LocalProxy__local', local)
+        object.__setattr__(self, '__name__', name)
+
+    def _get_current_object(self):
+        """Return the current object.  This is useful if you want the real
+        object behind the proxy at a time for performance reasons or because
+        you want to pass the object into a different context.
+        """
+        if not hasattr(self.__local, '__release_local__'):
+            return self.__local()
+        try:
+            return getattr(self.__local, self.__name__)
+        except AttributeError:
+            raise RuntimeError('no object bound to %s' % self.__name__)
+
+    @property
+    def __dict__(self):
+        try:
+            return self._get_current_object().__dict__
+        except RuntimeError:
+            raise AttributeError('__dict__')
+
+    def __repr__(self):
+        try:
+            obj = self._get_current_object()
+        except RuntimeError:
+            return '<%s unbound>' % self.__class__.__name__
+        return repr(obj)
+
+    def __nonzero__(self):
+        try:
+            return bool(self._get_current_object())
+        except RuntimeError:
+            return False
+
+    def __unicode__(self):
+        try:
+            return unicode(self._get_current_object())
+        except RuntimeError:
+            return repr(self)
+
+    def __dir__(self):
+        try:
+            return dir(self._get_current_object())
+        except RuntimeError:
+            return []
+
+    def __getattr__(self, name):
+        if name == '__members__':
+            return dir(self._get_current_object())
+        return getattr(self._get_current_object(), name)
+
+    def __setitem__(self, key, value):
+        self._get_current_object()[key] = value
+
+    def __delitem__(self, key):
+        del self._get_current_object()[key]
+
+    def __setslice__(self, i, j, seq):
+        self._get_current_object()[i:j] = seq
+
+    def __delslice__(self, i, j):
+        del self._get_current_object()[i:j]
+
+    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
+    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
+    __str__ = lambda x: str(x._get_current_object())
+    __lt__ = lambda x, o: x._get_current_object() < o
+    __le__ = lambda x, o: x._get_current_object() <= o
+    __eq__ = lambda x, o: x._get_current_object() == o
+    __ne__ = lambda x, o: x._get_current_object() != o
+    __gt__ = lambda x, o: x._get_current_object() > o
+    __ge__ = lambda x, o: x._get_current_object() >= o
+    __cmp__ = lambda x, o: cmp(x._get_current_object(), o)
+    __hash__ = lambda x: hash(x._get_current_object())
+    __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
+    __len__ = lambda x: len(x._get_current_object())
+    __getitem__ = lambda x, i: x._get_current_object()[i]
+    __iter__ = lambda x: iter(x._get_current_object())
+    __contains__ = lambda x, i: i in x._get_current_object()
+    __getslice__ = lambda x, i, j: x._get_current_object()[i:j]
+    __add__ = lambda x, o: x._get_current_object() + o
+    __sub__ = lambda x, o: x._get_current_object() - o
+    __mul__ = lambda x, o: x._get_current_object() * o
+    __floordiv__ = lambda x, o: x._get_current_object() // o
+    __mod__ = lambda x, o: x._get_current_object() % o
+    __divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
+    __pow__ = lambda x, o: x._get_current_object() ** o
+    __lshift__ = lambda x, o: x._get_current_object() << o
+    __rshift__ = lambda x, o: x._get_current_object() >> o
+    __and__ = lambda x, o: x._get_current_object() & o
+    __xor__ = lambda x, o: x._get_current_object() ^ o
+    __or__ = lambda x, o: x._get_current_object() | o
+    __div__ = lambda x, o: x._get_current_object().__div__(o)
+    __truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
+    __neg__ = lambda x: -(x._get_current_object())
+    __pos__ = lambda x: +(x._get_current_object())
+    __abs__ = lambda x: abs(x._get_current_object())
+    __invert__ = lambda x: ~(x._get_current_object())
+    __complex__ = lambda x: complex(x._get_current_object())
+    __int__ = lambda x: int(x._get_current_object())
+    __long__ = lambda x: long(x._get_current_object())
+    __float__ = lambda x: float(x._get_current_object())
+    __oct__ = lambda x: oct(x._get_current_object())
+    __hex__ = lambda x: hex(x._get_current_object())
+    __index__ = lambda x: x._get_current_object().__index__()
+    __coerce__ = lambda x, o: x.__coerce__(x, o)
+    __enter__ = lambda x: x.__enter__()
+    __exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/posixemulation.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+r"""
+    werkzeug.posixemulation
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    Provides a POSIX emulation for some features that are relevant to
+    web applications.  The main purpose is to simplify support for
+    systems such as Windows NT that are not 100% POSIX compatible.
+
+    Currently this only implements a :func:`rename` function that
+    follows POSIX semantics.  Eg: if the target file already exists it
+    will be replaced without asking.
+
+    This module was introduced in 0.6.1 and is not a public interface.
+    It might become one in later versions of Werkzeug.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import sys
+import os
+import errno
+import time
+import random
+
+
+can_rename_open_file = False
+if os.name == 'nt': # pragma: no cover
+    _rename = lambda src, dst: False
+    _rename_atomic = lambda src, dst: False
+
+    try:
+        import ctypes
+
+        _MOVEFILE_REPLACE_EXISTING = 0x1
+        _MOVEFILE_WRITE_THROUGH = 0x8
+        _MoveFileEx = ctypes.windll.kernel32.MoveFileExW
+
+        def _rename(src, dst):
+            if not isinstance(src, unicode):
+                src = unicode(src, sys.getfilesystemencoding())
+            if not isinstance(dst, unicode):
+                dst = unicode(dst, sys.getfilesystemencoding())
+            if _rename_atomic(src, dst):
+                return True
+            retry = 0
+            rv = False
+            while not rv and retry < 100:
+                rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
+                                           _MOVEFILE_WRITE_THROUGH)
+                if not rv:
+                    time.sleep(0.001)
+                    retry += 1
+            return rv
+
+        # new in Vista and Windows Server 2008
+        _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction
+        _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction
+        _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW
+        _CloseHandle = ctypes.windll.kernel32.CloseHandle
+        can_rename_open_file = True
+
+        def _rename_atomic(src, dst):
+            ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename')
+            if ta == -1:
+                return False
+            try:
+                retry = 0
+                rv = False
+                while not rv and retry < 100:
+                    rv = _MoveFileTransacted(src, dst, None, None,
+                                             _MOVEFILE_REPLACE_EXISTING |
+                                             _MOVEFILE_WRITE_THROUGH, ta)
+                    if rv:
+                        rv = _CommitTransaction(ta)
+                        break
+                    else:
+                        time.sleep(0.001)
+                        retry += 1
+                return rv
+            finally:
+                _CloseHandle(ta)
+    except Exception:
+        pass
+
+    def rename(src, dst):
+        # Try atomic or pseudo-atomic rename
+        if _rename(src, dst):
+            return
+        # Fall back to "move away and replace"
+        try:
+            os.rename(src, dst)
+        except OSError, e:
+            if e.errno != errno.EEXIST:
+                raise
+            old = "%s-%08x" % (dst, random.randint(0, sys.maxint))
+            os.rename(dst, old)
+            os.rename(src, dst)
+            try:
+                os.unlink(old)
+            except Exception:
+                pass
+else:
+    rename = os.rename
+    can_rename_open_file = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/routing.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,1430 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.routing
+    ~~~~~~~~~~~~~~~~
+
+    When it comes to combining multiple controller or view functions (however
+    you want to call them) you need a dispatcher.  A simple way would be
+    applying regular expression tests on the ``PATH_INFO`` and calling
+    registered callback functions that return the value then.
+
+    This module implements a much more powerful system than simple regular
+    expression matching because it can also convert values in the URLs and
+    build URLs.
+
+    Here a simple example that creates an URL map for an application with
+    two subdomains (www and kb) and some URL rules:
+
+    >>> m = Map([
+    ...     # Static URLs
+    ...     Rule('/', endpoint='static/index'),
+    ...     Rule('/about', endpoint='static/about'),
+    ...     Rule('/help', endpoint='static/help'),
+    ...     # Knowledge Base
+    ...     Subdomain('kb', [
+    ...         Rule('/', endpoint='kb/index'),
+    ...         Rule('/browse/', endpoint='kb/browse'),
+    ...         Rule('/browse/<int:id>/', endpoint='kb/browse'),
+    ...         Rule('/browse/<int:id>/<int:page>', endpoint='kb/browse')
+    ...     ])
+    ... ], default_subdomain='www')
+
+    If the application doesn't use subdomains it's perfectly fine to not set
+    the default subdomain and not use the `Subdomain` rule factory.  The endpoint
+    in the rules can be anything, for example import paths or unique
+    identifiers.  The WSGI application can use those endpoints to get the
+    handler for that URL.  It doesn't have to be a string at all but it's
+    recommended.
+
+    Now it's possible to create a URL adapter for one of the subdomains and
+    build URLs:
+
+    >>> c = m.bind('example.com')
+    >>> c.build("kb/browse", dict(id=42))
+    'http://kb.example.com/browse/42/'
+    >>> c.build("kb/browse", dict())
+    'http://kb.example.com/browse/'
+    >>> c.build("kb/browse", dict(id=42, page=3))
+    'http://kb.example.com/browse/42/3'
+    >>> c.build("static/about")
+    '/about'
+    >>> c.build("static/index", force_external=True)
+    'http://www.example.com/'
+
+    >>> c = m.bind('example.com', subdomain='kb')
+    >>> c.build("static/about")
+    'http://www.example.com/about'
+
+    The first argument to bind is the server name *without* the subdomain.
+    Per default it will assume that the script is mounted on the root, but
+    often that's not the case so you can provide the real mount point as
+    second argument:
+
+    >>> c = m.bind('example.com', '/applications/example')
+
+    The third argument can be the subdomain, if not given the default
+    subdomain is used.  For more details about binding have a look at the
+    documentation of the `MapAdapter`.
+
+    And here is how you can match URLs:
+
+    >>> c = m.bind('example.com')
+    >>> c.match("/")
+    ('static/index', {})
+    >>> c.match("/about")
+    ('static/about', {})
+    >>> c = m.bind('example.com', '/', 'kb')
+    >>> c.match("/")
+    ('kb/index', {})
+    >>> c.match("/browse/42/23")
+    ('kb/browse', {'id': 42, 'page': 23})
+
+    If matching fails you get a `NotFound` exception, if the rule thinks
+    it's a good idea to redirect (for example because the URL was defined
+    to have a slash at the end but the request was missing that slash) it
+    will raise a `RequestRedirect` exception.  Both are subclasses of the
+    `HTTPException` so you can use those errors as responses in the
+    application.
+
+    If matching succeeded but the URL rule was incompatible to the given
+    method (for example there were only rules for `GET` and `HEAD` and
+    routing system tried to match a `POST` request) a `MethodNotAllowed`
+    method is raised.
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+                             Thomas Johansson.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+from pprint import pformat
+from urlparse import urljoin
+from itertools import izip
+
+from werkzeug.urls import url_encode, url_quote
+from werkzeug.utils import redirect, format_string
+from werkzeug.exceptions import HTTPException, NotFound, MethodNotAllowed
+from werkzeug._internal import _get_environ
+from werkzeug.datastructures import ImmutableDict, MultiDict
+
+
+_rule_re = re.compile(r'''
+    (?P<static>[^<]*)                           # static rule data
+    <
+    (?:
+        (?P<converter>[a-zA-Z_][a-zA-Z0-9_]*)   # converter name
+        (?:\((?P<args>.*?)\))?                  # converter arguments
+        \:                                      # variable delimiter
+    )?
+    (?P<variable>[a-zA-Z][a-zA-Z0-9_]*)         # variable name
+    >
+''', re.VERBOSE)
+_simple_rule_re = re.compile(r'<([^>]+)>')
+
+
+def parse_rule(rule):
+    """Parse a rule and return it as generator. Each iteration yields tuples
+    in the form ``(converter, arguments, variable)``. If the converter is
+    `None` it's a static url part, otherwise it's a dynamic one.
+
+    :internal:
+    """
+    pos = 0
+    end = len(rule)
+    do_match = _rule_re.match
+    used_names = set()
+    while pos < end:
+        m = do_match(rule, pos)
+        if m is None:
+            break
+        data = m.groupdict()
+        if data['static']:
+            yield None, None, data['static']
+        variable = data['variable']
+        converter = data['converter'] or 'default'
+        if variable in used_names:
+            raise ValueError('variable name %r used twice.' % variable)
+        used_names.add(variable)
+        yield converter, data['args'] or None, variable
+        pos = m.end()
+    if pos < end:
+        remaining = rule[pos:]
+        if '>' in remaining or '<' in remaining:
+            raise ValueError('malformed url rule: %r' % rule)
+        yield None, None, remaining
+
+
+def get_converter(map, name, args):
+    """Create a new converter for the given arguments or raise
+    exception if the converter does not exist.
+
+    :internal:
+    """
+    if not name in map.converters:
+        raise LookupError('the converter %r does not exist' % name)
+    if args:
+        storage = type('_Storage', (), {'__getitem__': lambda s, x: x})()
+        args, kwargs = eval(u'(lambda *a, **kw: (a, kw))(%s)' % args, {}, storage)
+    else:
+        args = ()
+        kwargs = {}
+    return map.converters[name](map, *args, **kwargs)
+
+
+class RoutingException(Exception):
+    """Special exceptions that require the application to redirect, notifying
+    about missing urls, etc.
+
+    :internal:
+    """
+
+
+class RequestRedirect(HTTPException, RoutingException):
+    """Raise if the map requests a redirect. This is for example the case if
+    `strict_slashes` are activated and an url that requires a trailing slash.
+
+    The attribute `new_url` contains the absolute destination url.
+    """
+    code = 301
+
+    def __init__(self, new_url):
+        RoutingException.__init__(self, new_url)
+        self.new_url = new_url
+
+    def get_response(self, environ):
+        return redirect(self.new_url, 301)
+
+
+class RequestSlash(RoutingException):
+    """Internal exception."""
+
+
+class BuildError(RoutingException, LookupError):
+    """Raised if the build system cannot find a URL for an endpoint with the
+    values provided.
+    """
+
+    def __init__(self, endpoint, values, method):
+        LookupError.__init__(self, endpoint, values, method)
+        self.endpoint = endpoint
+        self.values = values
+        self.method = method
+
+
+class ValidationError(ValueError):
+    """Validation error.  If a rule converter raises this exception the rule
+    does not match the current URL and the next URL is tried.
+    """
+
+
+class RuleFactory(object):
+    """As soon as you have more complex URL setups it's a good idea to use rule
+    factories to avoid repetitive tasks.  Some of them are builtin, others can
+    be added by subclassing `RuleFactory` and overriding `get_rules`.
+    """
+
+    def get_rules(self, map):
+        """Subclasses of `RuleFactory` have to override this method and return
+        an iterable of rules."""
+        raise NotImplementedError()
+
+
+class Subdomain(RuleFactory):
+    """All URLs provided by this factory have the subdomain set to a
+    specific domain. For example if you want to use the subdomain for
+    the current language this can be a good setup::
+
+        url_map = Map([
+            Rule('/', endpoint='#select_language'),
+            Subdomain('<string(length=2):lang_code>', [
+                Rule('/', endpoint='index'),
+                Rule('/about', endpoint='about'),
+                Rule('/help', endpoint='help')
+            ])
+        ])
+
+    All the rules except for the ``'#select_language'`` endpoint will now
+    listen on a two letter long subdomain that holds the language code
+    for the current request.
+    """
+
+    def __init__(self, subdomain, rules):
+        self.subdomain = subdomain
+        self.rules = rules
+
+    def get_rules(self, map):
+        for rulefactory in self.rules:
+            for rule in rulefactory.get_rules(map):
+                rule = rule.empty()
+                rule.subdomain = self.subdomain
+                yield rule
+
+
+class Submount(RuleFactory):
+    """Like `Subdomain` but prefixes the URL rule with a given string::
+
+        url_map = Map([
+            Rule('/', endpoint='index'),
+            Submount('/blog', [
+                Rule('/', endpoint='blog/index'),
+                Rule('/entry/<entry_slug>', endpoint='blog/show')
+            ])
+        ])
+
+    Now the rule ``'blog/show'`` matches ``/blog/entry/<entry_slug>``.
+    """
+
+    def __init__(self, path, rules):
+        self.path = path.rstrip('/')
+        self.rules = rules
+
+    def get_rules(self, map):
+        for rulefactory in self.rules:
+            for rule in rulefactory.get_rules(map):
+                rule = rule.empty()
+                rule.rule = self.path + rule.rule
+                yield rule
+
+
+class EndpointPrefix(RuleFactory):
+    """Prefixes all endpoints (which must be strings for this factory) with
+    another string. This can be useful for sub applications::
+
+        url_map = Map([
+            Rule('/', endpoint='index'),
+            EndpointPrefix('blog/', [Submount('/blog', [
+                Rule('/', endpoint='index'),
+                Rule('/entry/<entry_slug>', endpoint='show')
+            ])])
+        ])
+    """
+
+    def __init__(self, prefix, rules):
+        self.prefix = prefix
+        self.rules = rules
+
+    def get_rules(self, map):
+        for rulefactory in self.rules:
+            for rule in rulefactory.get_rules(map):
+                rule = rule.empty()
+                rule.endpoint = self.prefix + rule.endpoint
+                yield rule
+
+
+class RuleTemplate(object):
+    """Returns copies of the rules wrapped and expands string templates in
+    the endpoint, rule, defaults or subdomain sections.
+
+    Here a small example for such a rule template::
+
+        from werkzeug.routing import Map, Rule, RuleTemplate
+
+        resource = RuleTemplate([
+            Rule('/$name/', endpoint='$name.list'),
+            Rule('/$name/<int:id>', endpoint='$name.show')
+        ])
+
+        url_map = Map([resource(name='user'), resource(name='page')])
+
+    When a rule template is called the keyword arguments are used to
+    replace the placeholders in all the string parameters.
+    """
+
+    def __init__(self, rules):
+        self.rules = list(rules)
+
+    def __call__(self, *args, **kwargs):
+        return RuleTemplateFactory(self.rules, dict(*args, **kwargs))
+
+
+class RuleTemplateFactory(RuleFactory):
+    """A factory that fills in template variables into rules.  Used by
+    `RuleTemplate` internally.
+
+    :internal:
+    """
+
+    def __init__(self, rules, context):
+        self.rules = rules
+        self.context = context
+
+    def get_rules(self, map):
+        for rulefactory in self.rules:
+            for rule in rulefactory.get_rules(map):
+                new_defaults = subdomain = None
+                if rule.defaults is not None:
+                    new_defaults = {}
+                    for key, value in rule.defaults.iteritems():
+                        if isinstance(value, basestring):
+                            value = format_string(value, self.context)
+                        new_defaults[key] = value
+                if rule.subdomain is not None:
+                    subdomain = format_string(rule.subdomain, self.context)
+                new_endpoint = rule.endpoint
+                if isinstance(new_endpoint, basestring):
+                    new_endpoint = format_string(new_endpoint, self.context)
+                yield Rule(
+                    format_string(rule.rule, self.context),
+                    new_defaults,
+                    subdomain,
+                    rule.methods,
+                    rule.build_only,
+                    new_endpoint,
+                    rule.strict_slashes
+                )
+
+
+class Rule(RuleFactory):
+    """A Rule represents one URL pattern.  There are some options for `Rule`
+    that change the way it behaves and are passed to the `Rule` constructor.
+    Note that besides the rule-string all arguments *must* be keyword arguments
+    in order to not break the application on Werkzeug upgrades.
+
+    `string`
+        Rule strings basically are just normal URL paths with placeholders in
+        the format ``<converter(arguments):name>`` where the converter and the
+        arguments are optional.  If no converter is defined the `default`
+        converter is used which means `string` in the normal configuration.
+
+        URL rules that end with a slash are branch URLs, others are leaves.
+        If you have `strict_slashes` enabled (which is the default), all
+        branch URLs that are matched without a trailing slash will trigger a
+        redirect to the same URL with the missing slash appended.
+
+        The converters are defined on the `Map`.
+
+    `endpoint`
+        The endpoint for this rule. This can be anything. A reference to a
+        function, a string, a number etc.  The preferred way is using a string
+        because the endpoint is used for URL generation.
+
+    `defaults`
+        An optional dict with defaults for other rules with the same endpoint.
+        This is a bit tricky but useful if you want to have unique URLs::
+
+            url_map = Map([
+                Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
+                Rule('/all/page/<int:page>', endpoint='all_entries')
+            ])
+
+        If a user now visits ``http://example.com/all/page/1`` he will be
+        redirected to ``http://example.com/all/``.  If `redirect_defaults` is
+        disabled on the `Map` instance this will only affect the URL
+        generation.
+
+    `subdomain`
+        The subdomain rule string for this rule. If not specified the rule
+        only matches for the `default_subdomain` of the map.  If the map is
+        not bound to a subdomain this feature is disabled.
+
+        Can be useful if you want to have user profiles on different subdomains
+        and all subdomains are forwarded to your application::
+
+            url_map = Map([
+                Rule('/', subdomain='<username>', endpoint='user/homepage'),
+                Rule('/stats', subdomain='<username>', endpoint='user/stats')
+            ])
+
+    `methods`
+        A sequence of http methods this rule applies to.  If not specified, all
+        methods are allowed. For example this can be useful if you want different
+        endpoints for `POST` and `GET`.  If methods are defined and the path
+        matches but the method matched against is not in this list or in the
+        list of another rule for that path the error raised is of the type
+        `MethodNotAllowed` rather than `NotFound`.  If `GET` is present in the
+        list of methods and `HEAD` is not, `HEAD` is added automatically.
+
+        .. versionchanged:: 0.6.1
+           `HEAD` is now automatically added to the methods if `GET` is
+           present.  The reason for this is that existing code often did not
+           work properly in servers not rewriting `HEAD` to `GET`
+           automatically and it was not documented how `HEAD` should be
+           treated.  This was considered a bug in Werkzeug because of that.
+
+    `strict_slashes`
+        Override the `Map` setting for `strict_slashes` only for this rule. If
+        not specified the `Map` setting is used.
+
+    `build_only`
+        Set this to True and the rule will never match but will create a URL
+        that can be build. This is useful if you have resources on a subdomain
+        or folder that are not handled by the WSGI application (like static data)
+
+    `redirect_to`
+        If given this must be either a string or callable.  In case of a
+        callable it's called with the url adapter that triggered the match and
+        the values of the URL as keyword arguments and has to return the target
+        for the redirect, otherwise it has to be a string with placeholders in
+        rule syntax::
+
+            def foo_with_slug(adapter, id):
+                # ask the database for the slug for the old id.  this of
+                # course has nothing to do with werkzeug.
+                return 'foo/' + Foo.get_slug_for_id(id)
+
+            url_map = Map([
+                Rule('/foo/<slug>', endpoint='foo'),
+                Rule('/some/old/url/<slug>', redirect_to='foo/<slug>'),
+                Rule('/other/old/url/<int:id>', redirect_to=foo_with_slug)
+            ])
+
+        When the rule is matched the routing system will raise a
+        `RequestRedirect` exception with the target for the redirect.
+
+        Keep in mind that the URL will be joined against the URL root of the
+        script so don't use a leading slash on the target URL unless you
+        really mean root of that domain.
+    """
+
+    def __init__(self, string, defaults=None, subdomain=None, methods=None,
+                 build_only=False, endpoint=None, strict_slashes=None,
+                 redirect_to=None):
+        if not string.startswith('/'):
+            raise ValueError('urls must start with a leading slash')
+        self.rule = string
+        self.is_leaf = not string.endswith('/')
+
+        self.map = None
+        self.strict_slashes = strict_slashes
+        self.subdomain = subdomain
+        self.defaults = defaults
+        self.build_only = build_only
+        if methods is None:
+            self.methods = None
+        else:
+            self.methods = set([x.upper() for x in methods])
+            if 'HEAD' not in self.methods and 'GET' in self.methods:
+                self.methods.add('HEAD')
+        self.endpoint = endpoint
+        self.greediness = 0
+        self.redirect_to = redirect_to
+
+        if defaults is not None:
+            self.arguments = set(map(str, defaults))
+        else:
+            self.arguments = set()
+        self._trace = self._converters = self._regex = self._weights = None
+
+    def empty(self):
+        """Return an unbound copy of this rule.  This can be useful if you
+        want to reuse an already bound URL for another map."""
+        defaults = None
+        if self.defaults is not None:
+            defaults = dict(self.defaults)
+        return Rule(self.rule, defaults, self.subdomain, self.methods,
+                    self.build_only, self.endpoint, self.strict_slashes,
+                    self.redirect_to)
+
+    def get_rules(self, map):
+        yield self
+
+    def refresh(self):
+        """Rebinds and refreshes the URL.  Call this if you modified the
+        rule in place.
+
+        :internal:
+        """
+        self.bind(self.map, rebind=True)
+
+    def bind(self, map, rebind=False):
+        """Bind the url to a map and create a regular expression based on
+        the information from the rule itself and the defaults from the map.
+
+        :internal:
+        """
+        if self.map is not None and not rebind:
+            raise RuntimeError('url rule %r already bound to map %r' %
+                               (self, self.map))
+        self.map = map
+        if self.strict_slashes is None:
+            self.strict_slashes = map.strict_slashes
+        if self.subdomain is None:
+            self.subdomain = map.default_subdomain
+
+        rule = self.subdomain + '|' + (self.is_leaf and self.rule
+                                       or self.rule.rstrip('/'))
+
+        self._trace = []
+        self._converters = {}
+        self._weights = []
+
+        regex_parts = []
+        for converter, arguments, variable in parse_rule(rule):
+            if converter is None:
+                regex_parts.append(re.escape(variable))
+                self._trace.append((False, variable))
+                self._weights.append(len(variable))
+            else:
+                convobj = get_converter(map, converter, arguments)
+                regex_parts.append('(?P<%s>%s)' % (variable, convobj.regex))
+                self._converters[variable] = convobj
+                self._trace.append((True, variable))
+                self._weights.append(convobj.weight)
+                self.arguments.add(str(variable))
+                if convobj.is_greedy:
+                    self.greediness += 1
+        if not self.is_leaf:
+            self._trace.append((False, '/'))
+
+        if not self.build_only:
+            regex = r'^%s%s$' % (
+                u''.join(regex_parts),
+                (not self.is_leaf or not self.strict_slashes) and \
+                    '(?<!/)(?P<__suffix__>/?)' or ''
+            )
+            self._regex = re.compile(regex, re.UNICODE)
+
+    def match(self, path):
+        """Check if the rule matches a given path. Path is a string in the
+        form ``"subdomain|/path(method)"`` and is assembled by the map.
+
+        If the rule matches a dict with the converted values is returned,
+        otherwise the return value is `None`.
+
+        :internal:
+        """
+        if not self.build_only:
+            m = self._regex.search(path)
+            if m is not None:
+                groups = m.groupdict()
+                # we have a folder like part of the url without a trailing
+                # slash and strict slashes enabled. raise an exception that
+                # tells the map to redirect to the same url but with a
+                # trailing slash
+                if self.strict_slashes and not self.is_leaf and \
+                   not groups.pop('__suffix__'):
+                    raise RequestSlash()
+                # if we are not in strict slashes mode we have to remove
+                # a __suffix__
+                elif not self.strict_slashes:
+                    del groups['__suffix__']
+
+                result = {}
+                for name, value in groups.iteritems():
+                    try:
+                        value = self._converters[name].to_python(value)
+                    except ValidationError:
+                        return
+                    result[str(name)] = value
+                if self.defaults is not None:
+                    result.update(self.defaults)
+                return result
+
+    def build(self, values, append_unknown=True):
+        """Assembles the relative url for that rule and the subdomain.
+        If building doesn't work for some reasons `None` is returned.
+
+        :internal:
+        """
+        tmp = []
+        add = tmp.append
+        processed = set(self.arguments)
+        for is_dynamic, data in self._trace:
+            if is_dynamic:
+                try:
+                    add(self._converters[data].to_url(values[data]))
+                except ValidationError:
+                    return
+                processed.add(data)
+            else:
+                add(data)
+        subdomain, url = (u''.join(tmp)).split('|', 1)
+
+        if append_unknown:
+            query_vars = MultiDict(values)
+            for key in processed:
+                if key in query_vars:
+                    del query_vars[key]
+
+            if query_vars:
+                url += '?' + url_encode(query_vars, self.map.charset,
+                                        sort=self.map.sort_parameters,
+                                        key=self.map.sort_key)
+
+        return subdomain, url
+
+    def provides_defaults_for(self, rule):
+        """Check if this rule has defaults for a given rule.
+
+        :internal:
+        """
+        return not self.build_only and self.defaults is not None and \
+               self.endpoint == rule.endpoint and self != rule and \
+               self.arguments == rule.arguments
+
+    def suitable_for(self, values, method=None):
+        """Check if the dict of values has enough data for url generation.
+
+        :internal:
+        """
+        if method is not None:
+            if self.methods is not None and method not in self.methods:
+                return False
+
+        valueset = set(values)
+
+        for key in self.arguments - set(self.defaults or ()):
+            if key not in values:
+                return False
+
+        if self.arguments.issubset(valueset):
+            if self.defaults is None:
+                return True
+            for key, value in self.defaults.iteritems():
+                if value != values[key]:
+                    return False
+
+        return True
+
+    def match_compare(self, other):
+        """Compare this object with another one for matching.
+
+        :internal:
+        """
+        for sw, ow in izip(self._weights, other._weights):
+            if sw > ow:
+                return -1
+            elif sw < ow:
+                return 1
+        if len(self._weights) > len(other._weights):
+            return -1
+        if len(self._weights) < len(other._weights):
+            return 1
+        if not other.arguments and self.arguments:
+            return 1
+        elif other.arguments and not self.arguments:
+            return -1
+        elif other.defaults is None and self.defaults is not None:
+            return 1
+        elif other.defaults is not None and self.defaults is None:
+            return -1
+        elif self.greediness > other.greediness:
+            return -1
+        elif self.greediness < other.greediness:
+            return 1
+        elif len(self.arguments) > len(other.arguments):
+            return 1
+        elif len(self.arguments) < len(other.arguments):
+            return -1
+        return 1
+
+    def build_compare(self, other):
+        """Compare this object with another one for building.
+
+        :internal:
+        """
+        if not other.arguments and self.arguments:
+            return -1
+        elif other.arguments and not self.arguments:
+            return 1
+        elif other.defaults is None and self.defaults is not None:
+            return -1
+        elif other.defaults is not None and self.defaults is None:
+            return 1
+        elif self.provides_defaults_for(other):
+            return -1
+        elif other.provides_defaults_for(self):
+            return 1
+        elif self.greediness > other.greediness:
+            return -1
+        elif self.greediness < other.greediness:
+            return 1
+        elif len(self.arguments) > len(other.arguments):
+            return -1
+        elif len(self.arguments) < len(other.arguments):
+            return 1
+        return -1
+
+    def __eq__(self, other):
+        return self.__class__ is other.__class__ and \
+               self._trace == other._trace
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __unicode__(self):
+        return self.rule
+
+    def __str__(self):
+        charset = self.map is not None and self.map.charset or 'utf-8'
+        return unicode(self).encode(charset)
+
+    def __repr__(self):
+        if self.map is None:
+            return '<%s (unbound)>' % self.__class__.__name__
+        charset = self.map is not None and self.map.charset or 'utf-8'
+        tmp = []
+        for is_dynamic, data in self._trace:
+            if is_dynamic:
+                tmp.append('<%s>' % data)
+            else:
+                tmp.append(data)
+        return '<%s %r%s -> %s>' % (
+            self.__class__.__name__,
+            (u''.join(tmp).encode(charset)).lstrip('|'),
+            self.methods is not None and ' (%s)' % \
+                ', '.join(self.methods) or '',
+            self.endpoint
+        )
+
+
+class BaseConverter(object):
+    """Base class for all converters."""
+    regex = '[^/]+'
+    is_greedy = False
+    weight = 100
+
+    def __init__(self, map):
+        self.map = map
+
+    def to_python(self, value):
+        return value
+
+    def to_url(self, value):
+        return url_quote(value, self.map.charset)
+
+
+class UnicodeConverter(BaseConverter):
+    """This converter is the default converter and accepts any string but
+    only one path segment.  Thus the string can not include a slash.
+
+    This is the default validator.
+
+    Example::
+
+        Rule('/pages/<page>'),
+        Rule('/<string(length=2):lang_code>')
+
+    :param map: the :class:`Map`.
+    :param minlength: the minimum length of the string.  Must be greater
+                      or equal 1.
+    :param maxlength: the maximum length of the string.
+    :param length: the exact length of the string.
+    """
+
+    def __init__(self, map, minlength=1, maxlength=None, length=None):
+        BaseConverter.__init__(self, map)
+        if length is not None:
+            length = '{%d}' % int(length)
+        else:
+            if maxlength is None:
+                maxlength = ''
+            else:
+                maxlength = int(maxlength)
+            length = '{%s,%s}' % (
+                int(minlength),
+                maxlength
+            )
+        self.regex = '[^/]' + length
+
+
+class AnyConverter(BaseConverter):
+    """Matches one of the items provided.  Items can either be Python
+    identifiers or unicode strings::
+
+        Rule('/<any(about, help, imprint, u"class"):page_name>')
+
+    :param map: the :class:`Map`.
+    :param items: this function accepts the possible items as positional
+                  arguments.
+    """
+
+    def __init__(self, map, *items):
+        BaseConverter.__init__(self, map)
+        self.regex = '(?:%s)' % '|'.join([re.escape(x) for x in items])
+
+
+class PathConverter(BaseConverter):
+    """Like the default :class:`UnicodeConverter`, but it also matches
+    slashes.  This is useful for wikis and similar applications::
+
+        Rule('/<path:wikipage>')
+        Rule('/<path:wikipage>/edit')
+
+    :param map: the :class:`Map`.
+    """
+    regex = '[^/].*?'
+    is_greedy = True
+    weight = 50
+
+
+class NumberConverter(BaseConverter):
+    """Baseclass for `IntegerConverter` and `FloatConverter`.
+
+    :internal:
+    """
+
+    def __init__(self, map, fixed_digits=0, min=None, max=None):
+        BaseConverter.__init__(self, map)
+        self.fixed_digits = fixed_digits
+        self.min = min
+        self.max = max
+
+    def to_python(self, value):
+        if (self.fixed_digits and len(value) != self.fixed_digits):
+            raise ValidationError()
+        value = self.num_convert(value)
+        if (self.min is not None and value < self.min) or \
+           (self.max is not None and value > self.max):
+            raise ValidationError()
+        return value
+
+    def to_url(self, value):
+        value = self.num_convert(value)
+        if self.fixed_digits:
+            value = ('%%0%sd' % self.fixed_digits) % value
+        return str(value)
+
+
+class IntegerConverter(NumberConverter):
+    """This converter only accepts integer values::
+
+        Rule('/page/<int:page>')
+
+    This converter does not support negative values.
+
+    :param map: the :class:`Map`.
+    :param fixed_digits: the number of fixed digits in the URL.  If you set
+                         this to ``4`` for example, the application will
+                         only match if the url looks like ``/0001/``.  The
+                         default is variable length.
+    :param min: the minimal value.
+    :param max: the maximal value.
+    """
+    regex = r'\d+'
+    num_convert = int
+
+
+class FloatConverter(NumberConverter):
+    """This converter only accepts floating point values::
+
+        Rule('/probability/<float:probability>')
+
+    This converter does not support negative values.
+
+    :param map: the :class:`Map`.
+    :param min: the minimal value.
+    :param max: the maximal value.
+    """
+    regex = r'\d+\.\d+'
+    num_convert = float
+
+    def __init__(self, map, min=None, max=None):
+        NumberConverter.__init__(self, map, 0, min, max)
+
+
+#: the default converter mapping for the map.
+DEFAULT_CONVERTERS = {
+    'default':          UnicodeConverter,
+    'string':           UnicodeConverter,
+    'any':              AnyConverter,
+    'path':             PathConverter,
+    'int':              IntegerConverter,
+    'float':            FloatConverter
+}
+
+
+class Map(object):
+    """The map class stores all the URL rules and some configuration
+    parameters.  Some of the configuration values are only stored on the
+    `Map` instance since those affect all rules, others are just defaults
+    and can be overridden for each rule.  Note that you have to specify all
+    arguments besides the `rules` as keyword arguments!
+
+    :param rules: sequence of url rules for this map.
+    :param default_subdomain: The default subdomain for rules without a
+                              subdomain defined.
+    :param charset: charset of the url. defaults to ``"utf-8"``
+    :param strict_slashes: Take care of trailing slashes.
+    :param redirect_defaults: This will redirect to the default rule if it
+                              wasn't visited that way. This helps creating
+                              unique URLs.
+    :param converters: A dict of converters that adds additional converters
+                       to the list of converters. If you redefine one
+                       converter this will override the original one.
+    :param sort_parameters: If set to `True` the url parameters are sorted.
+                            See `url_encode` for more details.
+    :param sort_key: The sort key function for `url_encode`.
+
+    .. versionadded:: 0.5
+        `sort_parameters` and `sort_key` was added.
+    """
+
+    #: .. versionadded:: 0.6
+    #:    a dict of default converters to be used.
+    default_converters = ImmutableDict(DEFAULT_CONVERTERS)
+
+    def __init__(self, rules=None, default_subdomain='', charset='utf-8',
+                 strict_slashes=True, redirect_defaults=True,
+                 converters=None, sort_parameters=False, sort_key=None):
+        self._rules = []
+        self._rules_by_endpoint = {}
+        self._remap = True
+
+        self.default_subdomain = default_subdomain
+        self.charset = charset
+        self.strict_slashes = strict_slashes
+        self.redirect_defaults = redirect_defaults
+
+        self.converters = self.default_converters.copy()
+        if converters:
+            self.converters.update(converters)
+
+        self.sort_parameters = sort_parameters
+        self.sort_key = sort_key
+
+        for rulefactory in rules or ():
+            self.add(rulefactory)
+
+    def is_endpoint_expecting(self, endpoint, *arguments):
+        """Iterate over all rules and check if the endpoint expects
+        the arguments provided.  This is for example useful if you have
+        some URLs that expect a language code and others that do not and
+        you want to wrap the builder a bit so that the current language
+        code is automatically added if not provided but endpoints expect
+        it.
+
+        :param endpoint: the endpoint to check.
+        :param arguments: this function accepts one or more arguments
+                          as positional arguments.  Each one of them is
+                          checked.
+        """
+        self.update()
+        arguments = set(arguments)
+        for rule in self._rules_by_endpoint[endpoint]:
+            if arguments.issubset(rule.arguments):
+                return True
+        return False
+
+    def iter_rules(self, endpoint=None):
+        """Iterate over all rules or the rules of an endpoint.
+
+        :param endpoint: if provided only the rules for that endpoint
+                         are returned.
+        :return: an iterator
+        """
+        if endpoint is not None:
+            return iter(self._rules_by_endpoint[endpoint])
+        return iter(self._rules)
+
+    def add(self, rulefactory):
+        """Add a new rule or factory to the map and bind it.  Requires that the
+        rule is not bound to another map.
+
+        :param rulefactory: a :class:`Rule` or :class:`RuleFactory`
+        """
+        for rule in rulefactory.get_rules(self):
+            rule.bind(self)
+            self._rules.append(rule)
+            self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
+        self._remap = True
+
+    def bind(self, server_name, script_name=None, subdomain=None,
+             url_scheme='http', default_method='GET', path_info=None):
+        """Return a new :class:`MapAdapter` with the details specified to the
+        call.  Note that `script_name` will default to ``'/'`` if not further
+        specified or `None`.  The `server_name` at least is a requirement
+        because the HTTP RFC requires absolute URLs for redirects and so all
+        redirect exceptions raised by Werkzeug will contain the full canonical
+        URL.
+
+        If no path_info is passed to :meth:`match` it will use the default path
+        info passed to bind.  While this doesn't really make sense for
+        manual bind calls, it's useful if you bind a map to a WSGI
+        environment which already contains the path info.
+
+        `subdomain` will default to the `default_subdomain` for this map if
+        no defined. If there is no `default_subdomain` you cannot use the
+        subdomain feature.
+        """
+        if subdomain is None:
+            subdomain = self.default_subdomain
+        if script_name is None:
+            script_name = '/'
+        return MapAdapter(self, server_name, script_name, subdomain,
+                          url_scheme, path_info, default_method)
+
+    def bind_to_environ(self, environ, server_name=None, subdomain=None):
+        """Like :meth:`bind` but you can pass it an WSGI environment and it
+        will fetch the information from that dictionary.  Note that because of
+        limitations in the protocol there is no way to get the current
+        subdomain and real `server_name` from the environment.  If you don't
+        provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or
+        `HTTP_HOST` if provided) as used `server_name` with disabled subdomain
+        feature.
+
+        If `subdomain` is `None` but an environment and a server name is
+        provided it will calculate the current subdomain automatically.
+        Example: `server_name` is ``'example.com'`` and the `SERVER_NAME`
+        in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated
+        subdomain will be ``'staging.dev'``.
+
+        If the object passed as environ has an environ attribute, the value of
+        this attribute is used instead.  This allows you to pass request
+        objects.  Additionally `PATH_INFO` added as a default of the
+        :class:`MapAdapter` so that you don't have to pass the path info to
+        the match method.
+
+        .. versionchanged:: 0.5
+            previously this method accepted a bogus `calculate_subdomain`
+            parameter that did not have any effect.  It was removed because
+            of that.
+
+        :param environ: a WSGI environment.
+        :param server_name: an optional server name hint (see above).
+        :param subdomain: optionally the current subdomain (see above).
+        """
+        environ = _get_environ(environ)
+        if server_name is None:
+            if 'HTTP_HOST' in environ:
+                server_name = environ['HTTP_HOST']
+            else:
+                server_name = environ['SERVER_NAME']
+                if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \
+                   in (('https', '443'), ('http', '80')):
+                    server_name += ':' + environ['SERVER_PORT']
+        elif subdomain is None:
+            wsgi_server_name = environ.get('HTTP_HOST', environ['SERVER_NAME'])
+            cur_server_name = wsgi_server_name.split(':', 1)[0].split('.')
+            real_server_name = server_name.split(':', 1)[0].split('.')
+            offset = -len(real_server_name)
+            if cur_server_name[offset:] != real_server_name:
+                raise ValueError('the server name provided (%r) does not '
+                                 'match the server name from the WSGI '
+                                 'environment (%r)' %
+                                 (server_name, wsgi_server_name))
+            subdomain = '.'.join(filter(None, cur_server_name[:offset]))
+        return Map.bind(self, server_name, environ.get('SCRIPT_NAME'),
+                        subdomain, environ['wsgi.url_scheme'],
+                        environ['REQUEST_METHOD'], environ.get('PATH_INFO'))
+
+    def update(self):
+        """Called before matching and building to keep the compiled rules
+        in the correct order after things changed.
+        """
+        if self._remap:
+            self._rules.sort(lambda a, b: a.match_compare(b))
+            for rules in self._rules_by_endpoint.itervalues():
+                rules.sort(lambda a, b: a.build_compare(b))
+            self._remap = False
+
+    def __repr__(self):
+        rules = self.iter_rules()
+        return '%s([%s])' % (self.__class__.__name__, pformat(list(rules)))
+
+
+class MapAdapter(object):
+    """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does
+    the URL matching and building based on runtime information.
+    """
+
+    def __init__(self, map, server_name, script_name, subdomain,
+                 url_scheme, path_info, default_method):
+        self.map = map
+        self.server_name = server_name
+        if not script_name.endswith('/'):
+            script_name += '/'
+        self.script_name = script_name
+        self.subdomain = subdomain
+        self.url_scheme = url_scheme
+        self.path_info = path_info or u''
+        self.default_method = default_method
+
+    def dispatch(self, view_func, path_info=None, method=None,
+                 catch_http_exceptions=False):
+        """Does the complete dispatching process.  `view_func` is called with
+        the endpoint and a dict with the values for the view.  It should
+        look up the view function, call it, and return a response object
+        or WSGI application.  http exceptions are not caught by default
+        so that applications can display nicer error messages by just
+        catching them by hand.  If you want to stick with the default
+        error messages you can pass it ``catch_http_exceptions=True`` and
+        it will catch the http exceptions.
+
+        Here a small example for the dispatch usage::
+
+            from werkzeug import Request, Response, responder
+            from werkzeug.routing import Map, Rule
+
+            def on_index(request):
+                return Response('Hello from the index')
+
+            url_map = Map([Rule('/', endpoint='index')])
+            views = {'index': on_index}
+
+            @responder
+            def application(environ, start_response):
+                request = Request(environ)
+                urls = url_map.bind_to_environ(environ)
+                return urls.dispatch(lambda e, v: views[e](request, **v),
+                                     catch_http_exceptions=True)
+
+        Keep in mind that this method might return exception objects, too, so
+        use :class:`Response.force_type` to get a response object.
+
+        :param view_func: a function that is called with the endpoint as
+                          first argument and the value dict as second.  Has
+                          to dispatch to the actual view function with this
+                          information.  (see above)
+        :param path_info: the path info to use for matching.  Overrides the
+                          path info specified on binding.
+        :param method: the HTTP method used for matching.  Overrides the
+                       method specified on binding.
+        :param catch_http_exceptions: set to `True` to catch any of the
+                                      werkzeug :class:`HTTPException`\s.
+        """
+        try:
+            try:
+                endpoint, args = self.match(path_info, method)
+            except RequestRedirect, e:
+                return e
+            return view_func(endpoint, args)
+        except HTTPException, e:
+            if catch_http_exceptions:
+                return e
+            raise
+
+    def match(self, path_info=None, method=None, return_rule=False):
+        """The usage is simple: you just pass the match method the current
+        path info as well as the method (which defaults to `GET`).  The
+        following things can then happen:
+
+        - you receive a `NotFound` exception that indicates that no URL is
+          matching.  A `NotFound` exception is also a WSGI application you
+          can call to get a default page not found page (happens to be the
+          same object as `werkzeug.exceptions.NotFound`)
+
+        - you receive a `MethodNotAllowed` exception that indicates that there
+          is a match for this URL but not for the current request method.
+          This is useful for RESTful applications.
+
+        - you receive a `RequestRedirect` exception with a `new_url`
+          attribute.  This exception is used to notify you about a request
+          Werkzeug requests from your WSGI application.  This is for example the
+          case if you request ``/foo`` although the correct URL is ``/foo/``
+          You can use the `RequestRedirect` instance as response-like object
+          similar to all other subclasses of `HTTPException`.
+
+        - you get a tuple in the form ``(endpoint, arguments)`` if there is
+          a match (unless `return_rule` is True, in which case you get a tuple
+          in the form ``(rule, arguments)``)
+
+        If the path info is not passed to the match method the default path
+        info of the map is used (defaults to the root URL if not defined
+        explicitly).
+
+        All of the exceptions raised are subclasses of `HTTPException` so they
+        can be used as WSGI responses.  The will all render generic error or
+        redirect pages.
+
+        Here is a small example for matching:
+
+        >>> m = Map([
+        ...     Rule('/', endpoint='index'),
+        ...     Rule('/downloads/', endpoint='downloads/index'),
+        ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
+        ... ])
+        >>> urls = m.bind("example.com", "/")
+        >>> urls.match("/", "GET")
+        ('index', {})
+        >>> urls.match("/downloads/42")
+        ('downloads/show', {'id': 42})
+
+        And here is what happens on redirect and missing URLs:
+
+        >>> urls.match("/downloads")
+        Traceback (most recent call last):
+          ...
+        RequestRedirect: http://example.com/downloads/
+        >>> urls.match("/missing")
+        Traceback (most recent call last):
+          ...
+        NotFound: 404 Not Found
+
+        :param path_info: the path info to use for matching.  Overrides the
+                          path info specified on binding.
+        :param method: the HTTP method used for matching.  Overrides the
+                       method specified on binding.
+        :param return_rule: return the rule that matched instead of just the
+                            endpoint (defaults to `False`).
+
+        .. versionadded:: 0.6
+            `return_rule` was added.
+        """
+        self.map.update()
+        if path_info is None:
+            path_info = self.path_info
+        if not isinstance(path_info, unicode):
+            path_info = path_info.decode(self.map.charset, 'ignore')
+        method = (method or self.default_method).upper()
+        path = u'%s|/%s' % (self.subdomain, path_info.lstrip('/'))
+        have_match_for = set()
+        for rule in self.map._rules:
+            try:
+                rv = rule.match(path)
+            except RequestSlash:
+                raise RequestRedirect(str('%s://%s%s%s/%s/' % (
+                    self.url_scheme,
+                    self.subdomain and self.subdomain + '.' or '',
+                    self.server_name,
+                    self.script_name[:-1],
+                    url_quote(path_info.lstrip('/'), self.map.charset)
+                )))
+            if rv is None:
+                continue
+            if rule.methods is not None and method not in rule.methods:
+                have_match_for.update(rule.methods)
+                continue
+            if self.map.redirect_defaults:
+                for r in self.map._rules_by_endpoint[rule.endpoint]:
+                    if r.provides_defaults_for(rule) and \
+                       r.suitable_for(rv, method):
+                        rv.update(r.defaults)
+                        subdomain, path = r.build(rv)
+                        raise RequestRedirect(str('%s://%s%s%s/%s' % (
+                            self.url_scheme,
+                            subdomain and subdomain + '.' or '',
+                            self.server_name,
+                            self.script_name[:-1],
+                            url_quote(path.lstrip('/'), self.map.charset)
+                        )))
+            if rule.redirect_to is not None:
+                if isinstance(rule.redirect_to, basestring):
+                    def _handle_match(match):
+                        value = rv[match.group(1)]
+                        return rule._converters[match.group(1)].to_url(value)
+                    redirect_url = _simple_rule_re.sub(_handle_match,
+                                                       rule.redirect_to)
+                else:
+                    redirect_url = rule.redirect_to(self, **rv)
+                raise RequestRedirect(str(urljoin('%s://%s%s%s' % (
+                    self.url_scheme,
+                    self.subdomain and self.subdomain + '.' or '',
+                    self.server_name,
+                    self.script_name
+                ), redirect_url)))
+            if return_rule:
+                return rule, rv
+            else:
+                return rule.endpoint, rv
+        if have_match_for:
+            raise MethodNotAllowed(valid_methods=list(have_match_for))
+        raise NotFound()
+
+    def test(self, path_info=None, method=None):
+        """Test if a rule would match.  Works like `match` but returns `True`
+        if the URL matches, or `False` if it does not exist.
+
+        :param path_info: the path info to use for matching.  Overrides the
+                          path info specified on binding.
+        :param method: the HTTP method used for matching.  Overrides the
+                       method specified on binding.
+        """
+        try:
+            self.match(path_info, method)
+        except RequestRedirect:
+            pass
+        except NotFound:
+            return False
+        return True
+
+    def _partial_build(self, endpoint, values, method, append_unknown):
+        """Helper for :meth:`build`.  Returns subdomain and path for the
+        rule that accepts this endpoint, values and method.
+
+        :internal:
+        """
+        # in case the method is none, try with the default method first
+        if method is None:
+            rv = self._partial_build(endpoint, values, self.default_method,
+                                     append_unknown)
+            if rv is not None:
+                return rv
+
+        # default method did not match or a specific method is passed,
+        # check all and go with first result.
+        for rule in self.map._rules_by_endpoint.get(endpoint, ()):
+            if rule.suitable_for(values, method):
+                rv = rule.build(values, append_unknown)
+                if rv is not None:
+                    return rv
+
+    def build(self, endpoint, values=None, method=None, force_external=False,
+              append_unknown=True):
+        """Building URLs works pretty much the other way round.  Instead of
+        `match` you call `build` and pass it the endpoint and a dict of
+        arguments for the placeholders.
+
+        The `build` function also accepts an argument called `force_external`
+        which, if you set it to `True` will force external URLs. Per default
+        external URLs (include the server name) will only be used if the
+        target URL is on a different subdomain.
+
+        >>> m = Map([
+        ...     Rule('/', endpoint='index'),
+        ...     Rule('/downloads/', endpoint='downloads/index'),
+        ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
+        ... ])
+        >>> urls = m.bind("example.com", "/")
+        >>> urls.build("index", {})
+        '/'
+        >>> urls.build("downloads/show", {'id': 42})
+        '/downloads/42'
+        >>> urls.build("downloads/show", {'id': 42}, force_external=True)
+        'http://example.com/downloads/42'
+
+        Because URLs cannot contain non ASCII data you will always get
+        bytestrings back.  Non ASCII characters are urlencoded with the
+        charset defined on the map instance.
+
+        Additional values are converted to unicode and appended to the URL as
+        URL querystring parameters:
+
+        >>> urls.build("index", {'q': 'My Searchstring'})
+        '/?q=My+Searchstring'
+
+        If a rule does not exist when building a `BuildError` exception is
+        raised.
+
+        The build method accepts an argument called `method` which allows you
+        to specify the method you want to have an URL built for if you have
+        different methods for the same endpoint specified.
+
+        .. versionadded:: 0.6
+           the `append_unknown` parameter was added.
+
+        :param endpoint: the endpoint of the URL to build.
+        :param values: the values for the URL to build.  Unhandled values are
+                       appended to the URL as query parameters.
+        :param method: the HTTP method for the rule if there are different
+                       URLs for different methods on the same endpoint.
+        :param force_external: enforce full canonical external URLs.
+        :param append_unknown: unknown parameters are appended to the generated
+                               URL as query string argument.  Disable this
+                               if you want the builder to ignore those.
+        """
+        self.map.update()
+        if values:
+            if isinstance(values, MultiDict):
+                values = dict((k, v) for k, v in values.iteritems(multi=True)
+                              if v is not None)
+            else:
+                values = dict((k, v) for k, v in values.iteritems()
+                              if v is not None)
+        else:
+            values = {}
+
+        rv = self._partial_build(endpoint, values, method, append_unknown)
+        if rv is None:
+            raise BuildError(endpoint, values, method)
+        subdomain, path = rv
+
+        if not force_external and subdomain == self.subdomain:
+            return str(urljoin(self.script_name, './' + path.lstrip('/')))
+        return str('%s://%s%s%s/%s' % (
+            self.url_scheme,
+            subdomain and subdomain + '.' or '',
+            self.server_name,
+            self.script_name[:-1],
+            path.lstrip('/')
+        ))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/script.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,303 @@
+# -*- coding: utf-8 -*-
+r'''
+    werkzeug.script
+    ~~~~~~~~~~~~~~~
+
+    Most of the time you have recurring tasks while writing an application
+    such as starting up an interactive python interpreter with some prefilled
+    imports, starting the development server, initializing the database or
+    something similar.
+
+    For that purpose werkzeug provides the `werkzeug.script` module which
+    helps you writing such scripts.
+
+
+    Basic Usage
+    -----------
+
+    The following snippet is roughly the same in every werkzeug script::
+
+        #!/usr/bin/env python
+        # -*- coding: utf-8 -*-
+        from werkzeug import script
+
+        # actions go here
+
+        if __name__ == '__main__':
+            script.run()
+
+    Starting this script now does nothing because no actions are defined.
+    An action is a function in the same module starting with ``"action_"``
+    which takes a number of arguments where every argument has a default.  The
+    type of the default value specifies the type of the argument.
+
+    Arguments can then be passed by position or using ``--name=value`` from
+    the shell.
+
+    Because a runserver and shell command is pretty common there are two
+    factory functions that create such commands::
+
+        def make_app():
+            from yourapplication import YourApplication
+            return YourApplication(...)
+
+        action_runserver = script.make_runserver(make_app, use_reloader=True)
+        action_shell = script.make_shell(lambda: {'app': make_app()})
+
+
+    Using The Scripts
+    -----------------
+
+    The script from above can be used like this from the shell now:
+
+    .. sourcecode:: text
+
+        $ ./manage.py --help
+        $ ./manage.py runserver localhost 8080 --debugger --no-reloader
+        $ ./manage.py runserver -p 4000
+        $ ./manage.py shell
+
+    As you can see it's possible to pass parameters as positional arguments
+    or as named parameters, pretty much like Python function calls.
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+'''
+import sys
+import inspect
+import getopt
+from os.path import basename
+
+
+argument_types = {
+    bool:       'boolean',
+    str:        'string',
+    int:        'integer',
+    float:      'float'
+}
+
+
+converters = {
+    'boolean':  lambda x: x.lower() in ('1', 'true', 'yes', 'on'),
+    'string':   str,
+    'integer':  int,
+    'float':    float
+}
+
+
+def run(namespace=None, action_prefix='action_', args=None):
+    """Run the script.  Participating actions are looked up in the caller's
+    namespace if no namespace is given, otherwise in the dict provided.
+    Only items that start with action_prefix are processed as actions.  If
+    you want to use all items in the namespace provided as actions set
+    action_prefix to an empty string.
+
+    :param namespace: An optional dict where the functions are looked up in.
+                      By default the local namespace of the caller is used.
+    :param action_prefix: The prefix for the functions.  Everything else
+                          is ignored.
+    :param args: the arguments for the function.  If not specified
+                 :data:`sys.argv` without the first argument is used.
+    """
+    if namespace is None:
+        namespace = sys._getframe(1).f_locals
+    actions = find_actions(namespace, action_prefix)
+
+    if args is None:
+        args = sys.argv[1:]
+    if not args or args[0] in ('-h', '--help'):
+        return print_usage(actions)
+    elif args[0] not in actions:
+        fail('Unknown action \'%s\'' % args[0])
+
+    arguments = {}
+    types = {}
+    key_to_arg = {}
+    long_options = []
+    formatstring = ''
+    func, doc, arg_def = actions[args.pop(0)]
+    for idx, (arg, shortcut, default, option_type) in enumerate(arg_def):
+        real_arg = arg.replace('-', '_')
+        if shortcut:
+            formatstring += shortcut
+            if not isinstance(default, bool):
+                formatstring += ':'
+            key_to_arg['-' + shortcut] = real_arg
+        long_options.append(isinstance(default, bool) and arg or arg + '=')
+        key_to_arg['--' + arg] = real_arg
+        key_to_arg[idx] = real_arg
+        types[real_arg] = option_type
+        arguments[real_arg] = default
+
+    try:
+        optlist, posargs = getopt.gnu_getopt(args, formatstring, long_options)
+    except getopt.GetoptError, e:
+        fail(str(e))
+
+    specified_arguments = set()
+    for key, value in enumerate(posargs):
+        try:
+            arg = key_to_arg[key]
+        except IndexError:
+            fail('Too many parameters')
+        specified_arguments.add(arg)
+        try:
+            arguments[arg] = converters[types[arg]](value)
+        except ValueError:
+            fail('Invalid value for argument %s (%s): %s' % (key, arg, value))
+
+    for key, value in optlist:
+        arg = key_to_arg[key]
+        if arg in specified_arguments:
+            fail('Argument \'%s\' is specified twice' % arg)
+        if types[arg] == 'boolean':
+            if arg.startswith('no_'):
+                value = 'no'
+            else:
+                value = 'yes'
+        try:
+            arguments[arg] = converters[types[arg]](value)
+        except ValueError:
+            fail('Invalid value for \'%s\': %s' % (key, value))
+
+    newargs = {}
+    for k, v in arguments.iteritems():
+        newargs[k.startswith('no_') and k[3:] or k] = v
+    arguments = newargs
+    return func(**arguments)
+
+
+def fail(message, code=-1):
+    """Fail with an error."""
+    print >> sys.stderr, 'Error:', message
+    sys.exit(code)
+
+
+def find_actions(namespace, action_prefix):
+    """Find all the actions in the namespace."""
+    actions = {}
+    for key, value in namespace.iteritems():
+        if key.startswith(action_prefix):
+            actions[key[len(action_prefix):]] = analyse_action(value)
+    return actions
+
+
+def print_usage(actions):
+    """Print the usage information.  (Help screen)"""
+    actions = actions.items()
+    actions.sort()
+    print 'usage: %s <action> [<options>]' % basename(sys.argv[0])
+    print '       %s --help' % basename(sys.argv[0])
+    print
+    print 'actions:'
+    for name, (func, doc, arguments) in actions:
+        print '  %s:' % name
+        for line in doc.splitlines():
+            print '    %s' % line
+        if arguments:
+            print
+        for arg, shortcut, default, argtype in arguments:
+            if isinstance(default, bool):
+                print '    %s' % (
+                    (shortcut and '-%s, ' % shortcut or '') + '--' + arg
+                )
+            else:
+                print '    %-30s%-10s%s' % (
+                    (shortcut and '-%s, ' % shortcut or '') + '--' + arg,
+                    argtype, default
+                )
+        print
+
+
+def analyse_action(func):
+    """Analyse a function."""
+    description = inspect.getdoc(func) or 'undocumented action'
+    arguments = []
+    args, varargs, kwargs, defaults = inspect.getargspec(func)
+    if varargs or kwargs:
+        raise TypeError('variable length arguments for action not allowed.')
+    if len(args) != len(defaults or ()):
+        raise TypeError('not all arguments have proper definitions')
+
+    for idx, (arg, definition) in enumerate(zip(args, defaults or ())):
+        if arg.startswith('_'):
+            raise TypeError('arguments may not start with an underscore')
+        if not isinstance(definition, tuple):
+            shortcut = None
+            default = definition
+        else:
+            shortcut, default = definition
+        argument_type = argument_types[type(default)]
+        if isinstance(default, bool) and default is True:
+            arg = 'no-' + arg
+        arguments.append((arg.replace('_', '-'), shortcut,
+                          default, argument_type))
+    return func, description, arguments
+
+
+def make_shell(init_func=None, banner=None, use_ipython=True):
+    """Returns an action callback that spawns a new interactive
+    python shell.
+
+    :param init_func: an optional initialization function that is
+                      called before the shell is started.  The return
+                      value of this function is the initial namespace.
+    :param banner: the banner that is displayed before the shell.  If
+                   not specified a generic banner is used instead.
+    :param use_ipython: if set to `True` ipython is used if available.
+    """
+    if banner is None:
+        banner = 'Interactive Werkzeug Shell'
+    if init_func is None:
+        init_func = dict
+    def action(ipython=use_ipython):
+        """Start a new interactive python session."""
+        namespace = init_func()
+        if ipython:
+            try:
+                import IPython
+            except ImportError:
+                pass
+            else:
+                sh = IPython.Shell.IPShellEmbed(banner=banner)
+                sh(global_ns={}, local_ns=namespace)
+                return
+        from code import interact
+        interact(banner, local=namespace)
+    return action
+
+
+def make_runserver(app_factory, hostname='localhost', port=5000,
+                   use_reloader=False, use_debugger=False, use_evalex=True,
+                   threaded=False, processes=1, static_files=None,
+                   extra_files=None, ssl_context=None):
+    """Returns an action callback that spawns a new development server.
+
+    .. versionadded:: 0.5
+       `static_files` and `extra_files` was added.
+
+    ..versionadded:: 0.6.1
+       `ssl_context` was added.
+
+    :param app_factory: a function that returns a new WSGI application.
+    :param hostname: the default hostname the server should listen on.
+    :param port: the default port of the server.
+    :param use_reloader: the default setting for the reloader.
+    :param use_evalex: the default setting for the evalex flag of the debugger.
+    :param threaded: the default threading setting.
+    :param processes: the default number of processes to start.
+    :param static_files: optional dict of static files.
+    :param extra_files: optional list of extra files to track for reloading.
+    :param ssl_context: optional SSL context for running server in HTTPS mode.
+    """
+    def action(hostname=('h', hostname), port=('p', port),
+               reloader=use_reloader, debugger=use_debugger,
+               evalex=use_evalex, threaded=threaded, processes=processes):
+        """Start a new development server."""
+        from werkzeug.serving import run_simple
+        app = app_factory()
+        run_simple(hostname, port, app, reloader, debugger, evalex,
+                   extra_files, 1, threaded, processes,
+                   static_files=static_files, ssl_context=ssl_context)
+    return action
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/security.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.security
+    ~~~~~~~~~~~~~~~~~
+
+    Security related helpers such as secure password hashing tools.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import hmac
+import string
+from random import SystemRandom
+
+# because the API of hmac changed with the introduction of the
+# new hashlib module, we have to support both.  This sets up a
+# mapping to the digest factory functions and the digest modules
+# (or factory functions with changed API)
+try:
+    from hashlib import sha1, md5
+    _hash_funcs = _hash_mods = {'sha1': sha1, 'md5': md5}
+    _sha1_mod = sha1
+    _md5_mod = md5
+except ImportError:
+    import sha as _sha1_mod, md5 as _md5_mod
+    _hash_mods = {'sha1': _sha1_mod, 'md5': _md5_mod}
+    _hash_funcs = {'sha1': _sha1_mod.new, 'md5': _md5_mod.new}
+
+
+SALT_CHARS = string.letters + string.digits
+
+
+_sys_rng = SystemRandom()
+
+
+def gen_salt(length):
+    """Generate a random string of SALT_CHARS with specified ``length``."""
+    if length <= 0:
+        raise ValueError('requested salt of length <= 0')
+    return ''.join(_sys_rng.choice(SALT_CHARS) for _ in xrange(length))
+
+
+def _hash_internal(method, salt, password):
+    """Internal password hash helper.  Supports plaintext without salt,
+    unsalted and salted passwords.  In case salted passwords are used
+    hmac is used.
+    """
+    if method == 'plain':
+        return password
+    if salt:
+        if method not in _hash_mods:
+            return None
+        if isinstance(salt, unicode):
+            salt = salt.encode('utf-8')
+        h = hmac.new(salt, None, _hash_mods[method])
+    else:
+        if method not in _hash_funcs:
+            return None
+        h = _hash_funcs[method]()
+    if isinstance(password, unicode):
+        password = password.encode('utf-8')
+    h.update(password)
+    return h.hexdigest()
+
+
+def generate_password_hash(password, method='sha1', salt_length=8):
+    """Hash a password with the given method and salt with with a string of
+    the given length.  The format of the string returned includes the method
+    that was used so that :func:`check_password_hash` can check the hash.
+
+    The format for the hashed string looks like this::
+
+        method$salt$hash
+
+    This method can **not** generate unsalted passwords but it is possible
+    to set the method to plain to enforce plaintext passwords.  If a salt
+    is used, hmac is used internally to salt the password.
+
+    :param password: the password to hash
+    :param method: the hash method to use (``'md5'`` or ``'sha1'``)
+    :param salt_length: the lengt of the salt in letters
+    """
+    salt = method != 'plain' and gen_salt(salt_length) or ''
+    h = _hash_internal(method, salt, password)
+    if h is None:
+        raise TypeError('invalid method %r' % method)
+    return '%s$%s$%s' % (method, salt, h)
+
+
+def check_password_hash(pwhash, password):
+    """check a password against a given salted and hashed password value.
+    In order to support unsalted legacy passwords this method supports
+    plain text passwords, md5 and sha1 hashes (both salted and unsalted).
+
+    Returns `True` if the password matched, `False` otherwise.
+
+    :param pwhash: a hashed string like returned by
+                   :func:`generate_password_hash`
+    :param password: the plaintext password to compare against the hash
+    """
+    if pwhash.count('$') < 2:
+        return False
+    method, salt, hashval = pwhash.split('$', 2)
+    return _hash_internal(method, salt, password) == hashval
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/serving.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,533 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.serving
+    ~~~~~~~~~~~~~~~~
+
+    There are many ways to serve a WSGI application.  While you're developing
+    it you usually don't want a full blown webserver like Apache but a simple
+    standalone one.  From Python 2.5 onwards there is the `wsgiref`_ server in
+    the standard library.  If you're using older versions of Python you can
+    download the package from the cheeseshop.
+
+    However there are some caveats. Sourcecode won't reload itself when
+    changed and each time you kill the server using ``^C`` you get an
+    `KeyboardInterrupt` error.  While the latter is easy to solve the first
+    one can be a pain in the ass in some situations.
+
+    The easiest way is creating a small ``start-myproject.py`` that runs the
+    application::
+
+        #!/usr/bin/env python
+        # -*- coding: utf-8 -*-
+        from myproject import make_app
+        from werkzeug import run_simple
+
+        app = make_app(...)
+        run_simple('localhost', 8080, app, use_reloader=True)
+
+    You can also pass it a `extra_files` keyword argument with a list of
+    additional files (like configuration files) you want to observe.
+
+    For bigger applications you should consider using `werkzeug.script`
+    instead of a simple start file.
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import socket
+import sys
+import time
+import thread
+import subprocess
+from urllib import unquote
+from itertools import chain
+from SocketServer import ThreadingMixIn, ForkingMixIn
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+
+import werkzeug
+from werkzeug._internal import _log
+from werkzeug.exceptions import InternalServerError
+
+
+class WSGIRequestHandler(BaseHTTPRequestHandler, object):
+    """A request handler that implements WSGI dispatching."""
+
+    @property
+    def server_version(self):
+        return 'Werkzeug/' + werkzeug.__version__
+
+    def make_environ(self):
+        if '?' in self.path:
+            path_info, query = self.path.split('?', 1)
+        else:
+            path_info = self.path
+            query = ''
+        url_scheme = self.server.ssl_context is None and 'http' or 'https'
+        environ = {
+            'wsgi.version':         (1, 0),
+            'wsgi.url_scheme':      url_scheme,
+            'wsgi.input':           self.rfile,
+            'wsgi.errors':          sys.stderr,
+            'wsgi.multithread':     self.server.multithread,
+            'wsgi.multiprocess':    self.server.multiprocess,
+            'wsgi.run_once':        False,
+            'SERVER_SOFTWARE':      self.server_version,
+            'REQUEST_METHOD':       self.command,
+            'SCRIPT_NAME':          '',
+            'PATH_INFO':            unquote(path_info),
+            'QUERY_STRING':         query,
+            'CONTENT_TYPE':         self.headers.get('Content-Type', ''),
+            'CONTENT_LENGTH':       self.headers.get('Content-Length', ''),
+            'REMOTE_ADDR':          self.client_address[0],
+            'REMOTE_PORT':          self.client_address[1],
+            'SERVER_NAME':          self.server.server_address[0],
+            'SERVER_PORT':          str(self.server.server_address[1]),
+            'SERVER_PROTOCOL':      self.request_version
+        }
+
+        for key, value in self.headers.items():
+            key = 'HTTP_' + key.upper().replace('-', '_')
+            if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
+                environ[key] = value
+
+        return environ
+
+    def run_wsgi(self):
+        app = self.server.app
+        environ = self.make_environ()
+        headers_set = []
+        headers_sent = []
+
+        def write(data):
+            assert headers_set, 'write() before start_response'
+            if not headers_sent:
+                status, response_headers = headers_sent[:] = headers_set
+                code, msg = status.split(None, 1)
+                self.send_response(int(code), msg)
+                header_keys = set()
+                for key, value in response_headers:
+                    self.send_header(key, value)
+                    key = key.lower()
+                    header_keys.add(key)
+                if 'content-length' not in header_keys:
+                    self.close_connection = True
+                    self.send_header('Connection', 'close')
+                if 'server' not in header_keys:
+                    self.send_header('Server', self.version_string())
+                if 'date' not in header_keys:
+                    self.send_header('Date', self.date_time_string())
+                self.end_headers()
+
+            assert type(data) is str, 'applications must write bytes'
+            self.wfile.write(data)
+            self.wfile.flush()
+
+        def start_response(status, response_headers, exc_info=None):
+            if exc_info:
+                try:
+                    if headers_sent:
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                finally:
+                    exc_info = None
+            elif headers_set:
+                raise AssertionError('Headers already set')
+            headers_set[:] = [status, response_headers]
+            return write
+
+        def execute(app):
+            application_iter = app(environ, start_response)
+            try:
+                for data in application_iter:
+                    write(data)
+                # make sure the headers are sent
+                if not headers_sent:
+                    write('')
+            finally:
+                if hasattr(application_iter, 'close'):
+                    application_iter.close()
+                application_iter = None
+
+        try:
+            execute(app)
+        except (socket.error, socket.timeout), e:
+            self.connection_dropped(e, environ)
+        except:
+            if self.server.passthrough_errors:
+                raise
+            from werkzeug.debug.tbtools import get_current_traceback
+            traceback = get_current_traceback(ignore_system_exceptions=True)
+            try:
+                # if we haven't yet sent the headers but they are set
+                # we roll back to be able to set them again.
+                if not headers_sent:
+                    del headers_set[:]
+                execute(InternalServerError())
+            except:
+                pass
+            self.server.log('error', 'Error on request:\n%s',
+                            traceback.plaintext)
+
+    def handle(self):
+        """Handles a request ignoring dropped connections."""
+        try:
+            return BaseHTTPRequestHandler.handle(self)
+        except (socket.error, socket.timeout), e:
+            self.connection_dropped(e)
+        except:
+            if self.server.ssl_context is None or not is_ssl_error():
+                raise
+
+    def connection_dropped(self, error, environ=None):
+        """Called if the connection was closed by the client.  By default
+        nothing happens.
+        """
+
+    def handle_one_request(self):
+        """Handle a single HTTP request."""
+        self.raw_requestline = self.rfile.readline()
+        if not self.raw_requestline:
+            self.close_connection = 1
+        elif self.parse_request():
+            return self.run_wsgi()
+
+    def send_response(self, code, message=None):
+        """Send the response header and log the response code."""
+        self.log_request(code)
+        if message is None:
+            message = code in self.responses and self.responses[code][0] or ''
+        if self.request_version != 'HTTP/0.9':
+            self.wfile.write("%s %d %s\r\n" %
+                             (self.protocol_version, code, message))
+
+    def version_string(self):
+        return BaseHTTPRequestHandler.version_string(self).strip()
+
+    def address_string(self):
+        return self.client_address[0]
+
+    def log_request(self, code='-', size='-'):
+        self.log('info', '"%s" %s %s', self.requestline, code, size)
+
+    def log_error(self, *args):
+        self.log('error', *args)
+
+    def log_message(self, format, *args):
+        self.log('info', format, *args)
+
+    def log(self, type, message, *args):
+        _log(type, '%s - - [%s] %s\n' % (self.address_string(),
+                                         self.log_date_time_string(),
+                                         message % args))
+
+
+#: backwards compatible name if someone is subclassing it
+BaseRequestHandler = WSGIRequestHandler
+
+
+def generate_adhoc_ssl_context():
+    """Generates an adhoc SSL context for the development server."""
+    from random import random
+    from OpenSSL import crypto, SSL
+
+    cert = crypto.X509()
+    cert.set_serial_number(int(random() * sys.maxint))
+    cert.gmtime_adj_notBefore(0)
+    cert.gmtime_adj_notAfter(60 * 60 * 24 * 365)
+
+    subject = cert.get_subject()
+    subject.CN = '*'
+    subject.O = 'Dummy Certificate'
+
+    issuer = cert.get_issuer()
+    issuer.CN = 'Untrusted Authority'
+    issuer.O = 'Self-Signed'
+
+    pkey = crypto.PKey()
+    pkey.generate_key(crypto.TYPE_RSA, 768)
+    cert.set_pubkey(pkey)
+    cert.sign(pkey, 'md5')
+
+    ctx = SSL.Context(SSL.SSLv23_METHOD)
+    ctx.use_privatekey(pkey)
+    ctx.use_certificate(cert)
+
+    return ctx
+
+
+def is_ssl_error(error=None):
+    """Checks if the given error (or the current one) is an SSL error."""
+    if error is None:
+        error = sys.exc_info()[1]
+    from OpenSSL import SSL
+    return isinstance(error, SSL.Error)
+
+
+class _SSLConnectionFix(object):
+    """Wrapper around SSL connection to provide a working makefile()."""
+
+    def __init__(self, con):
+        self._con = con
+
+    def makefile(self, mode, bufsize):
+        return socket._fileobject(self._con, mode, bufsize)
+
+    def __getattr__(self, attrib):
+        return getattr(self._con, attrib)
+
+
+def select_ip_version(host, port):
+    """Returns AF_INET4 or AF_INET6 depending on where to connect to."""
+    try:
+        info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
+                                  socket.SOCK_STREAM, 0,
+                                  socket.AI_PASSIVE)
+        if info:
+            return info[0][0]
+    except socket.gaierror:
+        pass
+    if ':' in host and hasattr(socket, 'AF_INET6'):
+        return socket.AF_INET6
+    return socket.AF_INET
+
+
+class BaseWSGIServer(HTTPServer, object):
+    """Simple single-threaded, single-process WSGI server."""
+    multithread = False
+    multiprocess = False
+
+    def __init__(self, host, port, app, handler=None,
+                 passthrough_errors=False, ssl_context=None):
+        if handler is None:
+            handler = WSGIRequestHandler
+        self.address_family = select_ip_version(host, port)
+        HTTPServer.__init__(self, (host, int(port)), handler)
+        self.app = app
+        self.passthrough_errors = passthrough_errors
+
+        if ssl_context is not None:
+            try:
+                from OpenSSL import tsafe
+            except ImportError:
+                raise TypeError('SSL is not available if the OpenSSL '
+                                'library is not installed.')
+            if ssl_context == 'adhoc':
+                ssl_context = generate_adhoc_ssl_context()
+            self.socket = tsafe.Connection(ssl_context, self.socket)
+            self.ssl_context = ssl_context
+        else:
+            self.ssl_context = None
+
+    def log(self, type, message, *args):
+        _log(type, message, *args)
+
+    def serve_forever(self):
+        try:
+            HTTPServer.serve_forever(self)
+        except KeyboardInterrupt:
+            pass
+
+    def handle_error(self, request, client_address):
+        if self.passthrough_errors:
+            raise
+        else:
+            return HTTPServer.handle_error(self, request, client_address)
+
+    def get_request(self):
+        con, info = self.socket.accept()
+        if self.ssl_context is not None:
+            con = _SSLConnectionFix(con)
+        return con, info
+
+
+class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
+    """A WSGI server that does threading."""
+    multithread = True
+
+
+class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
+    """A WSGI server that does forking."""
+    multiprocess = True
+
+    def __init__(self, host, port, app, processes=40, handler=None,
+                 passthrough_errors=False, ssl_context=None):
+        BaseWSGIServer.__init__(self, host, port, app, handler,
+                                passthrough_errors, ssl_context)
+        self.max_children = processes
+
+
+def make_server(host, port, app=None, threaded=False, processes=1,
+                request_handler=None, passthrough_errors=False,
+                ssl_context=None):
+    """Create a new server instance that is either threaded, or forks
+    or just processes one request after another.
+    """
+    if threaded and processes > 1:
+        raise ValueError("cannot have a multithreaded and "
+                         "multi process server.")
+    elif threaded:
+        return ThreadedWSGIServer(host, port, app, request_handler,
+                                  passthrough_errors, ssl_context)
+    elif processes > 1:
+        return ForkingWSGIServer(host, port, app, processes, request_handler,
+                                 passthrough_errors, ssl_context)
+    else:
+        return BaseWSGIServer(host, port, app, request_handler,
+                              passthrough_errors, ssl_context)
+
+
+def reloader_loop(extra_files=None, interval=1):
+    """When this function is run from the main thread, it will force other
+    threads to exit when any modules currently loaded change.
+
+    Copyright notice.  This function is based on the autoreload.py from
+    the CherryPy trac which originated from WSGIKit which is now dead.
+
+    :param extra_files: a list of additional files it should watch.
+    """
+    def iter_module_files():
+        for module in sys.modules.values():
+            filename = getattr(module, '__file__', None)
+            if filename:
+                old = None
+                while not os.path.isfile(filename):
+                    old = filename
+                    filename = os.path.dirname(filename)
+                    if filename == old:
+                        break
+                else:
+                    if filename[-4:] in ('.pyc', '.pyo'):
+                        filename = filename[:-1]
+                    yield filename
+
+    mtimes = {}
+    while 1:
+        for filename in chain(iter_module_files(), extra_files or ()):
+            try:
+                mtime = os.stat(filename).st_mtime
+            except OSError:
+                continue
+
+            old_time = mtimes.get(filename)
+            if old_time is None:
+                mtimes[filename] = mtime
+                continue
+            elif mtime > old_time:
+                _log('info', ' * Detected change in %r, reloading' % filename)
+                sys.exit(3)
+        time.sleep(interval)
+
+
+def restart_with_reloader():
+    """Spawn a new Python interpreter with the same arguments as this one,
+    but running the reloader thread.
+    """
+    while 1:
+        _log('info', ' * Restarting with reloader...')
+        args = [sys.executable] + sys.argv
+        new_environ = os.environ.copy()
+        new_environ['WERKZEUG_RUN_MAIN'] = 'true'
+
+        # a weird bug on windows. sometimes unicode strings end up in the
+        # environment and subprocess.call does not like this, encode them
+        # to latin1 and continue.
+        if os.name == 'nt':
+            for key, value in new_environ.iteritems():
+                if isinstance(value, unicode):
+                    new_environ[key] = value.encode('iso-8859-1')
+
+        exit_code = subprocess.call(args, env=new_environ)
+        if exit_code != 3:
+            return exit_code
+
+
+def run_with_reloader(main_func, extra_files=None, interval=1):
+    """Run the given function in an independent python interpreter."""
+    if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
+        thread.start_new_thread(main_func, ())
+        try:
+            reloader_loop(extra_files, interval)
+        except KeyboardInterrupt:
+            return
+    try:
+        sys.exit(restart_with_reloader())
+    except KeyboardInterrupt:
+        pass
+
+
+def run_simple(hostname, port, application, use_reloader=False,
+               use_debugger=False, use_evalex=True,
+               extra_files=None, reloader_interval=1, threaded=False,
+               processes=1, request_handler=None, static_files=None,
+               passthrough_errors=False, ssl_context=None):
+    """Start an application using wsgiref and with an optional reloader.  This
+    wraps `wsgiref` to fix the wrong default reporting of the multithreaded
+    WSGI variable and adds optional multithreading and fork support.
+
+    .. versionadded:: 0.5
+       `static_files` was added to simplify serving of static files as well
+       as `passthrough_errors`.
+
+    .. versionadded:: 0.6
+       support for SSL was added.
+
+    :param hostname: The host for the application.  eg: ``'localhost'``
+    :param port: The port for the server.  eg: ``8080``
+    :param application: the WSGI application to execute
+    :param use_reloader: should the server automatically restart the python
+                         process if modules were changed?
+    :param use_debugger: should the werkzeug debugging system be used?
+    :param use_evalex: should the exception evaluation feature be enabled?
+    :param extra_files: a list of files the reloader should watch
+                        additionally to the modules.  For example configuration
+                        files.
+    :param reloader_interval: the interval for the reloader in seconds.
+    :param threaded: should the process handle each request in a separate
+                     thread?
+    :param processes: number of processes to spawn.
+    :param request_handler: optional parameter that can be used to replace
+                            the default one.  You can use this to replace it
+                            with a different
+                            :class:`~BaseHTTPServer.BaseHTTPRequestHandler`
+                            subclass.
+    :param static_files: a dict of paths for static files.  This works exactly
+                         like :class:`SharedDataMiddleware`, it's actually
+                         just wrapping the application in that middleware before
+                         serving.
+    :param passthrough_errors: set this to `True` to disable the error catching.
+                               This means that the server will die on errors but
+                               it can be useful to hook debuggers in (pdb etc.)
+    :param ssl_context: an SSL context for the connection. Either an OpenSSL
+                        context, the string ``'adhoc'`` if the server should
+                        automatically create one, or `None` to disable SSL
+                        (which is the default).
+    """
+    if use_debugger:
+        from werkzeug.debug import DebuggedApplication
+        application = DebuggedApplication(application, use_evalex)
+    if static_files:
+        from werkzeug.wsgi import SharedDataMiddleware
+        application = SharedDataMiddleware(application, static_files)
+
+    def inner():
+        make_server(hostname, port, application, threaded,
+                    processes, request_handler,
+                    passthrough_errors, ssl_context).serve_forever()
+
+    if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
+        display_hostname = hostname != '*' and hostname or 'localhost'
+        if ':' in display_hostname:
+            display_hostname = '[%s]' % display_hostname
+        _log('info', ' * Running on %s://%s:%d/', ssl_context is None
+             and 'http' or 'https', display_hostname, port)
+    if use_reloader:
+        # Create and destroy a socket so that any exceptions are raised before
+        # we spawn a separate Python interpreter and lose this ability.
+        test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        test_socket.bind((hostname, port))
+        test_socket.close()
+        run_with_reloader(inner, extra_files, reloader_interval)
+    else:
+        inner()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/templates.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,393 @@
+# -*- coding: utf-8 -*-
+r"""
+    werkzeug.templates
+    ~~~~~~~~~~~~~~~~~~
+
+    A minimal template engine.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD License.
+"""
+import sys
+import re
+import __builtin__ as builtins
+from compiler import ast, parse
+from compiler.pycodegen import ModuleCodeGenerator
+from tokenize import PseudoToken
+from werkzeug import utils, urls
+from werkzeug._internal import _decode_unicode
+from werkzeug.datastructures import MultiDict
+
+
+# Copyright notice: The `parse_data` method uses the string interpolation
+# algorithm by Ka-Ping Yee which originally was part of `Itpl20.py`_.
+#
+# .. _Itpl20.py: http://lfw.org/python/Itpl20.py
+
+
+token_re = re.compile('%s|%s(?s)' % (
+    r'[uU]?[rR]?("""|\'\'\')((?<!\\)\\\1|.)*?\1',
+    PseudoToken
+))
+directive_re = re.compile(r'(?<!\\)<%(?:(#)|(py(?:thon)?\b)|'
+                          r'(?:\s*(\w+))\s*)(.*?)\s*%>\n?(?s)')
+escape_re = re.compile(r'\\\n|\\(\\|<%)')
+namestart_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
+undefined = type('UndefinedType', (object,), {
+    '__iter__': lambda x: iter(()),
+    '__repr__': lambda x: 'Undefined',
+    '__str__':  lambda x: ''
+})()
+runtime_vars = frozenset(['Undefined', '__to_unicode', '__context',
+                          '__write', '__write_many'])
+
+
+def call_stmt(func, args, lineno):
+    return ast.CallFunc(ast.Name(func, lineno=lineno),
+                        args, lineno=lineno)
+
+
+def tokenize(source, filename):
+    escape = escape_re.sub
+    escape_repl = lambda m: m.group(1) or ''
+    lineno = 1
+    pos = 0
+
+    for match in directive_re.finditer(source):
+        start, end = match.span()
+        if start > pos:
+            data = source[pos:start]
+            yield lineno, 'data', escape(escape_repl, data)
+            lineno += data.count('\n')
+        is_comment, is_code, cmd, args = match.groups()
+        if is_code:
+            yield lineno, 'code', args
+        elif not is_comment:
+            yield lineno, 'cmd', (cmd, args)
+        lineno += source[start:end].count('\n')
+        pos = end
+
+    if pos < len(source):
+        yield lineno, 'data', escape(escape_repl, source[pos:])
+
+
+def transform(node, filename):
+    root = ast.Module(None, node, lineno=1)
+    nodes = [root]
+    while nodes:
+        node = nodes.pop()
+        node.filename = filename
+        if node.__class__ in (ast.Printnl, ast.Print):
+            node.dest = ast.Name('__context')
+        elif node.__class__ is ast.Const and isinstance(node.value, str):
+            try:
+                node.value.decode('ascii')
+            except UnicodeError:
+                node.value = node.value.decode('utf-8')
+        nodes.extend(node.getChildNodes())
+    return root
+
+
+class TemplateSyntaxError(SyntaxError):
+
+    def __init__(self, msg, filename, lineno):
+        from linecache import getline
+        l = getline(filename, lineno)
+        SyntaxError.__init__(self, msg, (filename, lineno, len(l) or 1, l))
+
+
+class Parser(object):
+
+    def __init__(self, gen, filename):
+        self.gen = gen
+        self.filename = filename
+        self.lineno = 1
+
+    def fail(self, msg):
+        raise TemplateSyntaxError(msg, self.filename, self.lineno)
+
+    def parse_python(self, expr, type='exec'):
+        if isinstance(expr, unicode):
+            expr = '\xef\xbb\xbf' + expr.encode('utf-8')
+        try:
+            node = parse(expr, type)
+        except SyntaxError, e:
+            raise TemplateSyntaxError(str(e), self.filename,
+                                      self.lineno + e.lineno - 1)
+        nodes = [node]
+        while nodes:
+            n = nodes.pop()
+            if hasattr(n, 'lineno'):
+                n.lineno = (n.lineno or 1) + self.lineno - 1
+            nodes.extend(n.getChildNodes())
+        return node.node
+
+    def parse(self, needle=()):
+        start_lineno = self.lineno
+        result = []
+        add = result.append
+        for self.lineno, token, value in self.gen:
+            if token == 'data':
+                add(self.parse_data(value))
+            elif token == 'code':
+                add(self.parse_code(value.splitlines()))
+            elif token == 'cmd':
+                name, args = value
+                if name in needle:
+                    return name, args, ast.Stmt(result, lineno=start_lineno)
+                if name in ('for', 'while'):
+                    add(self.parse_loop(args, name))
+                elif name == 'if':
+                    add(self.parse_if(args))
+                else:
+                    self.fail('unknown directive %s' % name)
+        if needle:
+            self.fail('unexpected end of template')
+        return ast.Stmt(result, lineno=start_lineno)
+
+    def parse_loop(self, args, type):
+        rv = self.parse_python('%s %s: pass' % (type, args), 'exec').nodes[0]
+        tag, value, rv.body = self.parse(('end' + type, 'else'))
+        if value:
+            self.fail('unexpected data after ' + tag)
+        if tag == 'else':
+            tag, value, rv.else_ = self.parse(('end' + type,))
+            if value:
+                self.fail('unexpected data after else')
+        return rv
+
+    def parse_if(self, args):
+        cond = self.parse_python('if %s: pass' % args).nodes[0]
+        tag, value, body = self.parse(('else', 'elif', 'endif'))
+        cond.tests[0] = (cond.tests[0][0], body)
+        while 1:
+            if tag == 'else':
+                if value:
+                    self.fail('unexpected data after else')
+                tag, value, cond.else_ = self.parse(('endif',))
+            elif tag == 'elif':
+                expr = self.parse_python(value, 'eval')
+                tag, value, body = self.parse(('else', 'elif', 'endif'))
+                cond.tests.append((expr, body))
+                continue
+            break
+        if value:
+            self.fail('unexpected data after endif')
+        return cond
+
+    def parse_code(self, lines):
+        margin = sys.maxint
+        for line in lines[1:]:
+            content = len(line.lstrip())
+            if content:
+                indent = len(line) - content
+                margin = min(margin, indent)
+        if lines:
+            lines[0] = lines[0].lstrip()
+        if margin < sys.maxint:
+            for i in xrange(1, len(lines)):
+                lines[i] = lines[i][margin:]
+        while lines and not lines[-1]:
+            lines.pop()
+        while lines and not lines[0]:
+            lines.pop(0)
+        return self.parse_python('\n'.join(lines))
+
+    def parse_data(self, text):
+        start_lineno = lineno = self.lineno
+        pos = 0
+        end = len(text)
+        nodes = []
+
+        def match_or_fail(pos):
+            match = token_re.match(text, pos)
+            if match is None:
+                self.fail('invalid syntax')
+            return match.group().strip(), match.end()
+
+        def write_expr(code):
+            node = self.parse_python(code, 'eval')
+            nodes.append(call_stmt('__to_unicode', [node], lineno))
+            return code.count('\n')
+
+        def write_data(value):
+            if value:
+                nodes.append(ast.Const(value, lineno=lineno))
+                return value.count('\n')
+            return 0
+
+        while 1:
+            offset = text.find('$', pos)
+            if offset < 0:
+                break
+            next = text[offset + 1]
+
+            if next == '{':
+                lineno += write_data(text[pos:offset])
+                pos = offset + 2
+                level = 1
+                while level:
+                    token, pos = match_or_fail(pos)
+                    if token in ('{', '}'):
+                        level += token == '{' and 1 or -1
+                lineno += write_expr(text[offset + 2:pos - 1])
+            elif next in namestart_chars:
+                lineno += write_data(text[pos:offset])
+                token, pos = match_or_fail(offset + 1)
+                while pos < end:
+                    if text[pos] == '.' and pos + 1 < end and \
+                       text[pos + 1] in namestart_chars:
+                        token, pos = match_or_fail(pos + 1)
+                    elif text[pos] in '([':
+                        pos += 1
+                        level = 1
+                        while level:
+                            token, pos = match_or_fail(pos)
+                            if token in ('(', ')', '[', ']'):
+                                level += token in '([' and 1 or -1
+                    else:
+                        break
+                lineno += write_expr(text[offset + 1:pos])
+            else:
+                lineno += write_data(text[pos:offset + 1])
+                pos = offset + 1 + (next == '$')
+        write_data(text[pos:])
+
+        return ast.Discard(call_stmt(len(nodes) == 1 and '__write' or
+                           '__write_many', nodes, start_lineno),
+                           lineno=start_lineno)
+
+
+class Context(object):
+
+    def __init__(self, namespace, charset, errors):
+        self.charset = charset
+        self.errors = errors
+        self._namespace = namespace
+        self._buffer = []
+        self._write = self._buffer.append
+        _extend = self._buffer.extend
+        self.runtime = dict(
+            Undefined=undefined,
+            __to_unicode=self.to_unicode,
+            __context=self,
+            __write=self._write,
+            __write_many=lambda *a: _extend(a)
+        )
+
+    def write(self, value):
+        self._write(self.to_unicode(value))
+
+    def to_unicode(self, value):
+        if isinstance(value, str):
+            return _decode_unicode(value, self.charset, self.errors)
+        return unicode(value)
+
+    def get_value(self, as_unicode=True):
+        rv = u''.join(self._buffer)
+        if not as_unicode:
+            return rv.encode(self.charset, self.errors)
+        return rv
+
+    def __getitem__(self, key, default=undefined):
+        try:
+            return self._namespace[key]
+        except KeyError:
+            return getattr(builtins, key, default)
+
+    def get(self, key, default=None):
+        return self.__getitem__(key, default)
+
+    def __setitem__(self, key, value):
+        self._namespace[key] = value
+
+    def __delitem__(self, key):
+        del self._namespace[key]
+
+
+class TemplateCodeGenerator(ModuleCodeGenerator):
+
+    def __init__(self, node, filename):
+        ModuleCodeGenerator.__init__(self, transform(node, filename))
+
+    def _nameOp(self, prefix, name):
+        if name in runtime_vars:
+            return self.emit(prefix + '_GLOBAL', name)
+        return ModuleCodeGenerator._nameOp(self, prefix, name)
+
+
+class Template(object):
+    """Represents a simple text based template.  It's a good idea to load such
+    templates from files on the file system to get better debug output.
+    """
+
+    default_context = {
+        'escape':           utils.escape,
+        'url_quote':        urls.url_quote,
+        'url_quote_plus':   urls.url_quote_plus,
+        'url_encode':       urls.url_encode
+    }
+
+    def __init__(self, source, filename='<template>', charset='utf-8',
+                 errors='strict', unicode_mode=True):
+        if isinstance(source, str):
+            source = _decode_unicode(source, charset, errors)
+        if isinstance(filename, unicode):
+            filename = filename.encode('utf-8')
+        node = Parser(tokenize(u'\n'.join(source.splitlines()),
+                               filename), filename).parse()
+        self.code = TemplateCodeGenerator(node, filename).getCode()
+        self.filename = filename
+        self.charset = charset
+        self.errors = errors
+        self.unicode_mode = unicode_mode
+
+    @classmethod
+    def from_file(cls, file, charset='utf-8', errors='strict',
+                  unicode_mode=True):
+        """Load a template from a file.
+
+        .. versionchanged:: 0.5
+            The encoding parameter was renamed to charset.
+
+        :param file: a filename or file object to load the template from.
+        :param charset: the charset of the template to load.
+        :param errors: the error behavior of the charset decoding.
+        :param unicode_mode: set to `False` to disable unicode mode.
+        :return: a template
+        """
+        close = False
+        if isinstance(file, basestring):
+            f = open(file, 'r')
+            close = True
+        try:
+            data = _decode_unicode(f.read(), charset, errors)
+        finally:
+            if close:
+                f.close()
+        return cls(data, getattr(f, 'name', '<template>'), charset,
+                   errors, unicode_mode)
+
+    def render(self, *args, **kwargs):
+        """This function accepts either a dict or some keyword arguments which
+        will then be the context the template is evaluated in.  The return
+        value will be the rendered template.
+
+        :param context: the function accepts the same arguments as the
+                        :class:`dict` constructor.
+        :return: the rendered template as string
+        """
+        ns = self.default_context.copy()
+        if len(args) == 1 and isinstance(args[0], MultiDict):
+            ns.update(args[0].to_dict(flat=True))
+        else:
+            ns.update(dict(*args))
+        if kwargs:
+            ns.update(kwargs)
+        context = Context(ns, self.charset, self.errors)
+        exec self.code in context.runtime, context
+        return context.get_value(self.unicode_mode)
+
+    def substitute(self, *args, **kwargs):
+        """For API compatibility with `string.Template`."""
+        return self.render(*args, **kwargs)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/test.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,802 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.test
+    ~~~~~~~~~~~~~
+
+    This module implements a client to WSGI applications for testing.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import sys
+import urlparse
+import mimetypes
+from time import time
+from random import random
+from itertools import chain
+from tempfile import TemporaryFile
+from cStringIO import StringIO
+from cookielib import CookieJar
+from urllib2 import Request as U2Request
+
+from werkzeug._internal import _empty_stream, _get_environ
+from werkzeug.wrappers import BaseRequest
+from werkzeug.urls import url_encode, url_fix, iri_to_uri, _unquote
+from werkzeug.wsgi import get_host, get_current_url, ClosingIterator
+from werkzeug.datastructures import FileMultiDict, MultiDict, \
+     CombinedMultiDict, Headers, FileStorage
+
+
+def stream_encode_multipart(values, use_tempfile=True, threshold=1024 * 500,
+                            boundary=None, charset='utf-8'):
+    """Encode a dict of values (either strings or file descriptors or
+    :class:`FileStorage` objects.) into a multipart encoded string stored
+    in a file descriptor.
+    """
+    if boundary is None:
+        boundary = '---------------WerkzeugFormPart_%s%s' % (time(), random())
+    _closure = [StringIO(), 0, False]
+
+    if use_tempfile:
+        def write(string):
+            stream, total_length, on_disk = _closure
+            if on_disk:
+                stream.write(string)
+            else:
+                length = len(string)
+                if length + _closure[1] <= threshold:
+                    stream.write(string)
+                else:
+                    new_stream = TemporaryFile('wb+')
+                    new_stream.write(stream.getvalue())
+                    new_stream.write(string)
+                    _closure[0] = new_stream
+                    _closure[2] = True
+                _closure[1] = total_length + length
+    else:
+        write = _closure[0].write
+
+    if not isinstance(values, MultiDict):
+        values = MultiDict(values)
+
+    for key, values in values.iterlists():
+        for value in values:
+            write('--%s\r\nContent-Disposition: form-data; name="%s"' %
+                  (boundary, key))
+            reader = getattr(value, 'read', None)
+            if reader is not None:
+                filename = getattr(value, 'filename',
+                                   getattr(value, 'name', None))
+                content_type = getattr(value, 'content_type', None)
+                if content_type is None:
+                    content_type = filename and \
+                        mimetypes.guess_type(filename)[0] or \
+                        'application/octet-stream'
+                if filename is not None:
+                    write('; filename="%s"\r\n' % filename)
+                else:
+                    write('\r\n')
+                write('Content-Type: %s\r\n\r\n' % content_type)
+                while 1:
+                    chunk = reader(16384)
+                    if not chunk:
+                        break
+                    write(chunk)
+            else:
+                if isinstance(value, unicode):
+                    value = value.encode(charset)
+                write('\r\n\r\n' + value)
+            write('\r\n')
+    write('--%s--\r\n' % boundary)
+
+    length = int(_closure[0].tell())
+    _closure[0].seek(0)
+    return _closure[0], length, boundary
+
+
+def encode_multipart(values, boundary=None, charset='utf-8'):
+    """Like `stream_encode_multipart` but returns a tuple in the form
+    (``boundary``, ``data``) where data is a bytestring.
+    """
+    stream, length, boundary = stream_encode_multipart(
+        values, use_tempfile=False, boundary=boundary, charset=charset)
+    return boundary, stream.read()
+
+
+def File(fd, filename=None, mimetype=None):
+    """Backwards compat."""
+    from warnings import warn
+    warn(DeprecationWarning('werkzeug.test.File is deprecated, use the '
+                            'EnvironBuilder or FileStorage instead'))
+    return FileStorage(fd, filename=filename, content_type=mimetype)
+
+
+class _TestCookieHeaders(object):
+    """A headers adapter for cookielib
+    """
+
+    def __init__(self, headers):
+        self.headers = headers
+
+    def getheaders(self, name):
+        headers = []
+        name = name.lower()
+        for k, v in self.headers:
+            if k.lower() == name:
+                headers.append(v)
+        return headers
+
+
+class _TestCookieResponse(object):
+    """Something that looks like a httplib.HTTPResponse, but is actually just an
+    adapter for our test responses to make them available for cookielib.
+    """
+
+    def __init__(self, headers):
+        self.headers = _TestCookieHeaders(headers)
+
+    def info(self):
+        return self.headers
+
+
+class _TestCookieJar(CookieJar):
+    """A cookielib.CookieJar modified to inject and read cookie headers from
+    and to wsgi environments, and wsgi application responses.
+    """
+
+    def inject_wsgi(self, environ):
+        """Inject the cookies as client headers into the server's wsgi
+        environment.
+        """
+        cvals = []
+        for cookie in self:
+            cvals.append('%s=%s' % (cookie.name, cookie.value))
+        if cvals:
+            environ['HTTP_COOKIE'] = ', '.join(cvals)
+
+    def extract_wsgi(self, environ, headers):
+        """Extract the server's set-cookie headers as cookies into the
+        cookie jar.
+        """
+        self.extract_cookies(
+            _TestCookieResponse(headers),
+            U2Request(get_current_url(environ)),
+        )
+
+
+def _iter_data(data):
+    """Iterates over a dict or multidict yielding all keys and values.
+    This is used to iterate over the data passed to the
+    :class:`EnvironBuilder`.
+    """
+    if isinstance(data, MultiDict):
+        for key, values in data.iterlists():
+            for value in values:
+                yield key, value
+    else:
+        for key, values in data.iteritems():
+            if isinstance(values, list):
+                for value in values:
+                    yield key, value
+            else:
+                yield key, values
+
+
+class EnvironBuilder(object):
+    """This class can be used to conveniently create a WSGI environment
+    for testing purposes.  It can be used to quickly create WSGI environments
+    or request objects from arbitrary data.
+
+    The signature of this class is also used in some other places as of
+    Werkzeug 0.5 (:func:`create_environ`, :meth:`BaseResponse.from_values`,
+    :meth:`Client.open`).  Because of this most of the functionality is
+    available through the constructor alone.
+
+    Files and regular form data can be manipulated independently of each
+    other with the :attr:`form` and :attr:`files` attributes, but are
+    passed with the same argument to the constructor: `data`.
+
+    `data` can be any of these values:
+
+    -   a `str`: If it's a string it is converted into a :attr:`input_stream`,
+        the :attr:`content_length` is set and you have to provide a
+        :attr:`content_type`.
+    -   a `dict`: If it's a dict the keys have to be strings and the values
+        any of the following objects:
+
+        -   a :class:`file`-like object.  These are converted into
+            :class:`FileStorage` objects automatically.
+        -   a tuple.  The :meth:`~FileMultiDict.add_file` method is called
+            with the tuple items as positional arguments.
+
+    .. versionadded:: 0.6
+       `path` and `base_url` can now be unicode strings that are encoded using
+       the :func:`iri_to_uri` function.
+
+    :param path: the path of the request.  In the WSGI environment this will
+                 end up as `PATH_INFO`.  If the `query_string` is not defined
+                 and there is a question mark in the `path` everything after
+                 it is used as query string.
+    :param base_url: the base URL is a URL that is used to extract the WSGI
+                     URL scheme, host (server name + server port) and the
+                     script root (`SCRIPT_NAME`).
+    :param query_string: an optional string or dict with URL parameters.
+    :param method: the HTTP method to use, defaults to `GET`.
+    :param input_stream: an optional input stream.  Do not specify this and
+                         `data`.  As soon as an input stream is set you can't
+                         modify :attr:`args` and :attr:`files` unless you
+                         set the :attr:`input_stream` to `None` again.
+    :param content_type: The content type for the request.  As of 0.5 you
+                         don't have to provide this when specifying files
+                         and form data via `data`.
+    :param content_length: The content length for the request.  You don't
+                           have to specify this when providing data via
+                           `data`.
+    :param errors_stream: an optional error stream that is used for
+                          `wsgi.errors`.  Defaults to :data:`stderr`.
+    :param multithread: controls `wsgi.multithread`.  Defaults to `False`.
+    :param multiprocess: controls `wsgi.multiprocess`.  Defaults to `False`.
+    :param run_once: controls `wsgi.run_once`.  Defaults to `False`.
+    :param headers: an optional list or :class:`Headers` object of headers.
+    :param data: a string or dict of form data.  See explanation above.
+    :param environ_base: an optional dict of environment defaults.
+    :param environ_overrides: an optional dict of environment overrides.
+    :param charset: the charset used to encode unicode data.
+    """
+
+    #: the server protocol to use.  defaults to HTTP/1.1
+    server_protocol = 'HTTP/1.1'
+
+    #: the wsgi version to use.  defaults to (1, 0)
+    wsgi_version = (1, 0)
+
+    #: the default request class for :meth:`get_request`
+    request_class = BaseRequest
+
+    def __init__(self, path='/', base_url=None, query_string=None,
+                 method='GET', input_stream=None, content_type=None,
+                 content_length=None, errors_stream=None, multithread=False,
+                 multiprocess=False, run_once=False, headers=None, data=None,
+                 environ_base=None, environ_overrides=None, charset='utf-8'):
+        if query_string is None and '?' in path:
+            path, query_string = path.split('?', 1)
+        self.charset = charset
+        if isinstance(path, unicode):
+            path = iri_to_uri(path, charset)
+        self.path = path
+        if base_url is not None:
+            if isinstance(base_url, unicode):
+                base_url = iri_to_uri(base_url, charset)
+            else:
+                base_url = url_fix(base_url, charset)
+        self.base_url = base_url
+        if isinstance(query_string, basestring):
+            self.query_string = query_string
+        else:
+            if query_string is None:
+                query_string = MultiDict()
+            elif not isinstance(query_string, MultiDict):
+                query_string = MultiDict(query_string)
+            self.args = query_string
+        self.method = method
+        if headers is None:
+            headers = Headers()
+        elif not isinstance(headers, Headers):
+            headers = Headers(headers)
+        self.headers = headers
+        self.content_type = content_type
+        if errors_stream is None:
+            errors_stream = sys.stderr
+        self.errors_stream = errors_stream
+        self.multithread = multithread
+        self.multiprocess = multiprocess
+        self.run_once = run_once
+        self.environ_base = environ_base
+        self.environ_overrides = environ_overrides
+        self.input_stream = input_stream
+        self.content_length = content_length
+        self.closed = False
+
+        if data:
+            if input_stream is not None:
+                raise TypeError('can\'t provide input stream and data')
+            if isinstance(data, basestring):
+                self.input_stream = StringIO(data)
+                if self.content_length is None:
+                    self.content_length = len(data)
+            else:
+                for key, value in _iter_data(data):
+                    if isinstance(value, (tuple, dict)) or \
+                       hasattr(value, 'read'):
+                        self._add_file_from_data(key, value)
+                    else:
+                        self.form.setlistdefault(key).append(value)
+
+    def _add_file_from_data(self, key, value):
+        """Called in the EnvironBuilder to add files from the data dict."""
+        if isinstance(value, tuple):
+            self.files.add_file(key, *value)
+        elif isinstance(value, dict):
+            from warnings import warn
+            warn(DeprecationWarning('it\'s no longer possible to pass dicts '
+                                    'as `data`.  Use tuples or FileStorage '
+                                    'objects instead'), stacklevel=2)
+            value = dict(value)
+            mimetype = value.pop('mimetype', None)
+            if mimetype is not None:
+                value['content_type'] = mimetype
+            self.files.add_file(key, **value)
+        else:
+            self.files.add_file(key, value)
+
+    def _get_base_url(self):
+        return urlparse.urlunsplit((self.url_scheme, self.host,
+                                    self.script_root, '', '')).rstrip('/') + '/'
+
+    def _set_base_url(self, value):
+        if value is None:
+            scheme = 'http'
+            netloc = 'localhost'
+            scheme = 'http'
+            script_root = ''
+        else:
+            scheme, netloc, script_root, qs, anchor = urlparse.urlsplit(value)
+            if qs or anchor:
+                raise ValueError('base url must not contain a query string '
+                                 'or fragment')
+        self.script_root = script_root.rstrip('/')
+        self.host = netloc
+        self.url_scheme = scheme
+
+    base_url = property(_get_base_url, _set_base_url, doc='''
+        The base URL is a URL that is used to extract the WSGI
+        URL scheme, host (server name + server port) and the
+        script root (`SCRIPT_NAME`).''')
+    del _get_base_url, _set_base_url
+
+    def _get_content_type(self):
+        ct = self.headers.get('Content-Type')
+        if ct is None and not self._input_stream:
+            if self.method in ('POST', 'PUT'):
+                if self._files:
+                    return 'multipart/form-data'
+                return 'application/x-www-form-urlencoded'
+            return None
+        return ct
+
+    def _set_content_type(self, value):
+        if value is None:
+            self.headers.pop('Content-Type', None)
+        else:
+            self.headers['Content-Type'] = value
+
+    content_type = property(_get_content_type, _set_content_type, doc='''
+        The content type for the request.  Reflected from and to the
+        :attr:`headers`.  Do not set if you set :attr:`files` or
+        :attr:`form` for auto detection.''')
+    del _get_content_type, _set_content_type
+
+    def _get_content_length(self):
+        return self.headers.get('Content-Length', type=int)
+
+    def _set_content_length(self, value):
+        if value is None:
+            self.headers.pop('Content-Length', None)
+        else:
+            self.headers['Content-Length'] = str(value)
+
+    content_length = property(_get_content_length, _set_content_length, doc='''
+        The content length as integer.  Reflected from and to the
+        :attr:`headers`.  Do not set if you set :attr:`files` or
+        :attr:`form` for auto detection.''')
+    del _get_content_length, _set_content_length
+
+    def form_property(name, storage, doc):
+        key = '_' + name
+        def getter(self):
+            if self._input_stream is not None:
+                raise AttributeError('an input stream is defined')
+            rv = getattr(self, key)
+            if rv is None:
+                rv = storage()
+                setattr(self, key, rv)
+            return rv
+        def setter(self, value):
+            self._input_stream = None
+            setattr(self, key, value)
+        return property(getter, setter, doc)
+
+    form = form_property('form', MultiDict, doc='''
+        A :class:`MultiDict` of form values.''')
+    files = form_property('files', FileMultiDict, doc='''
+        A :class:`FileMultiDict` of uploaded files.  You can use the
+        :meth:`~FileMultiDict.add_file` method to add new files to the
+        dict.''')
+    del form_property
+
+    def _get_input_stream(self):
+        return self._input_stream
+
+    def _set_input_stream(self, value):
+        self._input_stream = value
+        self._form = self._files = None
+
+    input_stream = property(_get_input_stream, _set_input_stream, doc='''
+        An optional input stream.  If you set this it will clear
+        :attr:`form` and :attr:`files`.''')
+    del _get_input_stream, _set_input_stream
+
+    def _get_query_string(self):
+        if self._query_string is None:
+            if self._args is not None:
+                return url_encode(self._args, charset=self.charset)
+            return ''
+        return self._query_string
+
+    def _set_query_string(self, value):
+        self._query_string = value
+        self._args = None
+
+    query_string = property(_get_query_string, _set_query_string, doc='''
+        The query string.  If you set this to a string :attr:`args` will
+        no longer be available.''')
+    del _get_query_string, _set_query_string
+
+    def _get_args(self):
+        if self._query_string is not None:
+            raise AttributeError('a query string is defined')
+        if self._args is None:
+            self._args = MultiDict()
+        return self._args
+
+    def _set_args(self, value):
+        self._query_string = None
+        self._args = value
+
+    args = property(_get_args, _set_args, doc='''
+        The URL arguments as :class:`MultiDict`.''')
+    del _get_args, _set_args
+
+    @property
+    def server_name(self):
+        """The server name (read-only, use :attr:`host` to set)"""
+        return self.host.split(':', 1)[0]
+
+    @property
+    def server_port(self):
+        """The server port as integer (read-only, use :attr:`host` to set)"""
+        pieces = self.host.split(':', 1)
+        if len(pieces) == 2 and pieces[1].isdigit():
+            return int(pieces[1])
+        elif self.url_scheme == 'https':
+            return 443
+        return 80
+
+    def __del__(self):
+        self.close()
+
+    def close(self):
+        """Closes all files.  If you put real :class:`file` objects into the
+        :attr:`files` dict you can call this method to automatically close
+        them all in one go.
+        """
+        if self.closed:
+            return
+        try:
+            files = self.files.itervalues()
+        except AttributeError:
+            files = ()
+        for f in files:
+            try:
+                f.close()
+            except Exception, e:
+                pass
+        self.closed = True
+
+    def get_environ(self):
+        """Return the built environ."""
+        input_stream = self.input_stream
+        content_length = self.content_length
+        content_type = self.content_type
+
+        if input_stream is not None:
+            start_pos = input_stream.tell()
+            input_stream.seek(0, 2)
+            end_pos = input_stream.tell()
+            input_stream.seek(start_pos)
+            content_length = end_pos - start_pos
+        elif content_type == 'multipart/form-data':
+            values = CombinedMultiDict([self.form, self.files])
+            input_stream, content_length, boundary = \
+                stream_encode_multipart(values, charset=self.charset)
+            content_type += '; boundary="%s"' % boundary
+        elif content_type == 'application/x-www-form-urlencoded':
+            values = url_encode(self.form, charset=self.charset)
+            content_length = len(values)
+            input_stream = StringIO(values)
+        else:
+            input_stream = _empty_stream
+
+        result = {}
+        if self.environ_base:
+            result.update(self.environ_base)
+
+        def _path_encode(x):
+            if isinstance(x, unicode):
+                x = x.encode(self.charset)
+            return _unquote(x)
+
+        result.update({
+            'REQUEST_METHOD':       self.method,
+            'SCRIPT_NAME':          _path_encode(self.script_root),
+            'PATH_INFO':            _path_encode(self.path),
+            'QUERY_STRING':         self.query_string,
+            'SERVER_NAME':          self.server_name,
+            'SERVER_PORT':          str(self.server_port),
+            'HTTP_HOST':            self.host,
+            'SERVER_PROTOCOL':      self.server_protocol,
+            'CONTENT_TYPE':         content_type or '',
+            'CONTENT_LENGTH':       str(content_length or '0'),
+            'wsgi.version':         self.wsgi_version,
+            'wsgi.url_scheme':      self.url_scheme,
+            'wsgi.input':           input_stream,
+            'wsgi.errors':          self.errors_stream,
+            'wsgi.multithread':     self.multithread,
+            'wsgi.multiprocess':    self.multiprocess,
+            'wsgi.run_once':        self.run_once
+        })
+        for key, value in self.headers.to_list(self.charset):
+            result['HTTP_%s' % key.upper().replace('-', '_')] = value
+        if self.environ_overrides:
+            result.update(self.environ_overrides)
+        return result
+
+    def get_request(self, cls=None):
+        """Returns a request with the data.  If the request class is not
+        specified :attr:`request_class` is used.
+
+        :param cls: The request wrapper to use.
+        """
+        if cls is None:
+            cls = self.request_class
+        return cls(self.get_environ())
+
+
+class ClientRedirectError(Exception):
+    """
+    If a redirect loop is detected when using follow_redirects=True with
+    the :cls:`Client`, then this exception is raised.
+    """
+
+
+class Client(object):
+    """This class allows to send requests to a wrapped application.
+
+    The response wrapper can be a class or factory function that takes
+    three arguments: app_iter, status and headers.  The default response
+    wrapper just returns a tuple.
+
+    Example::
+
+        class ClientResponse(BaseResponse):
+            ...
+
+        client = Client(MyApplication(), response_wrapper=ClientResponse)
+
+    The use_cookies parameter indicates whether cookies should be stored and
+    sent for subsequent requests. This is True by default, but passing False
+    will disable this behaviour.
+
+    .. versionadded:: 0.5
+       `use_cookies` is new in this version.  Older versions did not provide
+       builtin cookie support.
+    """
+
+    def __init__(self, application, response_wrapper=None, use_cookies=True):
+        self.application = application
+        if response_wrapper is None:
+            response_wrapper = lambda a, s, h: (a, s, h)
+        self.response_wrapper = response_wrapper
+        if use_cookies:
+            self.cookie_jar = _TestCookieJar()
+        else:
+            self.cookie_jar = None
+        self.redirect_client = None
+
+    def open(self, *args, **kwargs):
+        """Takes the same arguments as the :class:`EnvironBuilder` class with
+        some additions:  You can provide a :class:`EnvironBuilder` or a WSGI
+        environment as only argument instead of the :class:`EnvironBuilder`
+        arguments and two optional keyword arguments (`as_tuple`, `buffered`)
+        that change the type of the return value or the way the application is
+        executed.
+
+        .. versionchanged:: 0.5
+           If a dict is provided as file in the dict for the `data` parameter
+           the content type has to be called `content_type` now instead of
+           `mimetype`.  This change was made for consistency with
+           :class:`werkzeug.FileWrapper`.
+
+            The `follow_redirects` parameter was added to :func:`open`.
+
+        Additional parameters:
+
+        :param as_tuple: Returns a tuple in the form ``(environ, result)``
+        :param buffered: Set this to True to buffer the application run.
+                         This will automatically close the application for
+                         you as well.
+        :param follow_redirects: Set this to True if the `Client` should
+                                 follow HTTP redirects.
+        """
+        as_tuple = kwargs.pop('as_tuple', False)
+        buffered = kwargs.pop('buffered', False)
+        follow_redirects = kwargs.pop('follow_redirects', False)
+        environ = None
+        if not kwargs and len(args) == 1:
+            if isinstance(args[0], EnvironBuilder):
+                environ = args[0].get_environ()
+            elif isinstance(args[0], dict):
+                environ = args[0]
+        if environ is None:
+            builder = EnvironBuilder(*args, **kwargs)
+            try:
+                environ = builder.get_environ()
+            finally:
+                builder.close()
+
+        if self.cookie_jar is not None:
+            self.cookie_jar.inject_wsgi(environ)
+        rv = run_wsgi_app(self.application, environ, buffered=buffered)
+        if self.cookie_jar is not None:
+            self.cookie_jar.extract_wsgi(environ, rv[2])
+
+        # handle redirects
+        redirect_chain = []
+        status_code = int(rv[1].split(None, 1)[0])
+        while status_code in (301, 302, 303, 305, 307) and follow_redirects:
+            if not self.redirect_client:
+                # assume that we're not using the user defined response wrapper
+                # so that we don't need any ugly hacks to get the status
+                # code from the response.
+                self.redirect_client = Client(self.application)
+                self.redirect_client.cookie_jar = self.cookie_jar
+
+            redirect = dict(rv[2])['Location']
+            scheme, netloc, script_root, qs, anchor = urlparse.urlsplit(redirect)
+            base_url = urlparse.urlunsplit((scheme, netloc, '', '', '')).rstrip('/') + '/'
+            host = get_host(create_environ('/', base_url, query_string=qs)).split(':', 1)[0]
+            if get_host(environ).split(':', 1)[0] != host:
+                raise RuntimeError('%r does not support redirect to '
+                                   'external targets' % self.__class__)
+
+            redirect_chain.append((redirect, status_code))
+
+            # the redirect request should be a new request, and not be based on
+            # the old request
+            redirect_kwargs = {
+                'path':             script_root,
+                'base_url':         base_url,
+                'query_string':     qs,
+                'as_tuple':         True,
+                'buffered':         buffered,
+                'follow_redirects': False,
+            }
+            environ, rv = self.redirect_client.open(**redirect_kwargs)
+            status_code = int(rv[1].split(None, 1)[0])
+
+            # Prevent loops
+            if redirect_chain[-1] in redirect_chain[:-1]:
+                raise ClientRedirectError("loop detected")
+
+        response = self.response_wrapper(*rv)
+        if as_tuple:
+            return environ, response
+        return response
+
+    def get(self, *args, **kw):
+        """Like open but method is enforced to GET."""
+        kw['method'] = 'GET'
+        return self.open(*args, **kw)
+
+    def post(self, *args, **kw):
+        """Like open but method is enforced to POST."""
+        kw['method'] = 'POST'
+        return self.open(*args, **kw)
+
+    def head(self, *args, **kw):
+        """Like open but method is enforced to HEAD."""
+        kw['method'] = 'HEAD'
+        return self.open(*args, **kw)
+
+    def put(self, *args, **kw):
+        """Like open but method is enforced to PUT."""
+        kw['method'] = 'PUT'
+        return self.open(*args, **kw)
+
+    def delete(self, *args, **kw):
+        """Like open but method is enforced to DELETE."""
+        kw['method'] = 'DELETE'
+        return self.open(*args, **kw)
+
+    def __repr__(self):
+        return '<%s %r>' % (
+            self.__class__.__name__,
+            self.application
+        )
+
+
+def create_environ(*args, **kwargs):
+    """Create a new WSGI environ dict based on the values passed.  The first
+    parameter should be the path of the request which defaults to '/'.  The
+    second one can either be an absolute path (in that case the host is
+    localhost:80) or a full path to the request with scheme, netloc port and
+    the path to the script.
+
+    This accepts the same arguments as the :class:`EnvironBuilder`
+    constructor.
+
+    .. versionchanged:: 0.5
+       This function is now a thin wrapper over :class:`EnvironBuilder` which
+       was added in 0.5.  The `headers`, `environ_base`, `environ_overrides`
+       and `charset` parameters were added.
+    """
+    builder = EnvironBuilder(*args, **kwargs)
+    try:
+        return builder.get_environ()
+    finally:
+        builder.close()
+
+
+def run_wsgi_app(app, environ, buffered=False):
+    """Return a tuple in the form (app_iter, status, headers) of the
+    application output.  This works best if you pass it an application that
+    returns an iterator all the time.
+
+    Sometimes applications may use the `write()` callable returned
+    by the `start_response` function.  This tries to resolve such edge
+    cases automatically.  But if you don't get the expected output you
+    should set `buffered` to `True` which enforces buffering.
+
+    If passed an invalid WSGI application the behavior of this function is
+    undefined.  Never pass non-conforming WSGI applications to this function.
+
+    :param app: the application to execute.
+    :param buffered: set to `True` to enforce buffering.
+    :return: tuple in the form ``(app_iter, status, headers)``
+    """
+    environ = _get_environ(environ)
+    response = []
+    buffer = []
+
+    def start_response(status, headers, exc_info=None):
+        if exc_info is not None:
+            raise exc_info[0], exc_info[1], exc_info[2]
+        response[:] = [status, headers]
+        return buffer.append
+
+    app_iter = app(environ, start_response)
+
+    # when buffering we emit the close call early and convert the
+    # application iterator into a regular list
+    if buffered:
+        close_func = getattr(app_iter, 'close', None)
+        try:
+            app_iter = list(app_iter)
+        finally:
+            if close_func is not None:
+                close_func()
+
+    # otherwise we iterate the application iter until we have
+    # a response, chain the already received data with the already
+    # collected data and wrap it in a new `ClosingIterator` if
+    # we have a close callable.
+    else:
+        while not response:
+            buffer.append(app_iter.next())
+        if buffer:
+            close_func = getattr(app_iter, 'close', None)
+            app_iter = chain(buffer, app_iter)
+            if close_func is not None:
+                app_iter = ClosingIterator(app_iter, close_func)
+
+    return app_iter, response[0], response[1]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/testapp.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.testapp
+    ~~~~~~~~~~~~~~~~
+
+    Provide a small test application that can be used to test a WSGI server
+    and check it for WSGI compliance.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+from werkzeug.templates import Template
+from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response
+
+
+logo = Response('''R0lGODlhoACgAOMIAAEDACwpAEpCAGdgAJaKAM28AOnVAP3rAP/////////
+//////////////////////yH5BAEKAAgALAAAAACgAKAAAAT+EMlJq704680R+F0ojmRpnuj0rWnrv
+nB8rbRs33gu0bzu/0AObxgsGn3D5HHJbCUFyqZ0ukkSDlAidctNFg7gbI9LZlrBaHGtzAae0eloe25
+7w9EDOX2fst/xenyCIn5/gFqDiVVDV4aGeYiKkhSFjnCQY5OTlZaXgZp8nJ2ekaB0SQOjqphrpnOiq
+ncEn65UsLGytLVmQ6m4sQazpbtLqL/HwpnER8bHyLrLOc3Oz8PRONPU1crXN9na263dMt/g4SzjMeX
+m5yDpLqgG7OzJ4u8lT/P69ej3JPn69kHzN2OIAHkB9RUYSFCFQYQJFTIkCDBiwoXWGnowaLEjRm7+G
+p9A7Hhx4rUkAUaSLJlxHMqVMD/aSycSZkyTplCqtGnRAM5NQ1Ly5OmzZc6gO4d6DGAUKA+hSocWYAo
+SlM6oUWX2O/o0KdaVU5vuSQLAa0ADwQgMEMB2AIECZhVSnTno6spgbtXmHcBUrQACcc2FrTrWS8wAf
+78cMFBgwIBgbN+qvTt3ayikRBk7BoyGAGABAdYyfdzRQGV3l4coxrqQ84GpUBmrdR3xNIDUPAKDBSA
+ADIGDhhqTZIWaDcrVX8EsbNzbkvCOxG8bN5w8ly9H8jyTJHC6DFndQydbguh2e/ctZJFXRxMAqqPVA
+tQH5E64SPr1f0zz7sQYjAHg0In+JQ11+N2B0XXBeeYZgBZFx4tqBToiTCPv0YBgQv8JqA6BEf6RhXx
+w1ENhRBnWV8ctEX4Ul2zc3aVGcQNC2KElyTDYyYUWvShdjDyMOGMuFjqnII45aogPhz/CodUHFwaDx
+lTgsaOjNyhGWJQd+lFoAGk8ObghI0kawg+EV5blH3dr+digkYuAGSaQZFHFz2P/cTaLmhF52QeSb45
+Jwxd+uSVGHlqOZpOeJpCFZ5J+rkAkFjQ0N1tah7JJSZUFNsrkeJUJMIBi8jyaEKIhKPomnC91Uo+NB
+yyaJ5umnnpInIFh4t6ZSpGaAVmizqjpByDegYl8tPE0phCYrhcMWSv+uAqHfgH88ak5UXZmlKLVJhd
+dj78s1Fxnzo6yUCrV6rrDOkluG+QzCAUTbCwf9SrmMLzK6p+OPHx7DF+bsfMRq7Ec61Av9i6GLw23r
+idnZ+/OO0a99pbIrJkproCQMA17OPG6suq3cca5ruDfXCCDoS7BEdvmJn5otdqscn+uogRHHXs8cbh
+EIfYaDY1AkrC0cqwcZpnM6ludx72x0p7Fo/hZAcpJDjax0UdHavMKAbiKltMWCF3xxh9k25N/Viud8
+ba78iCvUkt+V6BpwMlErmcgc502x+u1nSxJSJP9Mi52awD1V4yB/QHONsnU3L+A/zR4VL/indx/y64
+gqcj+qgTeweM86f0Qy1QVbvmWH1D9h+alqg254QD8HJXHvjQaGOqEqC22M54PcftZVKVSQG9jhkv7C
+JyTyDoAJfPdu8v7DRZAxsP/ky9MJ3OL36DJfCFPASC3/aXlfLOOON9vGZZHydGf8LnxYJuuVIbl83y
+Az5n/RPz07E+9+zw2A2ahz4HxHo9Kt79HTMx1Q7ma7zAzHgHqYH0SoZWyTuOLMiHwSfZDAQTn0ajk9
+YQqodnUYjByQZhZak9Wu4gYQsMyEpIOAOQKze8CmEF45KuAHTvIDOfHJNipwoHMuGHBnJElUoDmAyX
+c2Qm/R8Ah/iILCCJOEokGowdhDYc/yoL+vpRGwyVSCWFYZNljkhEirGXsalWcAgOdeAdoXcktF2udb
+qbUhjWyMQxYO01o6KYKOr6iK3fE4MaS+DsvBsGOBaMb0Y6IxADaJhFICaOLmiWTlDAnY1KzDG4ambL
+cWBA8mUzjJsN2KjSaSXGqMCVXYpYkj33mcIApyhQf6YqgeNAmNvuC0t4CsDbSshZJkCS1eNisKqlyG
+cF8G2JeiDX6tO6Mv0SmjCa3MFb0bJaGPMU0X7c8XcpvMaOQmCajwSeY9G0WqbBmKv34DsMIEztU6Y2
+KiDlFdt6jnCSqx7Dmt6XnqSKaFFHNO5+FmODxMCWBEaco77lNDGXBM0ECYB/+s7nKFdwSF5hgXumQe
+EZ7amRg39RHy3zIjyRCykQh8Zo2iviRKyTDn/zx6EefptJj2Cw+Ep2FSc01U5ry4KLPYsTyWnVGnvb
+UpyGlhjBUljyjHhWpf8OFaXwhp9O4T1gU9UeyPPa8A2l0p1kNqPXEVRm1AOs1oAGZU596t6SOR2mcB
+Oco1srWtkaVrMUzIErrKri85keKqRQYX9VX0/eAUK1hrSu6HMEX3Qh2sCh0q0D2CtnUqS4hj62sE/z
+aDs2Sg7MBS6xnQeooc2R2tC9YrKpEi9pLXfYXp20tDCpSP8rKlrD4axprb9u1Df5hSbz9QU0cRpfgn
+kiIzwKucd0wsEHlLpe5yHXuc6FrNelOl7pY2+11kTWx7VpRu97dXA3DO1vbkhcb4zyvERYajQgAADs
+='''.decode('base64'), mimetype='image/png')
+
+
+TEMPLATE = Template(ur'''\
+<%py
+    import sys, os
+    from textwrap import wrap
+    import werkzeug
+    from werkzeug.testapp import iter_sys_path
+    try:
+        import pkg_resources
+    except ImportError:
+        eggs = None
+    else:
+        eggs = list(pkg_resources.working_set)
+        eggs.sort(lambda a, b: cmp(a.project_name.lower(),
+                                   b.project_name.lower()))
+    sorted_environ = req.environ.items()
+    sorted_environ.sort(lambda a, b: cmp(str(a[0]).lower(), str(b[0]).lower()))
+%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<title>WSGI Information</title>
+<style type="text/css">
+  body       { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+               'Verdana', sans-serif; background-color: #AFC1C4; Color: #000;
+               text-align: center; margin: 1em; padding: 0; }
+  #logo      { float: right; padding: 10px; }
+  div.box    { text-align: left; width: 45em; padding: 1em; margin: auto;
+               border: 1px solid #aaa; background-color: white; }
+  h1         { color: #11557C; font-size: 2em; margin: 0 0 0.8em 0; }
+  h2         { font-size: 1.4em; margin: 1em 0 0.5em 0; }
+  table      { width: 100%; border-collapse: collapse; border: 1px solid #AFC5C9 }
+  Table th   { background-color: #AFC1C4; color: white; font-size: 0.72em;
+               font-weight: normal; width: 18em; vertical-align: top;
+               padding: 0.5em 0 0.1em 0.5em; }
+  table td   { border: 1px solid #AFC5C9; padding: 0.1em 0 0.1em 0.5em; }
+  code       { font-family: 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono',
+               monospace; font-size: 0.7em; }
+  ul li      { line-height: 1.5em; }
+  ul.path    { font-size: 0.7em; margin: 0; padding: 8px; list-style: none;
+               background: #E9F5F7; border: 1px solid #AFC5C9; }
+  ul.path li { line-height: 1.6em; }
+  li.virtual { color: #999; text-decoration: underline; }
+  li.exp     { background: white; }
+</style>
+<div class="box">
+  <img src="?resource=logo" id="logo" alt="[The Werkzeug Logo]" />
+  <h1>WSGI Information</h1>
+  <p>
+    This page displays all available information about the WSGI server and
+    the underlying Python interpreter.
+  <h2 id="python-interpreter">Python Interpreter</h2>
+  <table>
+    <tr>
+      <th>Python Version</th>
+      <td>${'<br>'.join(escape(sys.version).splitlines())}</td>
+    </tr>
+    <tr>
+      <th>Platform</th>
+      <td>$escape(sys.platform) [$escape(os.name)]</td>
+    </tr>
+    <tr>
+      <th>API Version</th>
+      <td>$sys.api_version</td>
+    </tr>
+    <tr>
+      <th>Byteorder</th>
+      <td>$sys.byteorder</td>
+    </tr>
+    <tr>
+      <th>Werkzeug Version</th>
+      <td>$escape(werkzeug.__version__)</td>
+    </tr>
+  </table>
+  <h2 id="wsgi-environment">WSGI Environment</h2>
+  <table>
+  <% for key, value in sorted_environ %>
+    <tr>
+      <th>$escape(str(key))</th>
+      <td><code>${' '.join(wrap(escape(repr(value))))}</code></td>
+    </tr>
+  <% endfor %>
+  </table>
+  <% if eggs %>
+  <h2 id="installed-eggs">Installed Eggs</h2>
+  <p>
+    The following python packages were installed on the system as
+    Python eggs:
+  <ul>
+  <% for egg in eggs %>
+    <li>$escape(egg.project_name) <small>[$escape(egg.version)]</small></li>
+  <% endfor %>
+  </ul>
+  <% endif %>
+  <h2 id="sys-path">Package Load Path</h2>
+  <p>
+    The following paths are the current contents of the load path.  The
+    following entries are looked up for Python packages.  Note that not
+    all items in this path are folders.  Gray and underlined items are
+    entries pointing to invalid resources or used by custom import hooks
+    such as the zip importer.
+  <p>
+    Items with a bright background were expanded for display from a relative
+    path.  If you encounter such paths in the output you might want to check
+    your setup as relative paths are usually problematic in multithreaded
+    environments.
+  <ul class="path">
+  <% for item, virtual, expanded in iter_sys_path() %>
+    <%py
+      class_ = []
+      if virtual:
+          class_.append('virtual')
+      if expanded:
+          class_.append('exp')
+      class_ = ' '.join(class_)
+    %>
+    <li<% if class_ %> class="$class_"<% endif %>>$escape(item)</li>
+  <% endfor %>
+  </ul>
+</div>''')
+
+
+def iter_sys_path():
+    if os.name == 'posix':
+        def strip(x):
+            prefix = os.path.expanduser('~')
+            if x.startswith(prefix):
+                x = '~' + x[len(prefix):]
+            return x
+    else:
+        strip = lambda x: x
+
+    cwd = os.path.abspath(os.getcwd())
+    for item in sys.path:
+        path = os.path.join(cwd, item or os.path.curdir)
+        yield strip(os.path.normpath(path)), \
+              not os.path.isdir(path), path != item
+
+
+def test_app(environ, start_response):
+    """Simple test application that dumps the environment.  You can use
+    it to check if Werkzeug is working properly:
+
+    .. sourcecode:: pycon
+
+        >>> from werkzeug import run_simple, test_app
+        >>> run_simple('localhost', 3000, test_app)
+         * Running on http://localhost:3000/
+
+    The application displays important information from the WSGI environment,
+    the Python interpreter and the installed libraries.
+    """
+    req = Request(environ, populate_request=False)
+    if req.args.get('resource') == 'logo':
+        response = logo
+    else:
+        response = Response(TEMPLATE.render(req=req), mimetype='text/html')
+    return response(environ, start_response)
+
+
+if __name__ == '__main__':
+    from werkzeug.serving import run_simple
+    run_simple('localhost', 5000, test_app, use_reloader=True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/urls.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,463 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.urls
+    ~~~~~~~~~~~~~
+
+    This module implements various URL related functions.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import urlparse
+
+from werkzeug._internal import _decode_unicode
+from werkzeug.datastructures import MultiDict, iter_multi_items
+
+
+#: list of characters that are always safe in URLs.
+_always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+                'abcdefghijklmnopqrstuvwxyz'
+                '0123456789_.-')
+_safe_map = dict((c, c) for c in _always_safe)
+for i in xrange(0x80):
+    c = chr(i)
+    if c not in _safe_map:
+        _safe_map[c] = '%%%02X' % i
+_safe_map.update((chr(i), '%%%02X' % i) for i in xrange(0x80, 0x100))
+_safemaps = {}
+
+#: lookup table for encoded characters.
+_hexdig = '0123456789ABCDEFabcdef'
+_hextochr = dict((a + b, chr(int(a + b, 16)))
+                 for a in _hexdig for b in _hexdig)
+
+
+def _quote(s, safe='/', _join=''.join):
+    assert isinstance(s, str), 'quote only works on bytes'
+    if not s or not s.rstrip(_always_safe + safe):
+        return s
+    try:
+        quoter = _safemaps[safe]
+    except KeyError:
+        safe_map = _safe_map.copy()
+        safe_map.update([(c, c) for c in safe])
+        _safemaps[safe] = quoter = safe_map.__getitem__
+    return _join(map(quoter, s))
+
+
+def _quote_plus(s, safe=''):
+    if ' ' in s:
+        return _quote(s, safe + ' ').replace(' ', '+')
+    return _quote(s, safe)
+
+
+def _safe_urlsplit(s):
+    """the urlparse.urlsplit cache breaks if it contains unicode and
+    we cannot control that.  So we force type cast that thing back
+    to what we think it is.
+    """
+    rv = urlparse.urlsplit(s)
+    if type(rv[1]) is not type(s):
+        try:
+            return tuple(map(type(s), rv))
+        except UnicodeError:
+            # oh well, we most likely will break later again, but
+            # let's just say it worked out well to that point.
+            pass
+    return rv
+
+
+def _unquote(s, unsafe=''):
+    assert isinstance(s, str), 'unquote only works on bytes'
+    rv = s.split('%')
+    if len(rv) == 1:
+        return s
+    s = rv[0]
+    for item in rv[1:]:
+        try:
+            char = _hextochr[item[:2]]
+            if char in unsafe:
+                raise KeyError()
+            s += char + item[2:]
+        except KeyError:
+            s += '%' + item
+    return s
+
+
+def _unquote_plus(s):
+    return _unquote(s.replace('+', ' '))
+
+
+def _uri_split(uri):
+    """Splits up an URI or IRI."""
+    scheme, netloc, path, query, fragment = _safe_urlsplit(uri)
+
+    port = None
+
+    if '@' in netloc:
+        auth, hostname = netloc.split('@', 1)
+    else:
+        auth = None
+        hostname = netloc
+    if hostname:
+        if ':' in hostname:
+            hostname, port = hostname.split(':', 1)
+    return scheme, auth, hostname, port, path, query, fragment
+
+
+def iri_to_uri(iri, charset='utf-8'):
+    r"""Converts any unicode based IRI to an acceptable ASCII URI.  Werkzeug
+    always uses utf-8 URLs internally because this is what browsers and HTTP
+    do as well.  In some places where it accepts an URL it also accepts a
+    unicode IRI and converts it into a URI.
+
+    Examples for IRI versus URI:
+
+    >>> iri_to_uri(u'http://☃.net/')
+    'http://xn--n3h.net/'
+    >>> iri_to_uri(u'http://üser:pässword@☃.net/påth')
+    'http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th'
+
+    .. versionadded:: 0.6
+
+    :param iri: the iri to convert
+    :param charset: the charset for the URI
+    """
+    iri = unicode(iri)
+    scheme, auth, hostname, port, path, query, fragment = _uri_split(iri)
+
+    scheme = scheme.encode('ascii')
+    hostname = hostname.encode('idna')
+    if auth:
+        if ':' in auth:
+            auth, password = auth.split(':', 1)
+        else:
+            password = None
+        auth = _quote(auth.encode(charset))
+        if password:
+            auth += ':' + _quote(password.encode(charset))
+        hostname = auth + '@' + hostname
+    if port:
+        hostname += ':' + port
+
+    path = _quote(path.encode(charset), safe="/:~+")
+    query = _quote(query.encode(charset), safe="=%&[]:;$()+,!?*/")
+
+    return urlparse.urlunsplit([scheme, hostname, path, query, fragment])
+
+
+def uri_to_iri(uri, charset='utf-8', errors='ignore'):
+    r"""Converts a URI in a given charset to a IRI.
+
+    Examples for URI versus IRI
+
+    >>> uri_to_iri('http://xn--n3h.net/')
+    u'http://\u2603.net/'
+    >>> uri_to_iri('http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th')
+    u'http://\xfcser:p\xe4ssword@\u2603.net/p\xe5th'
+
+    Query strings are left unchanged:
+
+    >>> uri_to_iri('/?foo=24&x=%26%2f')
+    u'/?foo=24&x=%26%2f'
+
+    .. versionadded:: 0.6
+
+    :param uri: the URI to convert
+    :param charset: the charset of the URI
+    :param errors: the error handling on decode
+    """
+    uri = url_fix(str(uri), charset)
+    scheme, auth, hostname, port, path, query, fragment = _uri_split(uri)
+
+    scheme = _decode_unicode(scheme, 'ascii', errors)
+
+    try:
+        hostname = hostname.decode('idna')
+    except UnicodeError:
+        # dammit, that codec raised an error.  Because it does not support
+        # any error handling we have to fake it.... badly
+        if errors not in ('ignore', 'replace'):
+            raise
+        hostname = hostname.decode('ascii', errors)
+
+    if auth:
+        if ':' in auth:
+            auth, password = auth.split(':', 1)
+        else:
+            password = None
+        auth = _decode_unicode(_unquote(auth), charset, errors)
+        if password:
+            auth += u':' + _decode_unicode(_unquote(password),
+                                           charset, errors)
+        hostname = auth + u'@' + hostname
+    if port:
+        # port should be numeric, but you never know...
+        hostname += u':' + port.decode(charset, errors)
+
+    path = _decode_unicode(_unquote(path, '/;?'), charset, errors)
+    query = _decode_unicode(_unquote(query, ';/?:@&=+,$'),
+                            charset, errors)
+
+    return urlparse.urlunsplit([scheme, hostname, path, query, fragment])
+
+
+def url_decode(s, charset='utf-8', decode_keys=False, include_empty=True,
+               errors='ignore', separator='&', cls=None):
+    """Parse a querystring and return it as :class:`MultiDict`.  Per default
+    only values are decoded into unicode strings.  If `decode_keys` is set to
+    `True` the same will happen for keys.
+
+    Per default a missing value for a key will default to an empty key.  If
+    you don't want that behavior you can set `include_empty` to `False`.
+
+    Per default encoding errors are ignored.  If you want a different behavior
+    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
+    `HTTPUnicodeError` is raised.
+
+    .. versionchanged:: 0.5
+       In previous versions ";" and "&" could be used for url decoding.
+       This changed in 0.5 where only "&" is supported.  If you want to
+       use ";" instead a different `separator` can be provided.
+
+       The `cls` parameter was added.
+
+    :param s: a string with the query string to decode.
+    :param charset: the charset of the query string.
+    :param decode_keys: set to `True` if you want the keys to be decoded
+                        as well.
+    :param include_empty: Set to `False` if you don't want empty values to
+                          appear in the dict.
+    :param errors: the decoding error behavior.
+    :param separator: the pair separator to be used, defaults to ``&``
+    :param cls: an optional dict class to use.  If this is not specified
+                       or `None` the default :class:`MultiDict` is used.
+    """
+    if cls is None:
+        cls = MultiDict
+    result = []
+    for pair in str(s).split(separator):
+        if not pair:
+            continue
+        if '=' in pair:
+            key, value = pair.split('=', 1)
+        else:
+            key = pair
+            value = ''
+        key = _unquote_plus(key)
+        if decode_keys:
+            key = _decode_unicode(key, charset, errors)
+        result.append((key, url_unquote_plus(value, charset, errors)))
+    return cls(result)
+
+
+def url_encode(obj, charset='utf-8', encode_keys=False, sort=False, key=None,
+               separator='&'):
+    """URL encode a dict/`MultiDict`.  If a value is `None` it will not appear
+    in the result string.  Per default only values are encoded into the target
+    charset strings.  If `encode_keys` is set to ``True`` unicode keys are
+    supported too.
+
+    If `sort` is set to `True` the items are sorted by `key` or the default
+    sorting algorithm.
+
+    .. versionadded:: 0.5
+        `sort`, `key`, and `separator` were added.
+
+    :param obj: the object to encode into a query string.
+    :param charset: the charset of the query string.
+    :param encode_keys: set to `True` if you have unicode keys.
+    :param sort: set to `True` if you want parameters to be sorted by `key`.
+    :param separator: the separator to be used for the pairs.
+    :param key: an optional function to be used for sorting.  For more details
+                check out the :func:`sorted` documentation.
+    """
+    iterable = iter_multi_items(obj)
+    if sort:
+        iterable = sorted(iterable, key=key)
+    tmp = []
+    for key, value in iterable:
+        if value is None:
+            continue
+        if encode_keys and isinstance(key, unicode):
+            key = key.encode(charset)
+        else:
+            key = str(key)
+        if isinstance(value, unicode):
+            value = value.encode(charset)
+        else:
+            value = str(value)
+        tmp.append('%s=%s' % (_quote(key),
+                              _quote_plus(value)))
+    return separator.join(tmp)
+
+
+def url_quote(s, charset='utf-8', safe='/:'):
+    """URL encode a single string with a given encoding.
+
+    :param s: the string to quote.
+    :param charset: the charset to be used.
+    :param safe: an optional sequence of safe characters.
+    """
+    if isinstance(s, unicode):
+        s = s.encode(charset)
+    elif not isinstance(s, str):
+        s = str(s)
+    return _quote(s, safe=safe)
+
+
+def url_quote_plus(s, charset='utf-8', safe=''):
+    """URL encode a single string with the given encoding and convert
+    whitespace to "+".
+
+    :param s: the string to quote.
+    :param charset: the charset to be used.
+    :param safe: an optional sequence of safe characters.
+    """
+    if isinstance(s, unicode):
+        s = s.encode(charset)
+    elif not isinstance(s, str):
+        s = str(s)
+    return _quote_plus(s, safe=safe)
+
+
+def url_unquote(s, charset='utf-8', errors='ignore'):
+    """URL decode a single string with a given decoding.
+
+    Per default encoding errors are ignored.  If you want a different behavior
+    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
+    `HTTPUnicodeError` is raised.
+
+    :param s: the string to unquote.
+    :param charset: the charset to be used.
+    :param errors: the error handling for the charset decoding.
+    """
+    if isinstance(s, unicode):
+        s = s.encode(charset)
+    return _decode_unicode(_unquote(s), charset, errors)
+
+
+def url_unquote_plus(s, charset='utf-8', errors='ignore'):
+    """URL decode a single string with the given decoding and decode
+    a "+" to whitespace.
+
+    Per default encoding errors are ignored.  If you want a different behavior
+    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
+    `HTTPUnicodeError` is raised.
+
+    :param s: the string to unquote.
+    :param charset: the charset to be used.
+    :param errors: the error handling for the charset decoding.
+    """
+    return _decode_unicode(_unquote_plus(s), charset, errors)
+
+
+def url_fix(s, charset='utf-8'):
+    r"""Sometimes you get an URL by a user that just isn't a real URL because
+    it contains unsafe characters like ' ' and so on.  This function can fix
+    some of the problems in a similar way browsers handle data entered by the
+    user:
+
+    >>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffskl\xe4rung)')
+    'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
+
+    :param s: the string with the URL to fix.
+    :param charset: The target charset for the URL if the url was given as
+                    unicode string.
+    """
+    if isinstance(s, unicode):
+        s = s.encode(charset, 'ignore')
+    scheme, netloc, path, qs, anchor = _safe_urlsplit(s)
+    path = _quote(path, '/%')
+    qs = _quote_plus(qs, ':&%=')
+    return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))
+
+
+class Href(object):
+    """Implements a callable that constructs URLs with the given base. The
+    function can be called with any number of positional and keyword
+    arguments which than are used to assemble the URL.  Works with URLs
+    and posix paths.
+
+    Positional arguments are appended as individual segments to
+    the path of the URL:
+
+    >>> href = Href('/foo')
+    >>> href('bar', 23)
+    '/foo/bar/23'
+    >>> href('foo', bar=23)
+    '/foo/foo?bar=23'
+
+    If any of the arguments (positional or keyword) evaluates to `None` it
+    will be skipped.  If no keyword arguments are given the last argument
+    can be a :class:`dict` or :class:`MultiDict` (or any other dict subclass),
+    otherwise the keyword arguments are used for the query parameters, cutting
+    off the first trailing underscore of the parameter name:
+
+    >>> href(is_=42)
+    '/foo?is=42'
+    >>> href({'foo': 'bar'})
+    '/foo?foo=bar'
+
+    Combining of both methods is not allowed:
+
+    >>> href({'foo': 'bar'}, bar=42)
+    Traceback (most recent call last):
+      ...
+    TypeError: keyword arguments and query-dicts can't be combined
+
+    Accessing attributes on the href object creates a new href object with
+    the attribute name as prefix:
+
+    >>> bar_href = href.bar
+    >>> bar_href("blub")
+    '/foo/bar/blub'
+
+    If `sort` is set to `True` the items are sorted by `key` or the default
+    sorting algorithm:
+
+    >>> href = Href("/", sort=True)
+    >>> href(a=1, b=2, c=3)
+    '/?a=1&b=2&c=3'
+
+    .. versionadded:: 0.5
+        `sort` and `key` were added.
+    """
+
+    def __init__(self, base='./', charset='utf-8', sort=False, key=None):
+        if not base:
+            base = './'
+        self.base = base
+        self.charset = charset
+        self.sort = sort
+        self.key = key
+
+    def __getattr__(self, name):
+        if name[:2] == '__':
+            raise AttributeError(name)
+        base = self.base
+        if base[-1:] != '/':
+            base += '/'
+        return Href(urlparse.urljoin(base, name), self.charset, self.sort,
+                    self.key)
+
+    def __call__(self, *path, **query):
+        if path and isinstance(path[-1], dict):
+            if query:
+                raise TypeError('keyword arguments and query-dicts '
+                                'can\'t be combined')
+            query, path = path[-1], path[:-1]
+        elif query:
+            query = dict([(k.endswith('_') and k[:-1] or k, v)
+                          for k, v in query.items()])
+        path = '/'.join([url_quote(x, self.charset) for x in path
+                         if x is not None]).lstrip('/')
+        rv = self.base
+        if path:
+            if not rv.endswith('/'):
+                rv += '/'
+            rv = urlparse.urljoin(rv, path)
+        if query:
+            rv += '?' + url_encode(query, self.charset, sort=self.sort,
+                                   key=self.key)
+        return str(rv)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/useragents.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.useragents
+    ~~~~~~~~~~~~~~~~~~~
+
+    This module provides a helper to inspect user agent strings.  This module
+    is far from complete but should work for most of the currently available
+    browsers.
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+
+
+class UserAgentParser(object):
+    """A simple user agent parser.  Used by the `UserAgent`."""
+
+    platforms = (
+        ('iphone', 'iphone'),
+        (r'darwin|mac|os\s*x', 'macos'),
+        ('win', 'windows'),
+        (r'android', 'android'),
+        (r'x11|lin(\b|ux)?', 'linux'),
+        ('(sun|i86)os', 'solaris'),
+        (r'nintendo\s+wii', 'wii'),
+        ('irix', 'irix'),
+        ('hp-?ux', 'hpux'),
+        ('aix', 'aix'),
+        ('sco|unix_sv', 'sco'),
+        ('bsd', 'bsd'),
+        ('amiga', 'amiga')
+    )
+    browsers = (
+        ('googlebot', 'google'),
+        ('msnbot', 'msn'),
+        ('yahoo', 'yahoo'),
+        ('ask jeeves', 'ask'),
+        (r'aol|america\s+online\s+browser', 'aol'),
+        ('opera', 'opera'),
+        ('chrome', 'chrome'),
+        ('firefox|firebird|phoenix|iceweasel', 'firefox'),
+        ('galeon', 'galeon'),
+        ('safari', 'safari'),
+        ('webkit', 'webkit'),
+        ('camino', 'camino'),
+        ('konqueror', 'konqueror'),
+        ('k-meleon', 'kmeleon'),
+        ('netscape', 'netscape'),
+        (r'msie|microsoft\s+internet\s+explorer', 'msie'),
+        ('lynx', 'lynx'),
+        ('links', 'links'),
+        ('seamonkey|mozilla', 'seamonkey')
+    )
+
+    _browser_version_re = r'(?:%s)[/\sa-z(]*(\d+[.\da-z]+)?(?i)'
+    _language_re = re.compile(
+        r'(?:;\s*|\s+)(\b\w{2}\b(?:-\b\w{2}\b)?)\s*;|'
+        r'(?:\(|\[|;)\s*(\b\w{2}\b(?:-\b\w{2}\b)?)\s*(?:\]|\)|;)'
+    )
+
+    def __init__(self):
+        self.platforms = [(b, re.compile(a, re.I)) for a, b in self.platforms]
+        self.browsers = [(b, re.compile(self._browser_version_re % a))
+                         for a, b in self.browsers]
+
+    def __call__(self, user_agent):
+        for platform, regex in self.platforms:
+            match = regex.search(user_agent)
+            if match is not None:
+                break
+        else:
+            platform = None
+        for browser, regex in self.browsers:
+            match = regex.search(user_agent)
+            if match is not None:
+                version = match.group(1)
+                break
+        else:
+            browser = version = None
+        match = self._language_re.search(user_agent)
+        if match is not None:
+            language = match.group(1) or match.group(2)
+        else:
+            language = None
+        return platform, browser, version, language
+
+
+class UserAgent(object):
+    """Represents a user agent.  Pass it a WSGI environment or a user agent
+    string and you can inspect some of the details from the user agent
+    string via the attributes.  The following attributes exist:
+
+    .. attribute:: string
+
+       the raw user agent string
+
+    .. attribute:: platform
+
+       the browser platform.  The following platforms are currently
+       recognized:
+
+       -   `aix`
+       -   `amiga`
+       -   `android`
+       -   `bsd`
+       -   `hpux`
+       -   `iphone`
+       -   `irix`
+       -   `linux`
+       -   `macos`
+       -   `sco`
+       -   `solaris`
+       -   `wii`
+       -   `windows`
+
+    .. attribute:: browser
+
+        the name of the browser.  The following browsers are currently
+        recognized:
+
+        -   `aol` *
+        -   `ask` *
+        -   `camino`
+        -   `chrome`
+        -   `firefox`
+        -   `galeon`
+        -   `google` *
+        -   `kmeleon`
+        -   `konqueror`
+        -   `links`
+        -   `lynx`
+        -   `msie`
+        -   `msn`
+        -   `netscape`
+        -   `opera`
+        -   `safari`
+        -   `seamonkey`
+        -   `webkit`
+        -   `yahoo` *
+
+        (Browsers maked with a star (``*``) are crawlers.)
+
+    .. attribute:: version
+
+        the version of the browser
+
+    .. attribute:: language
+
+        the language of the browser
+    """
+
+    _parser = UserAgentParser()
+
+    def __init__(self, environ_or_string):
+        if isinstance(environ_or_string, dict):
+            environ_or_string = environ_or_string.get('HTTP_USER_AGENT', '')
+        self.string = environ_or_string
+        self.platform, self.browser, self.version, self.language = \
+            self._parser(environ_or_string)
+
+    def to_header(self):
+        return self.string
+
+    def __str__(self):
+        return self.string
+
+    def __nonzero__(self):
+        return bool(self.browser)
+
+    def __repr__(self):
+        return '<%s %r/%s>' % (
+            self.__class__.__name__,
+            self.browser,
+            self.version
+        )
+
+
+# conceptionally this belongs in this module but because we want to lazily
+# load the user agent module (which happens in wrappers.py) we have to import
+# it afterwards.  The class itself has the module set to this module so
+# pickle, inspect and similar modules treat the object as if it was really
+# implemented here.
+from werkzeug.wrappers import UserAgentMixin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/utils.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,681 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.utils
+    ~~~~~~~~~~~~~~
+
+    This module implements various utilities for WSGI applications.  Most of
+    them are used by the request and response wrappers but especially for
+    middleware development it makes sense to use them without the wrappers.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import os
+from time import time
+from datetime import datetime, timedelta
+
+from werkzeug._internal import _decode_unicode, \
+     _iter_modules, _ExtendedCookie, _ExtendedMorsel, \
+     _DictAccessorProperty, _dump_date, _parse_signature, _missing
+
+
+_format_re = re.compile(r'\$(?:(%s)|\{(%s)\})' % (('[a-zA-Z_][a-zA-Z0-9_]*',) * 2))
+_entity_re = re.compile(r'&([^;]+);')
+_filename_ascii_strip_re = re.compile(r'[^A-Za-z0-9_.-]')
+_windows_device_files = ('CON', 'AUX', 'COM1', 'COM2', 'COM3', 'COM4', 'LPT1',
+                         'LPT2', 'LPT3', 'PRN', 'NUL')
+
+
+class cached_property(object):
+    """A decorator that converts a function into a lazy property.  The
+    function wrapped is called the first time to retrieve the result
+    and then that calculated result is used the next time you access
+    the value::
+
+        class Foo(object):
+
+            @cached_property
+            def foo(self):
+                # calculate something important here
+                return 42
+
+    The class has to have a `__dict__` in order for this property to
+    work.
+
+    .. versionchanged:: 0.6
+       the `writeable` attribute and parameter was deprecated.  If a
+       cached property is writeable or not has to be documented now.
+       For performance reasons the implementation does not honor the
+       writeable setting and will always make the property writeable.
+    """
+
+    # implementation detail: this property is implemented as non-data
+    # descriptor.  non-data descriptors are only invoked if there is
+    # no entry with the same name in the instance's __dict__.
+    # this allows us to completely get rid of the access function call
+    # overhead.  If one choses to invoke __get__ by hand the property
+    # will still work as expected because the lookup logic is replicated
+    # in __get__ for manual invocation.
+
+    def __init__(self, func, name=None, doc=None, writeable=False):
+        if writeable:
+            from warnings import warn
+            warn(DeprecationWarning('the writeable argument to the '
+                                    'cached property is a noop since 0.6 '
+                                    'because the property is writeable '
+                                    'by default for performance reasons'))
+
+        self.__name__ = name or func.__name__
+        self.__module__ = func.__module__
+        self.__doc__ = doc or func.__doc__
+        self.func = func
+
+    def __get__(self, obj, type=None):
+        if obj is None:
+            return self
+        value = obj.__dict__.get(self.__name__, _missing)
+        if value is _missing:
+            value = self.func(obj)
+            obj.__dict__[self.__name__] = value
+        return value
+
+
+class environ_property(_DictAccessorProperty):
+    """Maps request attributes to environment variables. This works not only
+    for the Werzeug request object, but also any other class with an
+    environ attribute:
+
+    >>> class Test(object):
+    ...     environ = {'key': 'value'}
+    ...     test = environ_property('key')
+    >>> var = Test()
+    >>> var.test
+    'value'
+
+    If you pass it a second value it's used as default if the key does not
+    exist, the third one can be a converter that takes a value and converts
+    it.  If it raises :exc:`ValueError` or :exc:`TypeError` the default value
+    is used. If no default value is provided `None` is used.
+
+    Per default the property is read only.  You have to explicitly enable it
+    by passing ``read_only=False`` to the constructor.
+    """
+
+    read_only = True
+
+    def lookup(self, obj):
+        return obj.environ
+
+
+class header_property(_DictAccessorProperty):
+    """Like `environ_property` but for headers."""
+
+    def lookup(self, obj):
+        return obj.headers
+
+
+class HTMLBuilder(object):
+    """Helper object for HTML generation.
+
+    Per default there are two instances of that class.  The `html` one, and
+    the `xhtml` one for those two dialects.  The class uses keyword parameters
+    and positional parameters to generate small snippets of HTML.
+
+    Keyword parameters are converted to XML/SGML attributes, positional
+    arguments are used as children.  Because Python accepts positional
+    arguments before keyword arguments it's a good idea to use a list with the
+    star-syntax for some children:
+
+    >>> html.p(class_='foo', *[html.a('foo', href='foo.html'), ' ',
+    ...                        html.a('bar', href='bar.html')])
+    u'<p class="foo"><a href="foo.html">foo</a> <a href="bar.html">bar</a></p>'
+
+    This class works around some browser limitations and can not be used for
+    arbitrary SGML/XML generation.  For that purpose lxml and similar
+    libraries exist.
+
+    Calling the builder escapes the string passed:
+
+    >>> html.p(html("<foo>"))
+    u'<p>&lt;foo&gt;</p>'
+    """
+
+    from htmlentitydefs import name2codepoint
+    _entity_re = re.compile(r'&([^;]+);')
+    _entities = name2codepoint.copy()
+    _entities['apos'] = 39
+    _empty_elements = set([
+        'area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img',
+        'input', 'isindex', 'link', 'meta', 'param'
+    ])
+    _boolean_attributes = set([
+        'selected', 'checked', 'compact', 'declare', 'defer', 'disabled',
+        'ismap', 'multiple', 'nohref', 'noresize', 'noshade', 'nowrap'
+    ])
+    _plaintext_elements = set(['textarea'])
+    _c_like_cdata = set(['script', 'style'])
+    del name2codepoint
+
+    def __init__(self, dialect):
+        self._dialect = dialect
+
+    def __call__(self, s):
+        return escape(s)
+
+    def __getattr__(self, tag):
+        if tag[:2] == '__':
+            raise AttributeError(tag)
+        def proxy(*children, **arguments):
+            buffer = ['<' + tag]
+            write = buffer.append
+            for key, value in arguments.iteritems():
+                if value is None:
+                    continue
+                if key.endswith('_'):
+                    key = key[:-1]
+                if key in self._boolean_attributes:
+                    if not value:
+                        continue
+                    value = self._dialect == 'xhtml' and '="%s"' % key or ''
+                else:
+                    value = '="%s"' % escape(value, True)
+                write(' ' + key + value)
+            if not children and tag in self._empty_elements:
+                write(self._dialect == 'xhtml' and ' />' or '>')
+                return ''.join(buffer)
+            write('>')
+            children_as_string = ''.join(unicode(x) for x in children
+                                         if x is not None)
+            if children_as_string:
+                if tag in self._plaintext_elements:
+                    children_as_string = escape(children_as_string)
+                elif tag in self._c_like_cdata and self._dialect == 'xhtml':
+                    children_as_string = '/*<![CDATA[*/%s/*]]>*/' % \
+                                         children_as_string
+            buffer.extend((children_as_string, '</%s>' % tag))
+            return ''.join(buffer)
+        return proxy
+
+    def __repr__(self):
+        return '<%s for %r>' % (
+            self.__class__.__name__,
+            self._dialect
+        )
+
+
+html = HTMLBuilder('html')
+xhtml = HTMLBuilder('xhtml')
+
+
+def get_content_type(mimetype, charset):
+    """Return the full content type string with charset for a mimetype.
+
+    If the mimetype represents text the charset will be appended as charset
+    parameter, otherwise the mimetype is returned unchanged.
+
+    :param mimetype: the mimetype to be used as content type.
+    :param charset: the charset to be appended in case it was a text mimetype.
+    :return: the content type.
+    """
+    if mimetype.startswith('text/') or \
+       mimetype == 'application/xml' or \
+       (mimetype.startswith('application/') and
+        mimetype.endswith('+xml')):
+        mimetype += '; charset=' + charset
+    return mimetype
+
+
+def format_string(string, context):
+    """String-template format a string:
+
+    >>> format_string('$foo and ${foo}s', dict(foo=42))
+    '42 and 42s'
+
+    This does not do any attribute lookup etc.  For more advanced string
+    formattings have a look at the `werkzeug.template` module.
+
+    :param string: the format string.
+    :param context: a dict with the variables to insert.
+    """
+    def lookup_arg(match):
+        x = context[match.group(1) or match.group(2)]
+        if not isinstance(x, basestring):
+            x = type(string)(x)
+        return x
+    return _format_re.sub(lookup_arg, string)
+
+
+def secure_filename(filename):
+    r"""Pass it a filename and it will return a secure version of it.  This
+    filename can then safely be stored on a regular file system and passed
+    to :func:`os.path.join`.  The filename returned is an ASCII only string
+    for maximum portability.
+
+    On windows system the function also makes sure that the file is not
+    named after one of the special device files.
+
+    >>> secure_filename("My cool movie.mov")
+    'My_cool_movie.mov'
+    >>> secure_filename("../../../etc/passwd")
+    'etc_passwd'
+    >>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
+    'i_contain_cool_umlauts.txt'
+
+    The function might return an empty filename.  It's your responsibility
+    to ensure that the filename is unique and that you generate random
+    filename if the function returned an empty one.
+
+    .. versionadded:: 0.5
+
+    :param filename: the filename to secure
+    """
+    if isinstance(filename, unicode):
+        from unicodedata import normalize
+        filename = normalize('NFKD', filename).encode('ascii', 'ignore')
+    for sep in os.path.sep, os.path.altsep:
+        if sep:
+            filename = filename.replace(sep, ' ')
+    filename = str(_filename_ascii_strip_re.sub('', '_'.join(
+                   filename.split()))).strip('._')
+
+    # on nt a couple of special files are present in each folder.  We
+    # have to ensure that the target file is not such a filename.  In
+    # this case we prepend an underline
+    if os.name == 'nt' and filename and \
+       filename.split('.')[0].upper() in _windows_device_files:
+        filename = '_' + filename
+
+    return filename
+
+
+def escape(s, quote=False):
+    """Replace special characters "&", "<" and ">" to HTML-safe sequences.  If
+    the optional flag `quote` is `True`, the quotation mark character (") is
+    also translated.
+
+    There is a special handling for `None` which escapes to an empty string.
+
+    :param s: the string to escape.
+    :param quote: set to true to also escape double quotes.
+    """
+    if s is None:
+        return ''
+    elif hasattr(s, '__html__'):
+        return s.__html__()
+    elif not isinstance(s, basestring):
+        s = unicode(s)
+    s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
+    if quote:
+        s = s.replace('"', "&quot;")
+    return s
+
+
+def unescape(s):
+    """The reverse function of `escape`.  This unescapes all the HTML
+    entities, not only the XML entities inserted by `escape`.
+
+    :param s: the string to unescape.
+    """
+    def handle_match(m):
+        name = m.group(1)
+        if name in HTMLBuilder._entities:
+            return unichr(HTMLBuilder._entities[name])
+        try:
+            if name[:2] in ('#x', '#X'):
+                return unichr(int(name[2:], 16))
+            elif name.startswith('#'):
+                return unichr(int(name[1:]))
+        except ValueError:
+            pass
+        return u''
+    return _entity_re.sub(handle_match, s)
+
+
+def cookie_date(expires=None):
+    """Formats the time to ensure compatibility with Netscape's cookie
+    standard.
+
+    Accepts a floating point number expressed in seconds since the epoch in, a
+    datetime object or a timetuple.  All times in UTC.  The :func:`parse_date`
+    function can be used to parse such a date.
+
+    Outputs a string in the format ``Wdy, DD-Mon-YYYY HH:MM:SS GMT``.
+
+    :param expires: If provided that date is used, otherwise the current.
+    """
+    return _dump_date(expires, '-')
+
+
+def parse_cookie(header, charset='utf-8', errors='ignore',
+                 cls=None):
+    """Parse a cookie.  Either from a string or WSGI environ.
+
+    Per default encoding errors are ignored.  If you want a different behavior
+    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
+    :exc:`HTTPUnicodeError` is raised.
+
+    .. versionchanged:: 0.5
+       This function now returns a :class:`TypeConversionDict` instead of a
+       regular dict.  The `cls` parameter was added.
+
+    :param header: the header to be used to parse the cookie.  Alternatively
+                   this can be a WSGI environment.
+    :param charset: the charset for the cookie values.
+    :param errors: the error behavior for the charset decoding.
+    :param cls: an optional dict class to use.  If this is not specified
+                       or `None` the default :class:`TypeConversionDict` is
+                       used.
+    """
+    if isinstance(header, dict):
+        header = header.get('HTTP_COOKIE', '')
+    if cls is None:
+        cls = TypeConversionDict
+    cookie = _ExtendedCookie()
+    cookie.load(header)
+    result = {}
+
+    # decode to unicode and skip broken items.  Our extended morsel
+    # and extended cookie will catch CookieErrors and convert them to
+    # `None` items which we have to skip here.
+    for key, value in cookie.iteritems():
+        if value.value is not None:
+            result[key] = _decode_unicode(unquote_header_value(value.value),
+                                          charset, errors)
+
+    return cls(result)
+
+
+def dump_cookie(key, value='', max_age=None, expires=None, path='/',
+                domain=None, secure=None, httponly=False, charset='utf-8',
+                sync_expires=True):
+    """Creates a new Set-Cookie header without the ``Set-Cookie`` prefix
+    The parameters are the same as in the cookie Morsel object in the
+    Python standard library but it accepts unicode data, too.
+
+    :param max_age: should be a number of seconds, or `None` (default) if
+                    the cookie should last only as long as the client's
+                    browser session.  Additionally `timedelta` objects
+                    are accepted, too.
+    :param expires: should be a `datetime` object or unix timestamp.
+    :param path: limits the cookie to a given path, per default it will
+                 span the whole domain.
+    :param domain: Use this if you want to set a cross-domain cookie. For
+                   example, ``domain=".example.com"`` will set a cookie
+                   that is readable by the domain ``www.example.com``,
+                   ``foo.example.com`` etc. Otherwise, a cookie will only
+                   be readable by the domain that set it.
+    :param secure: The cookie will only be available via HTTPS
+    :param httponly: disallow JavaScript to access the cookie.  This is an
+                     extension to the cookie standard and probably not
+                     supported by all browsers.
+    :param charset: the encoding for unicode values.
+    :param sync_expires: automatically set expires if max_age is defined
+                         but expires not.
+    """
+    try:
+        key = str(key)
+    except UnicodeError:
+        raise TypeError('invalid key %r' % key)
+    if isinstance(value, unicode):
+        value = value.encode(charset)
+    value = quote_header_value(value)
+    morsel = _ExtendedMorsel(key, value)
+    if isinstance(max_age, timedelta):
+        max_age = (max_age.days * 60 * 60 * 24) + max_age.seconds
+    if expires is not None:
+        if not isinstance(expires, basestring):
+            expires = cookie_date(expires)
+        morsel['expires'] = expires
+    elif max_age is not None and sync_expires:
+        morsel['expires'] = cookie_date(time() + max_age)
+    for k, v in (('path', path), ('domain', domain), ('secure', secure),
+                 ('max-age', max_age), ('httponly', httponly)):
+        if v is not None and v is not False:
+            morsel[k] = str(v)
+    return morsel.output(header='').lstrip()
+
+
+def http_date(timestamp=None):
+    """Formats the time to match the RFC1123 date format.
+
+    Accepts a floating point number expressed in seconds since the epoch in, a
+    datetime object or a timetuple.  All times in UTC.  The :func:`parse_date`
+    function can be used to parse such a date.
+
+    Outputs a string in the format ``Wdy, DD Mon YYYY HH:MM:SS GMT``.
+
+    :param timestamp: If provided that date is used, otherwise the current.
+    """
+    return _dump_date(timestamp, ' ')
+
+
+def redirect(location, code=302):
+    """Return a response object (a WSGI application) that, if called,
+    redirects the client to the target location.  Supported codes are 301,
+    302, 303, 305, and 307.  300 is not supported because it's not a real
+    redirect and 304 because it's the answer for a request with a request
+    with defined If-Modified-Since headers.
+
+    .. versionadded:: 0.6
+       The location can now be a unicode string that is encoded using
+       the :func:`iri_to_uri` function.
+
+    :param location: the location the response should redirect to.
+    :param code: the redirect status code.
+    """
+    assert code in (301, 302, 303, 305, 307), 'invalid code'
+    from werkzeug.wrappers import BaseResponse
+    display_location = location
+    if isinstance(location, unicode):
+        from werkzeug.urls import iri_to_uri
+        location = iri_to_uri(location)
+    response = BaseResponse(
+        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
+        '<title>Redirecting...</title>\n'
+        '<h1>Redirecting...</h1>\n'
+        '<p>You should be redirected automatically to target URL: '
+        '<a href="%s">%s</a>.  If not click the link.' %
+        (location, display_location), code, mimetype='text/html')
+    response.headers['Location'] = location
+    return response
+
+
+def append_slash_redirect(environ, code=301):
+    """Redirect to the same URL but with a slash appended.  The behavior
+    of this function is undefined if the path ends with a slash already.
+
+    :param environ: the WSGI environment for the request that triggers
+                    the redirect.
+    :param code: the status code for the redirect.
+    """
+    new_path = environ['PATH_INFO'].strip('/') + '/'
+    query_string = environ.get('QUERY_STRING')
+    if query_string:
+        new_path += '?' + query_string
+    return redirect(new_path, code)
+
+
+def import_string(import_name, silent=False):
+    """Imports an object based on a string.  This is useful if you want to
+    use import paths as endpoints or something similar.  An import path can
+    be specified either in dotted notation (``xml.sax.saxutils.escape``)
+    or with a colon as object delimiter (``xml.sax.saxutils:escape``).
+
+    If `silent` is True the return value will be `None` if the import fails.
+
+    :param import_name: the dotted name for the object to import.
+    :param silent: if set to `True` import errors are ignored and
+                   `None` is returned instead.
+    :return: imported object
+    """
+    # force the import name to automatically convert to strings
+    if isinstance(import_name, unicode):
+        import_name = str(import_name)
+    try:
+        if ':' in import_name:
+            module, obj = import_name.split(':', 1)
+        elif '.' in import_name:
+            module, obj = import_name.rsplit('.', 1)
+        else:
+            return __import__(import_name)
+        # __import__ is not able to handle unicode strings in the fromlist
+        # if the module is a package
+        if isinstance(obj, unicode):
+            obj = obj.encode('utf-8')
+        try:
+            return getattr(__import__(module, None, None, [obj]), obj)
+        except (ImportError, AttributeError):
+            # support importing modules not yet set up by the parent module
+            # (or package for that matter)
+            modname = module + '.' + obj
+            __import__(modname)
+            return sys.modules[modname]
+    except ImportError:
+        if not silent:
+            raise
+
+
+def find_modules(import_path, include_packages=False, recursive=False):
+    """Find all the modules below a package.  This can be useful to
+    automatically import all views / controllers so that their metaclasses /
+    function decorators have a chance to register themselves on the
+    application.
+
+    Packages are not returned unless `include_packages` is `True`.  This can
+    also recursively list modules but in that case it will import all the
+    packages to get the correct load path of that module.
+
+    :param import_name: the dotted name for the package to find child modules.
+    :param include_packages: set to `True` if packages should be returned, too.
+    :param recursive: set to `True` if recursion should happen.
+    :return: generator
+    """
+    module = import_string(import_path)
+    path = getattr(module, '__path__', None)
+    if path is None:
+        raise ValueError('%r is not a package' % import_path)
+    basename = module.__name__ + '.'
+    for modname, ispkg in _iter_modules(path):
+        modname = basename + modname
+        if ispkg:
+            if include_packages:
+                yield modname
+            if recursive:
+                for item in find_modules(modname, include_packages, True):
+                    yield item
+        else:
+            yield modname
+
+
+def validate_arguments(func, args, kwargs, drop_extra=True):
+    """Check if the function accepts the arguments and keyword arguments.
+    Returns a new ``(args, kwargs)`` tuple that can safely be passed to
+    the function without causing a `TypeError` because the function signature
+    is incompatible.  If `drop_extra` is set to `True` (which is the default)
+    any extra positional or keyword arguments are dropped automatically.
+
+    The exception raised provides three attributes:
+
+    `missing`
+        A set of argument names that the function expected but where
+        missing.
+
+    `extra`
+        A dict of keyword arguments that the function can not handle but
+        where provided.
+
+    `extra_positional`
+        A list of values that where given by positional argument but the
+        function cannot accept.
+
+    This can be useful for decorators that forward user submitted data to
+    a view function::
+
+        from werkzeug import ArgumentValidationError, validate_arguments
+
+        def sanitize(f):
+            def proxy(request):
+                data = request.values.to_dict()
+                try:
+                    args, kwargs = validate_arguments(f, (request,), data)
+                except ArgumentValidationError:
+                    raise BadRequest('The browser failed to transmit all '
+                                     'the data expected.')
+                return f(*args, **kwargs)
+            return proxy
+
+    :param func: the function the validation is performed against.
+    :param args: a tuple of positional arguments.
+    :param kwargs: a dict of keyword arguments.
+    :param drop_extra: set to `False` if you don't want extra arguments
+                       to be silently dropped.
+    :return: tuple in the form ``(args, kwargs)``.
+    """
+    parser = _parse_signature(func)
+    args, kwargs, missing, extra, extra_positional = parser(args, kwargs)[:5]
+    if missing:
+        raise ArgumentValidationError(tuple(missing))
+    elif (extra or extra_positional) and not drop_extra:
+        raise ArgumentValidationError(None, extra, extra_positional)
+    return tuple(args), kwargs
+
+
+def bind_arguments(func, args, kwargs):
+    """Bind the arguments provided into a dict.  When passed a function,
+    a tuple of arguments and a dict of keyword arguments `bind_arguments`
+    returns a dict of names as the function would see it.  This can be useful
+    to implement a cache decorator that uses the function arguments to build
+    the cache key based on the values of the arguments.
+
+    :param func: the function the arguments should be bound for.
+    :param args: tuple of positional arguments.
+    :param kwargs: a dict of keyword arguments.
+    :return: a :class:`dict` of bound keyword arguments.
+    """
+    args, kwargs, missing, extra, extra_positional, \
+        arg_spec, vararg_var, kwarg_var = _parse_signature(func)(args, kwargs)
+    values = {}
+    for (name, has_default, default), value in zip(arg_spec, args):
+        values[name] = value
+    if vararg_var is not None:
+        values[vararg_var] = tuple(extra_positional)
+    elif extra_positional:
+        raise TypeError('too many positional arguments')
+    if kwarg_var is not None:
+        multikw = set(extra) & set([x[0] for x in arg_spec])
+        if multikw:
+            raise TypeError('got multiple values for keyword argument ' +
+                            repr(iter(multikw).next()))
+        values[kwarg_var] = extra
+    elif extra:
+        raise TypeError('got unexpected keyword argument ' +
+                        repr(iter(extra).next()))
+    return values
+
+
+class ArgumentValidationError(ValueError):
+    """Raised if :func:`validate_arguments` fails to validate"""
+
+    def __init__(self, missing=None, extra=None, extra_positional=None):
+        self.missing = set(missing or ())
+        self.extra = extra or {}
+        self.extra_positional = extra_positional or []
+        ValueError.__init__(self, 'function arguments invalid.  ('
+                            '%d missing, %d additional)' % (
+            len(self.missing),
+            len(self.extra) + len(self.extra_positional)
+        ))
+
+
+# circular dependencies
+from werkzeug.http import quote_header_value, unquote_header_value
+from werkzeug.exceptions import BadRequest
+from werkzeug.datastructures import TypeConversionDict
+
+
+# DEPRECATED
+# these objects were previously in this module as well.  we import
+# them here for backwards compatibility with old pickles.
+from werkzeug.datastructures import MultiDict, CombinedMultiDict, \
+     Headers, EnvironHeaders
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/wrappers.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,1495 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.wrappers
+    ~~~~~~~~~~~~~~~~~
+
+    The wrappers are simple request and response objects which you can
+    subclass to do whatever you want them to do.  The request object contains
+    the information transmitted by the client (webbrowser) and the response
+    object contains all the information sent back to the browser.
+
+    An important detail is that the request object is created with the WSGI
+    environ and will act as high-level proxy whereas the response object is an
+    actual WSGI application.
+
+    Like everything else in Werkzeug these objects will work correctly with
+    unicode data.  Incoming form data parsed by the response object will be
+    decoded into an unicode object if possible and if it makes sense.
+
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import tempfile
+import urlparse
+from datetime import datetime, timedelta
+
+from werkzeug.http import HTTP_STATUS_CODES, \
+     parse_accept_header, parse_cache_control_header, parse_etags, \
+     parse_date, generate_etag, is_resource_modified, unquote_etag, \
+     quote_etag, parse_set_header, parse_authorization_header, \
+     parse_www_authenticate_header, remove_entity_headers, \
+     parse_options_header, dump_options_header
+from werkzeug.urls import url_decode, iri_to_uri
+from werkzeug.formparser import parse_form_data, default_stream_factory
+from werkzeug.utils import cached_property, environ_property, \
+     cookie_date, parse_cookie, dump_cookie, http_date, escape, \
+     header_property, get_content_type
+from werkzeug.wsgi import get_current_url, get_host, LimitedStream, \
+     ClosingIterator
+from werkzeug.datastructures import MultiDict, CombinedMultiDict, Headers, \
+     EnvironHeaders, ImmutableMultiDict, ImmutableTypeConversionDict, \
+     ImmutableList, MIMEAccept, CharsetAccept, LanguageAccept, \
+     ResponseCacheControl, RequestCacheControl, CallbackDict
+from werkzeug._internal import _empty_stream, _decode_unicode, \
+     _patch_wrapper, _get_environ
+
+
+def _run_wsgi_app(*args):
+    """This function replaces itself to ensure that the test module is not
+    imported unless required.  DO NOT USE!
+    """
+    global _run_wsgi_app
+    from werkzeug.test import run_wsgi_app as _run_wsgi_app
+    return _run_wsgi_app(*args)
+
+
+def _warn_if_string(iterable):
+    """Helper for the response objects to check if the iterable returned
+    to the WSGI server is not a string.
+    """
+    if isinstance(iterable, basestring):
+        from warnings import warn
+        warn(Warning('response iterable was set to a string.  This appears '
+                     'to work but means that the server will send the '
+                     'data to the client char, by char.  This is almost '
+                     'never intended behavior, use response.data to assign '
+                     'strings to the response object.'), stacklevel=2)
+
+
+class BaseRequest(object):
+    """Very basic request object.  This does not implement advanced stuff like
+    entity tag parsing or cache controls.  The request object is created with
+    the WSGI environment as first argument and will add itself to the WSGI
+    environment as ``'werkzeug.request'`` unless it's created with
+    `populate_request` set to False.
+
+    There are a couple of mixins available that add additional functionality
+    to the request object, there is also a class called `Request` which
+    subclasses `BaseRequest` and all the important mixins.
+
+    It's a good idea to create a custom subclass of the :class:`BaseRequest`
+    and add missing functionality either via mixins or direct implementation.
+    Here an example for such subclasses::
+
+        from werkzeug import BaseRequest, ETagRequestMixin
+
+        class Request(BaseRequest, ETagRequestMixin):
+            pass
+
+    Request objects are **read only**.  As of 0.5 modifications are not
+    allowed in any place.  Unlike the lower level parsing functions the
+    request object will use immutable objects everywhere possible.
+
+    Per default the request object will assume all the text data is `utf-8`
+    encoded.  Please refer to `the unicode chapter <unicode.txt>`_ for more
+    details about customizing the behavior.
+
+    Per default the request object will be added to the WSGI
+    environment as `werkzeug.request` to support the debugging system.
+    If you don't want that, set `populate_request` to `False`.
+
+    If `shallow` is `True` the environment is initialized as shallow
+    object around the environ.  Every operation that would modify the
+    environ in any way (such as consuming form data) raises an exception
+    unless the `shallow` attribute is explicitly set to `False`.  This
+    is useful for middlewares where you don't want to consume the form
+    data by accident.  A shallow request is not populated to the WSGI
+    environment.
+
+    .. versionchanged:: 0.5
+       read-only mode was enforced by using immutables classes for all
+       data.
+    """
+
+    #: the charset for the request, defaults to utf-8
+    charset = 'utf-8'
+
+    #: the error handling procedure for errors, defaults to 'ignore'
+    encoding_errors = 'ignore'
+
+    #: set to True if the application runs behind an HTTP proxy
+    is_behind_proxy = False
+
+    #: the maximum content length.  This is forwarded to the form data
+    #: parsing function (:func:`parse_form_data`).  When set and the
+    #: :attr:`form` or :attr:`files` attribute is accessed and the
+    #: parsing fails because more than the specified value is transmitted
+    #: a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
+    #:
+    #: Have a look at :ref:`dealing-with-request-data` for more details.
+    #:
+    #: .. versionadded:: 0.5
+    max_content_length = None
+
+    #: the maximum form field size.  This is forwarded to the form data
+    #: parsing function (:func:`parse_form_data`).  When set and the
+    #: :attr:`form` or :attr:`files` attribute is accessed and the
+    #: data in memory for post data is longer than the specified value a
+    #: :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
+    #:
+    #: Have a look at :ref:`dealing-with-request-data` for more details.
+    #:
+    #: .. versionadded:: 0.5
+    max_form_memory_size = None
+
+    #: the class to use for `args` and `form`.  The default is an
+    #: :class:`ImmutableMultiDict` which supports multiple values per key.
+    #: alternatively it makes sense to use an :class:`ImmutableOrderedMultiDict`
+    #: which preserves order or a :class:`ImmutableDict` which is
+    #: the fastest but only remembers the last key.  It is also possible
+    #: to use mutable structures, but this is not recommended.
+    #:
+    #: .. versionadded:: 0.6
+    parameter_storage_class = ImmutableMultiDict
+
+    #: the type to be used for list values from the incoming WSGI
+    #: environment.  By default an :class:`ImmutableList` is used
+    #: (for example for :attr:`access_list`).
+    #:
+    #: .. versionadded:: 0.6
+    list_storage_class = ImmutableList
+
+    #: the type to be used for dict values from the incoming WSGI
+    #: environment.  By default an :class:`ImmutableTypeConversionDict`
+    #: is used (for example for :attr:`cookies`).
+    #:
+    #: .. versionadded:: 0.6
+    dict_storage_class = ImmutableTypeConversionDict
+
+    def __init__(self, environ, populate_request=True, shallow=False):
+        self.environ = environ
+        if populate_request and not shallow:
+            self.environ['werkzeug.request'] = self
+        self.shallow = shallow
+
+    def __repr__(self):
+        # make sure the __repr__ even works if the request was created
+        # from an invalid WSGI environment.  If we display the request
+        # in a debug session we don't want the repr to blow up.
+        args = []
+        try:
+            args.append("'%s'" % self.url)
+            args.append('[%s]' % self.method)
+        except:
+            args.append('(invalid WSGI environ)')
+
+        return '<%s %s>' % (
+            self.__class__.__name__,
+            ' '.join(args)
+        )
+
+    @property
+    def url_charset(self):
+        """The charset that is assumed for URLs.  Defaults to the value
+        of :attr:`charset`.
+
+        .. versionadded:: 0.6
+        """
+        return self.charset
+
+    @classmethod
+    def from_values(cls, *args, **kwargs):
+        """Create a new request object based on the values provided.  If
+        environ is given missing values are filled from there.  This method is
+        useful for small scripts when you need to simulate a request from an URL.
+        Do not use this method for unittesting, there is a full featured client
+        object (:class:`Client`) that allows to create multipart requests,
+        support for cookies etc.
+
+        This accepts the same options as the :class:`EnvironBuilder`.
+
+        .. versionchanged:: 0.5
+           This method now accepts the same arguments as
+           :class:`EnvironBuilder`.  Because of this the `environ` parameter
+           is now called `environ_overrides`.
+
+        :return: request object
+        """
+        from werkzeug.test import EnvironBuilder
+        charset = kwargs.pop('charset', cls.charset)
+        builder = EnvironBuilder(*args, **kwargs)
+        try:
+            return builder.get_request(cls)
+        finally:
+            builder.close()
+
+    @classmethod
+    def application(cls, f):
+        """Decorate a function as responder that accepts the request as first
+        argument.  This works like the :func:`responder` decorator but the
+        function is passed the request object as first argument::
+
+            @Request.application
+            def my_wsgi_app(request):
+                return Response('Hello World!')
+
+        :param f: the WSGI callable to decorate
+        :return: a new WSGI callable
+        """
+        #: return a callable that wraps the -2nd argument with the request
+        #: and calls the function with all the arguments up to that one and
+        #: the request.  The return value is then called with the latest
+        #: two arguments.  This makes it possible to use this decorator for
+        #: both methods and standalone WSGI functions.
+        return _patch_wrapper(f, lambda *a: f(*a[:-2]+(cls(a[-2]),))(*a[-2:]))
+
+    def _get_file_stream(self, total_content_length, content_type, filename=None,
+                         content_length=None):
+        """Called to get a stream for the file upload.
+
+        This must provide a file-like class with `read()`, `readline()`
+        and `seek()` methods that is both writeable and readable.
+
+        The default implementation returns a temporary file if the total
+        content length is higher than 500KB.  Because many browsers do not
+        provide a content length for the files only the total content
+        length matters.
+
+        .. versionchanged:: 0.5
+           Previously this function was not passed any arguments.  In 0.5 older
+           functions not accepting any arguments are still supported for
+           backwards compatibility.
+
+        :param total_content_length: the total content length of all the
+                                     data in the request combined.  This value
+                                     is guaranteed to be there.
+        :param content_type: the mimetype of the uploaded file.
+        :param filename: the filename of the uploaded file.  May be `None`.
+        :param content_length: the length of this file.  This value is usually
+                               not provided because webbrowsers do not provide
+                               this value.
+        """
+        return default_stream_factory(total_content_length, content_type,
+                                      filename, content_length)
+
+    def _load_form_data(self):
+        """Method used internally to retrieve submitted data.  After calling
+        this sets `form` and `files` on the request object to multi dicts
+        filled with the incoming form data.  As a matter of fact the input
+        stream will be empty afterwards.
+
+        :internal:
+        """
+        # abort early if we have already consumed the stream
+        if 'stream' in self.__dict__:
+            return
+        if self.shallow:
+            raise RuntimeError('A shallow request tried to consume '
+                               'form data.  If you really want to do '
+                               'that, set `shallow` to False.')
+        data = None
+        stream = _empty_stream
+        if self.environ['REQUEST_METHOD'] in ('POST', 'PUT'):
+            try:
+                data = parse_form_data(self.environ, self._get_file_stream,
+                                       self.charset, self.encoding_errors,
+                                       self.max_form_memory_size,
+                                       self.max_content_length,
+                                       cls=self.parameter_storage_class,
+                                       silent=False)
+            except ValueError, e:
+                self._form_parsing_failed(e)
+        else:
+            # if we have a content length header we are able to properly
+            # guard the incoming stream, no matter what request method is
+            # used.
+            content_length = self.headers.get('content-length', type=int)
+            if content_length is not None:
+                stream = LimitedStream(self.environ['wsgi.input'],
+                                       content_length)
+
+        if data is None:
+            data = (stream, self.parameter_storage_class(),
+                    self.parameter_storage_class())
+
+        # inject the values into the instance dict so that we bypass
+        # our cached_property non-data descriptor.
+        d = self.__dict__
+        d['stream'], d['form'], d['files'] = data
+
+    def _form_parsing_failed(self, error):
+        """Called if parsing of form data failed.  This is currently only
+        invoked for failed multipart uploads.  By default this method does
+        nothing.
+
+        :param error: a `ValueError` object with a message why the
+                      parsing failed.
+
+        .. versionadded:: 0.5.1
+        """
+
+    @cached_property
+    def stream(self):
+        """The parsed stream if the submitted data was not multipart or
+        urlencoded form data.  This stream is the stream left by the form data
+        parser module after parsing.  This is *not* the WSGI input stream but
+        a wrapper around it that ensures the caller does not accidentally
+        read past `Content-Length`.
+        """
+        self._load_form_data()
+        return self.stream
+
+    input_stream = environ_property('wsgi.input', 'The WSGI input stream.\n'
+        'In general it\'s a bad idea to use this one because you can easily '
+        'read past the boundary.  Use the :attr:`stream` instead.')
+
+    @cached_property
+    def args(self):
+        """The parsed URL parameters.  By default a :class:`ImmutableMultiDict`
+        is returned from this function.  This can be changed by setting
+        :attr:`parameter_storage_class` to a different type.  This might
+        be necessary if the order of the form data is important.
+        """
+        return url_decode(self.environ.get('QUERY_STRING', ''),
+                          self.url_charset, errors=self.encoding_errors,
+                          cls=self.parameter_storage_class)
+
+    @cached_property
+    def data(self):
+        """This reads the buffered incoming data from the client into the
+        string.  Usually it's a bad idea to access :attr:`data` because a client
+        could send dozens of megabytes or more to cause memory problems on the
+        server.
+
+        To circumvent that make sure to check the content length first.
+        """
+        return self.stream.read()
+
+    @cached_property
+    def form(self):
+        """The form parameters.  By default a :class:`ImmutableMultiDict`
+        is returned from this function.  This can be changed by setting
+        :attr:`parameter_storage_class` to a different type.  This might
+        be necessary if the order of the form data is important.
+        """
+        self._load_form_data()
+        return self.form
+
+    @cached_property
+    def values(self):
+        """Combined multi dict for :attr:`args` and :attr:`form`."""
+        args = []
+        for d in self.args, self.form:
+            if not isinstance(d, MultiDict):
+                d = MultiDict(d)
+            args.append(d)
+        return CombinedMultiDict(args)
+
+    @cached_property
+    def files(self):
+        """:class:`MultiDict` object containing all uploaded files.  Each key in
+        :attr:`files` is the name from the ``<input type="file" name="">``.  Each
+        value in :attr:`files` is a Werkzeug :class:`FileStorage` object.
+
+        Note that :attr:`files` will only contain data if the request method was
+        POST or PUT and the ``<form>`` that posted to the request had
+        ``enctype="multipart/form-data"``.  It will be empty otherwise.
+
+        See the :class:`MultiDict` / :class:`FileStorage` documentation for more
+        details about the used data structure.
+        """
+        self._load_form_data()
+        return self.files
+
+    @cached_property
+    def cookies(self):
+        """Read only access to the retrieved cookie values as dictionary."""
+        return parse_cookie(self.environ, self.charset,
+                            cls=self.dict_storage_class)
+
+    @cached_property
+    def headers(self):
+        """The headers from the WSGI environ as immutable
+        :class:`EnvironHeaders`.
+        """
+        return EnvironHeaders(self.environ)
+
+    @cached_property
+    def path(self):
+        """Requested path as unicode.  This works a bit like the regular path
+        info in the WSGI environment but will always include a leading slash,
+        even if the URL root is accessed.
+        """
+        path = '/' + (self.environ.get('PATH_INFO') or '').lstrip('/')
+        return _decode_unicode(path, self.url_charset, self.encoding_errors)
+
+    @cached_property
+    def script_root(self):
+        """The root path of the script without the trailing slash."""
+        path = (self.environ.get('SCRIPT_NAME') or '').rstrip('/')
+        return _decode_unicode(path, self.url_charset, self.encoding_errors)
+
+    @cached_property
+    def url(self):
+        """The reconstructed current URL"""
+        return get_current_url(self.environ)
+
+    @cached_property
+    def base_url(self):
+        """Like :attr:`url` but without the querystring"""
+        return get_current_url(self.environ, strip_querystring=True)
+
+    @cached_property
+    def url_root(self):
+        """The full URL root (with hostname), this is the application root."""
+        return get_current_url(self.environ, True)
+
+    @cached_property
+    def host_url(self):
+        """Just the host with scheme."""
+        return get_current_url(self.environ, host_only=True)
+
+    @cached_property
+    def host(self):
+        """Just the host including the port if available."""
+        return get_host(self.environ)
+
+    query_string = environ_property('QUERY_STRING', '', read_only=True, doc=
+        '''The URL parameters as raw bytestring.''')
+    method = environ_property('REQUEST_METHOD', 'GET', read_only=True, doc=
+        '''The transmission method. (For example ``'GET'`` or ``'POST'``).''')
+
+    @cached_property
+    def access_route(self):
+        """If a forwarded header exists this is a list of all ip addresses
+        from the client ip to the last proxy server.
+        """
+        if 'HTTP_X_FORWARDED_FOR' in self.environ:
+            addr = self.environ['HTTP_X_FORWARDED_FOR'].split(',')
+            return self.list_storage_class([x.strip() for x in addr])
+        elif 'REMOTE_ADDR' in self.environ:
+            return self.list_storage_class([self.environ['REMOTE_ADDR']])
+        return self.list_storage_class()
+
+    @property
+    def remote_addr(self):
+        """The remote address of the client."""
+        if self.is_behind_proxy and self.access_route:
+            return self.access_route[0]
+        return self.environ.get('REMOTE_ADDR')
+
+    remote_user = environ_property('REMOTE_USER', doc='''
+        If the server supports user authentication, and the script is
+        protected, this attribute contains the username the user has
+        authenticated as.''')
+
+    is_xhr = property(lambda x: x.environ.get('HTTP_X_REQUESTED_WITH', '')
+                      .lower() == 'xmlhttprequest', doc='''
+        True if the request was triggered via a JavaScript XMLHttpRequest.
+        This only works with libraries that support the `X-Requested-With`
+        header and set it to "XMLHttpRequest".  Libraries that do that are
+        prototype, jQuery and Mochikit and probably some more.''')
+    is_secure = property(lambda x: x.environ['wsgi.url_scheme'] == 'https',
+                         doc='`True` if the request is secure.')
+    is_multithread = environ_property('wsgi.multithread', doc='''
+        boolean that is `True` if the application is served by
+        a multithreaded WSGI server.''')
+    is_multiprocess = environ_property('wsgi.multiprocess', doc='''
+        boolean that is `True` if the application is served by
+        a WSGI server that spawns multiple processes.''')
+    is_run_once = environ_property('wsgi.run_once', doc='''
+        boolean that is `True` if the application will be executed only
+        once in a process lifetime.  This is the case for CGI for example,
+        but it's not guaranteed that the exeuction only happens one time.''')
+
+
+class BaseResponse(object):
+    """Base response class.  The most important fact about a response object
+    is that it's a regular WSGI application.  It's initialized with a couple
+    of response parameters (headers, body, status code etc.) and will start a
+    valid WSGI response when called with the environ and start response
+    callable.
+
+    Because it's a WSGI application itself processing usually ends before the
+    actual response is sent to the server.  This helps debugging systems
+    because they can catch all the exceptions before responses are started.
+
+    Here a small example WSGI application that takes advantage of the
+    response objects::
+
+        from werkzeug import BaseResponse as Response
+
+        def index():
+            return Response('Index page')
+
+        def application(environ, start_response):
+            path = environ.get('PATH_INFO') or '/'
+            if path == '/':
+                response = index()
+            else:
+                response = Response('Not Found', status=404)
+            return response(environ, start_response)
+
+    Like :class:`BaseRequest` which object is lacking a lot of functionality
+    implemented in mixins.  This gives you a better control about the actual
+    API of your response objects, so you can create subclasses and add custom
+    functionality.  A full featured response object is available as
+    :class:`Response` which implements a couple of useful mixins.
+
+    To enforce a new type of already existing responses you can use the
+    :meth:`force_type` method.  This is useful if you're working with different
+    subclasses of response objects and you want to post process them with a
+    know interface.
+
+    Per default the request object will assume all the text data is `utf-8`
+    encoded.  Please refer to `the unicode chapter <unicode.txt>`_ for more
+    details about customizing the behavior.
+
+    Response can be any kind of iterable or string.  If it's a string
+    it's considered being an iterable with one item which is the string
+    passed.  Headers can be a list of tuples or a :class:`Headers` object.
+
+    Special note for `mimetype` and `content_type`:  For most mime types
+    `mimetype` and `content_type` work the same, the difference affects
+    only 'text' mimetypes.  If the mimetype passed with `mimetype` is a
+    mimetype starting with `text/` it becomes a charset parameter defined
+    with the charset of the response object.  In contrast the
+    `content_type` parameter is always added as header unmodified.
+
+    .. versionchanged:: 0.5
+       the `direct_passthrough` parameter was added.
+
+    :param response: a string or response iterable.
+    :param status: a string with a status or an integer with the status code.
+    :param headers: a list of headers or an :class:`Headers` object.
+    :param mimetype: the mimetype for the request.  See notice above.
+    :param content_type: the content type for the request.  See notice above.
+    :param direct_passthrough: if set to `True` :meth:`iter_encoded` is not
+                               called before iteration which makes it
+                               possible to pass special iterators though
+                               unchanged (see :func:`wrap_file` for more
+                               details.)
+    """
+
+    #: the charset of the response.
+    charset = 'utf-8'
+
+    #: the default status if none is provided.
+    default_status = 200
+
+    #: the default mimetype if none is provided.
+    default_mimetype = 'text/plain'
+
+    #: if set to `False` accessing properties on the response object will
+    #: not try to consume the response iterator and convert it into a list.
+    #:
+    #: .. versionadded:: 0.6.2
+    #:
+    #:    That attribute was previously called `implicit_seqence_conversion`.
+    #:    (Notice the typo).  If you did use this feature, you have to adapt
+    #:    your code to the name change.
+    implicit_sequence_conversion = True
+
+    def __init__(self, response=None, status=None, headers=None,
+                 mimetype=None, content_type=None, direct_passthrough=False):
+        if isinstance(headers, Headers):
+            self.headers = headers
+        elif not headers:
+            self.headers = Headers()
+        else:
+            self.headers = Headers(headers)
+
+        if content_type is None:
+            if mimetype is None and 'content-type' not in self.headers:
+                mimetype = self.default_mimetype
+            if mimetype is not None:
+                mimetype = get_content_type(mimetype, self.charset)
+            content_type = mimetype
+        if content_type is not None:
+            self.headers['Content-Type'] = content_type
+        if status is None:
+            status = self.default_status
+        if isinstance(status, (int, long)):
+            self.status_code = status
+        else:
+            self.status = status
+
+        self.direct_passthrough = direct_passthrough
+        self._on_close = []
+
+        # we set the response after the headers so that if a class changes
+        # the charset attribute, the data is set in the correct charset.
+        if response is None:
+            self.response = []
+        elif isinstance(response, basestring):
+            self.data = response
+        else:
+            self.response = response
+
+    def call_on_close(self, func):
+        """Adds a function to the internal list of functions that should
+        be called as part of closing down the response.
+
+        .. versionadded:: 0.6
+        """
+        self._on_close.append(func)
+
+    def __repr__(self):
+        if self.is_sequence:
+            body_info = '%d bytes' % sum(map(len, self.iter_encoded()))
+        else:
+            body_info = self.is_streamed and 'streamed' or 'likely-streamed'
+        return '<%s %s [%s]>' % (
+            self.__class__.__name__,
+            body_info,
+            self.status
+        )
+
+    @classmethod
+    def force_type(cls, response, environ=None):
+        """Enforce that the WSGI response is a response object of the current
+        type.  Werkzeug will use the :class:`BaseResponse` internally in many
+        situations like the exceptions.  If you call :meth:`get_response` on an
+        exception you will get back a regular :class:`BaseResponse` object, even
+        if you are using a custom subclass.
+
+        This method can enforce a given response type, and it will also
+        convert arbitrary WSGI callables into response objects if an environ
+        is provided::
+
+            # convert a Werkzeug response object into an instance of the
+            # MyResponseClass subclass.
+            response = MyResponseClass.force_type(response)
+
+            # convert any WSGI application into a response object
+            response = MyResponseClass.force_type(response, environ)
+
+        This is especially useful if you want to post-process responses in
+        the main dispatcher and use functionality provided by your subclass.
+
+        Keep in mind that this will modify response objects in place if
+        possible!
+
+        :param response: a response object or wsgi application.
+        :param environ: a WSGI environment object.
+        :return: a response object.
+        """
+        if not isinstance(response, BaseResponse):
+            if environ is None:
+                raise TypeError('cannot convert WSGI application into '
+                                'response objects without an environ')
+            response = BaseResponse(*_run_wsgi_app(response, environ))
+        response.__class__ = cls
+        return response
+
+    @classmethod
+    def from_app(cls, app, environ, buffered=False):
+        """Create a new response object from an application output.  This
+        works best if you pass it an application that returns a generator all
+        the time.  Sometimes applications may use the `write()` callable
+        returned by the `start_response` function.  This tries to resolve such
+        edge cases automatically.  But if you don't get the expected output
+        you should set `buffered` to `True` which enforces buffering.
+
+        :param app: the WSGI application to execute.
+        :param environ: the WSGI environment to execute against.
+        :param buffered: set to `True` to enforce buffering.
+        :return: a response object.
+        """
+        return cls(*_run_wsgi_app(app, environ, buffered))
+
+    def _get_status_code(self):
+        try:
+            return int(self.status.split(None, 1)[0])
+        except ValueError:
+            return 0
+    def _set_status_code(self, code):
+        try:
+            self.status = '%d %s' % (code, HTTP_STATUS_CODES[code].upper())
+        except KeyError:
+            self.status = '%d UNKNOWN' % code
+    status_code = property(_get_status_code, _set_status_code,
+                           'The HTTP Status code as number')
+    del _get_status_code, _set_status_code
+
+    def _get_data(self):
+        """The string representation of the request body.  Whenever you access
+        this property the request iterable is encoded and flattened.  This
+        can lead to unwanted behavior if you stream big data.
+
+        This behavior can be disabled by setting
+        :attr:`implicit_sequence_conversion` to `False`.
+        """
+        self._ensure_sequence()
+        return ''.join(self.iter_encoded())
+    def _set_data(self, value):
+        # if an unicode string is set, it's encoded directly.  this allows
+        # us to guess the content length automatically in `get_wsgi_headers`.
+        if isinstance(value, unicode):
+            value = value.encode(self.charset)
+        self.response = [value]
+    data = property(_get_data, _set_data, doc=_get_data.__doc__)
+    del _get_data, _set_data
+
+    def _ensure_sequence(self, mutable=False):
+        """This method can be called by methods that need a sequence.  If
+        `mutable` is true, it will also ensure that the response sequence
+        is a standard Python list.
+
+        .. versionadded:: 0.6
+        """
+        if self.is_sequence:
+            # if we need a mutable object, we ensure it's a list.
+            if mutable and not isinstance(self.response, list):
+                self.response = list(self.response)
+            return
+        if not self.implicit_sequence_conversion:
+            raise RuntimeError('The response object required the iterable '
+                               'to be a sequence, but the implicit '
+                               'conversion was disabled.  Call '
+                               'make_sequence() yourself.')
+        self.make_sequence()
+
+    def make_sequence(self):
+        """Converts the response iterator in a list.  By default this happens
+        automatically if required.  If `implicit_sequence_conversion` is
+        disabled, this method is not automatically called and some properties
+        might raise exceptions.  This also encodes all the items.
+
+        .. versionadded:: 0.6
+        """
+        if not self.is_sequence:
+            # if we consume an iterable we have to ensure that the close
+            # method of the iterable is called if available when we tear
+            # down the response
+            close = getattr(self.response, 'close', None)
+            self.response = list(self.iter_encoded())
+            if close is not None:
+                self.call_on_close(close)
+
+    def iter_encoded(self, charset=None):
+        """Iter the response encoded with the encoding of the response.
+        If the response object is invoked as WSGI application the return
+        value of this method is used as application iterator unless
+        :attr:`direct_passthrough` was activated.
+
+        .. versionchanged:: 0.6
+
+           The `charset` parameter was deprecated and became a no-op.
+        """
+        # XXX: deprecated
+        if __debug__ and charset is not None: # pragma: no cover
+            from warnings import warn
+            warn(DeprecationWarning('charset was deprecated and is ignored.'),
+                 stacklevel=2)
+        charset = self.charset
+        if __debug__:
+            _warn_if_string(self.response)
+        for item in self.response:
+            if isinstance(item, unicode):
+                yield item.encode(charset)
+            else:
+                yield str(item)
+
+    def set_cookie(self, key, value='', max_age=None, expires=None,
+                   path='/', domain=None, secure=None, httponly=False):
+        """Sets a cookie. The parameters are the same as in the cookie `Morsel`
+        object in the Python standard library but it accepts unicode data, too.
+
+        :param key: the key (name) of the cookie to be set.
+        :param value: the value of the cookie.
+        :param max_age: should be a number of seconds, or `None` (default) if
+                        the cookie should last only as long as the client's
+                        browser session.
+        :param expires: should be a `datetime` object or UNIX timestamp.
+        :param domain: if you want to set a cross-domain cookie.  For example,
+                       ``domain=".example.com"`` will set a cookie that is
+                       readable by the domain ``www.example.com``,
+                       ``foo.example.com`` etc.  Otherwise, a cookie will only
+                       be readable by the domain that set it.
+        :param path: limits the cookie to a given path, per default it will
+                     span the whole domain.
+        """
+        self.headers.add('Set-Cookie', dump_cookie(key, value, max_age,
+                         expires, path, domain, secure, httponly,
+                         self.charset))
+
+    def delete_cookie(self, key, path='/', domain=None):
+        """Delete a cookie.  Fails silently if key doesn't exist.
+
+        :param key: the key (name) of the cookie to be deleted.
+        :param path: if the cookie that should be deleted was limited to a
+                     path, the path has to be defined here.
+        :param domain: if the cookie that should be deleted was limited to a
+                       domain, that domain has to be defined here.
+        """
+        self.set_cookie(key, expires=0, max_age=0, path=path, domain=domain)
+
+    @property
+    def header_list(self): # pragma: no cover
+        # XXX: deprecated
+        if __debug__:
+            from warnings import warn
+            warn(DeprecationWarning('header_list is deprecated'),
+                 stacklevel=2)
+        return self.headers.to_list(self.charset)
+
+    @property
+    def is_streamed(self):
+        """If the response is streamed (the response is not an iterable with
+        a length information) this property is `True`.  In this case streamed
+        means that there is no information about the number of iterations.
+        This is usually `True` if a generator is passed to the response object.
+
+        This is useful for checking before applying some sort of post
+        filtering that should not take place for streamed responses.
+        """
+        try:
+            len(self.response)
+        except TypeError:
+            return True
+        return False
+
+    @property
+    def is_sequence(self):
+        """If the iterator is buffered, this property will be `True`.  A
+        response object will consider an iterator to be buffered if the
+        response attribute is a list or tuple.
+
+        .. versionadded:: 0.6
+        """
+        return isinstance(self.response, (tuple, list))
+
+    def close(self):
+        """Close the wrapped response if possible."""
+        if hasattr(self.response, 'close'):
+            self.response.close()
+        for func in self._on_close:
+            func()
+
+    def freeze(self):
+        """Call this method if you want to make your response object ready for
+        being pickled.  This buffers the generator if there is one.  It will
+        also set the `Content-Length` header to the length of the body.
+
+        .. versionchanged:: 0.6
+           The `Content-Length` header is now set.
+        """
+        # we explicitly set the length to a list of the *encoded* response
+        # iterator.  Even if the implicit sequence conversion is disabled.
+        self.response = list(self.iter_encoded())
+        self.headers['Content-Length'] = str(sum(map(len, self.response)))
+
+    def fix_headers(self, environ):
+        # XXX: deprecated
+        if __debug__:
+            from warnings import warn
+            warn(DeprecationWarning('called into deprecated fix_headers baseclass '
+                                    'method.  Use get_wsgi_headers instead.'),
+                 stacklevel=2)
+        self.headers[:] = self.get_wsgi_headers(environ)
+
+    def get_wsgi_headers(self, environ):
+        """This is automatically called right before the response is started
+        and returns headers modified for the given environment.  It returns a
+        copy of the headers from the response with some modifications applied
+        if necessary.
+
+        For example the location header (if present) is joined with the root
+        URL of the environment.  Also the content length is automatically set
+        to zero here for certain status codes.
+
+        .. versionchanged:: 0.6
+           Previously that function was called `fix_headers` and modified
+           the response object in place.  Also since 0.6, IRIs in location
+           and content-location headers are handled properly.
+
+           Also starting with 0.6, Werkzeug will attempt to set the content
+           length if it is able to figure it out on its own.  This is the
+           case if all the strings in the response iterable are already
+           encoded and the iterable is buffered.
+
+        :param environ: the WSGI environment of the request.
+        :return: returns a new :class:`Headers` object.
+        """
+        headers = Headers(self.headers)
+
+        # make sure the location header is an absolute URL
+        location = headers.get('location')
+        if location is not None:
+            if isinstance(location, unicode):
+                location = iri_to_uri(location)
+            headers['Location'] = urlparse.urljoin(
+                get_current_url(environ, root_only=True),
+                location
+            )
+
+        # make sure the content location is a URL
+        content_location = headers.get('content-location')
+        if content_location is not None and \
+           isinstance(content_location, unicode):
+            headers['Content-Location'] = iri_to_uri(content_location)
+
+        if 100 <= self.status_code < 200 or self.status_code == 204:
+            headers['Content-Length'] = '0'
+        elif self.status_code == 304:
+            remove_entity_headers(headers)
+
+        # if we can determine the content length automatically, we
+        # should try to do that.  But only if this does not involve
+        # flattening the iterator or encoding of unicode strings in
+        # the response.
+        if self.is_sequence and 'content-length' not in self.headers:
+            try:
+                content_length = sum(len(str(x)) for x in self.response)
+            except UnicodeError:
+                # aha, something non-bytestringy in there, too bad, we
+                # can't safely figure out the length of the response.
+                pass
+            else:
+                headers['Content-Length'] = str(content_length)
+
+        return headers
+
+    def get_app_iter(self, environ):
+        """Returns the application iterator for the given environ.  Depending
+        on the request method and the current status code the return value
+        might be an empty response rather than the one from the response.
+
+        If the request method is `HEAD` or the status code is in a range
+        where the HTTP specification requires an empty response, an empty
+        iterable is returned.
+
+        .. versionadded:: 0.6
+
+        :param environ: the WSGI environment of the request.
+        :return: a response iterable.
+        """
+        if environ['REQUEST_METHOD'] == 'HEAD' or \
+           100 <= self.status_code < 200 or self.status_code in (204, 304):
+            return ()
+        if self.direct_passthrough:
+            if __debug__:
+                _warn_if_string(self.response)
+            return self.response
+        return ClosingIterator(self.iter_encoded(), self.close)
+
+    def get_wsgi_response(self, environ):
+        """Returns the final WSGI response as tuple.  The first item in
+        the tuple is the application iterator, the second the status and
+        the third the list of headers.  The response returned is created
+        specially for the given environment.  For example if the request
+        method in the WSGI environment is ``'HEAD'`` the response will
+        be empty and only the headers and status code will be present.
+
+        .. versionadded:: 0.6
+
+        :param environ: the WSGI environment of the request.
+        :return: an ``(app_iter, status, headers)`` tuple.
+        """
+        # XXX: code for backwards compatibility with custom fix_headers
+        # methods.
+        if self.fix_headers.func_code is not \
+           BaseResponse.fix_headers.func_code:
+            if __debug__:
+                from warnings import warn
+                warn(DeprecationWarning('fix_headers changed behavior in 0.6 '
+                                        'and is now called get_wsgi_headers. '
+                                        'See documentation for more details.'),
+                     stacklevel=2)
+            self.fix_headers(environ)
+            headers = self.headers
+        else:
+            headers = self.get_wsgi_headers(environ)
+        app_iter = self.get_app_iter(environ)
+        return app_iter, self.status, headers.to_list(self.charset)
+
+    def __call__(self, environ, start_response):
+        """Process this response as WSGI application.
+
+        :param environ: the WSGI environment.
+        :param start_response: the response callable provided by the WSGI
+                               server.
+        :return: an application iterator
+        """
+        app_iter, status, headers = self.get_wsgi_response(environ)
+        start_response(status, headers)
+        return app_iter
+
+
+class AcceptMixin(object):
+    """A mixin for classes with an :attr:`~BaseResponse.environ` attribute to
+    get all the HTTP accept headers as :class:`Accept` objects (or subclasses
+    thereof).
+    """
+
+    @cached_property
+    def accept_mimetypes(self):
+        """List of mimetypes this client supports as :class:`MIMEAccept`
+        object.
+        """
+        return parse_accept_header(self.environ.get('HTTP_ACCEPT'), MIMEAccept)
+
+    @cached_property
+    def accept_charsets(self):
+        """List of charsets this client supports as :class:`CharsetAccept`
+        object.
+        """
+        return parse_accept_header(self.environ.get('HTTP_ACCEPT_CHARSET'),
+                                   CharsetAccept)
+
+    @cached_property
+    def accept_encodings(self):
+        """List of encodings this client accepts.  Encodings in a HTTP term
+        are compression encodings such as gzip.  For charsets have a look at
+        :attr:`accept_charset`.
+        """
+        return parse_accept_header(self.environ.get('HTTP_ACCEPT_ENCODING'))
+
+    @cached_property
+    def accept_languages(self):
+        """List of languages this client accepts as :class:`LanguageAccept`
+        object.
+
+        .. versionchanged 0.5
+           In previous versions this was a regular :class:`Accept` object.
+        """
+        return parse_accept_header(self.environ.get('HTTP_ACCEPT_LANGUAGE'),
+                                   LanguageAccept)
+
+
+class ETagRequestMixin(object):
+    """Add entity tag and cache descriptors to a request object or object with
+    a WSGI environment available as :attr:`~BaseRequest.environ`.  This not
+    only provides access to etags but also to the cache control header.
+    """
+
+    @cached_property
+    def cache_control(self):
+        """A :class:`RequestCacheControl` object for the incoming cache control
+        headers.
+        """
+        cache_control = self.environ.get('HTTP_CACHE_CONTROL')
+        return parse_cache_control_header(cache_control, None,
+                                          RequestCacheControl)
+
+    @cached_property
+    def if_match(self):
+        """An object containing all the etags in the `If-Match` header."""
+        return parse_etags(self.environ.get('HTTP_IF_MATCH'))
+
+    @cached_property
+    def if_none_match(self):
+        """An object containing all the etags in the `If-None-Match` header."""
+        return parse_etags(self.environ.get('HTTP_IF_NONE_MATCH'))
+
+    @cached_property
+    def if_modified_since(self):
+        """The parsed `If-Modified-Since` header as datetime object."""
+        return parse_date(self.environ.get('HTTP_IF_MODIFIED_SINCE'))
+
+    @cached_property
+    def if_unmodified_since(self):
+        """The parsed `If-Unmodified-Since` header as datetime object."""
+        return parse_date(self.environ.get('HTTP_IF_UNMODIFIED_SINCE'))
+
+
+class UserAgentMixin(object):
+    """Adds a `user_agent` attribute to the request object which contains the
+    parsed user agent of the browser that triggered the request as `UserAgent`
+    object.
+    """
+
+    @cached_property
+    def user_agent(self):
+        """The current user agent."""
+        from werkzeug.useragents import UserAgent
+        return UserAgent(self.environ)
+
+
+class AuthorizationMixin(object):
+    """Adds an :attr:`authorization` property that represents the parsed value
+    of the `Authorization` header as :class:`Authorization` object.
+    """
+
+    @cached_property
+    def authorization(self):
+        """The `Authorization` object in parsed form."""
+        header = self.environ.get('HTTP_AUTHORIZATION')
+        return parse_authorization_header(header)
+
+
+class ETagResponseMixin(object):
+    """Adds extra functionality to a response object for etag and cache
+    handling.  This mixin requires an object with at least a `headers`
+    object that implements a dict like interface similar to :class:`Headers`.
+
+    If you want the :meth:`freeze` method to automatically add an etag, you
+    have to mixin this method before the response base class.  The default
+    response class does not do that.
+    """
+
+    @property
+    def cache_control(self):
+        """The Cache-Control general-header field is used to specify
+        directives that MUST be obeyed by all caching mechanisms along the
+        request/response chain.
+        """
+        def on_update(cache_control):
+            if not cache_control and 'cache-control' in self.headers:
+                del self.headers['cache-control']
+            elif cache_control:
+                self.headers['Cache-Control'] = cache_control.to_header()
+        return parse_cache_control_header(self.headers.get('cache-control'),
+                                          on_update,
+                                          ResponseCacheControl)
+
+    def make_conditional(self, request_or_environ):
+        """Make the response conditional to the request.  This method works
+        best if an etag was defined for the response already.  The `add_etag`
+        method can be used to do that.  If called without etag just the date
+        header is set.
+
+        This does nothing if the request method in the request or environ is
+        anything but GET or HEAD.
+
+        It does not remove the body of the response because that's something
+        the :meth:`__call__` function does for us automatically.
+
+        Returns self so that you can do ``return resp.make_conditional(req)``
+        but modifies the object in-place.
+
+        :param request_or_environ: a request object or WSGI environment to be
+                                   used to make the response conditional
+                                   against.
+        """
+        environ = _get_environ(request_or_environ)
+        if environ['REQUEST_METHOD'] in ('GET', 'HEAD'):
+            self.headers['Date'] = http_date()
+            if 'content-length' in self.headers:
+                self.headers['Content-Length'] = len(self.data)
+            if not is_resource_modified(environ, self.headers.get('etag'), None,
+                                        self.headers.get('last-modified')):
+                self.status_code = 304
+        return self
+
+    def add_etag(self, overwrite=False, weak=False):
+        """Add an etag for the current response if there is none yet."""
+        if overwrite or 'etag' not in self.headers:
+            self.set_etag(generate_etag(self.data), weak)
+
+    def set_etag(self, etag, weak=False):
+        """Set the etag, and override the old one if there was one."""
+        self.headers['ETag'] = quote_etag(etag, weak)
+
+    def get_etag(self):
+        """Return a tuple in the form ``(etag, is_weak)``.  If there is no
+        ETag the return value is ``(None, None)``.
+        """
+        return unquote_etag(self.headers.get('ETag'))
+
+    def freeze(self, no_etag=False):
+        """Call this method if you want to make your response object ready for
+        pickeling.  This buffers the generator if there is one.  This also
+        sets the etag unless `no_etag` is set to `True`.
+        """
+        if not no_etag:
+            self.add_etag()
+        super(ETagResponseMixin, self).freeze()
+
+
+class ResponseStream(object):
+    """A file descriptor like object used by the :class:`ResponseStreamMixin` to
+    represent the body of the stream.  It directly pushes into the response
+    iterable of the response object.
+    """
+
+    mode = 'wb+'
+
+    def __init__(self, response):
+        self.response = response
+        self.closed = False
+
+    def write(self, value):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        self.response._ensure_sequence(mutable=True)
+        self.response.response.append(value)
+
+    def writelines(self, seq):
+        for item in seq:
+            self.write(item)
+
+    def close(self):
+        self.closed = True
+
+    def flush(self):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+
+    def isatty(self):
+        if self.closed:
+            raise ValueError('I/O operation on closed file')
+        return False
+
+    @property
+    def encoding(self):
+        return self.response.charset
+
+
+class ResponseStreamMixin(object):
+    """Mixin for :class:`BaseRequest` subclasses.  Classes that inherit from
+    this mixin will automatically get a :attr:`stream` property that provides
+    a write-only interface to the response iterable.
+    """
+
+    @cached_property
+    def stream(self):
+        """The response iterable as write-only stream."""
+        return ResponseStream(self)
+
+
+class CommonRequestDescriptorsMixin(object):
+    """A mixin for :class:`BaseRequest` subclasses.  Request objects that
+    mix this class in will automatically get descriptors for a couple of
+    HTTP headers with automatic type conversion.
+
+    .. versionadded:: 0.5
+    """
+
+    content_type = environ_property('CONTENT_TYPE', doc='''
+         The Content-Type entity-header field indicates the media type of
+         the entity-body sent to the recipient or, in the case of the HEAD
+         method, the media type that would have been sent had the request
+         been a GET.''')
+    content_length = environ_property('CONTENT_LENGTH', None, int, str, doc='''
+         The Content-Length entity-header field indicates the size of the
+         entity-body in bytes or, in the case of the HEAD method, the size of
+         the entity-body that would have been sent had the request been a
+         GET.''')
+    referrer = environ_property('HTTP_REFERER', doc='''
+        The Referer[sic] request-header field allows the client to specify,
+        for the server's benefit, the address (URI) of the resource from which
+        the Request-URI was obtained (the "referrer", although the header
+        field is misspelled).''')
+    date = environ_property('HTTP_DATE', None, parse_date, doc='''
+        The Date general-header field represents the date and time at which
+        the message was originated, having the same semantics as orig-date
+        in RFC 822.''')
+    max_forwards = environ_property('HTTP_MAX_FORWARDS', None, int, doc='''
+         The Max-Forwards request-header field provides a mechanism with the
+         TRACE and OPTIONS methods to limit the number of proxies or gateways
+         that can forward the request to the next inbound server.''')
+
+    def _parse_content_type(self):
+        if not hasattr(self, '_parsed_content_type'):
+            self._parsed_content_type = \
+                parse_options_header(self.environ.get('CONTENT_TYPE', ''))
+
+    @property
+    def mimetype(self):
+        """Like :attr:`content_type` but without parameters (eg, without
+        charset, type etc.).  For example if the content
+        type is ``text/html; charset=utf-8`` the mimetype would be
+        ``'text/html'``.
+        """
+        self._parse_content_type()
+        return self._parsed_content_type[0]
+
+    @property
+    def mimetype_params(self):
+        """The mimetype parameters as dict.  For example if the content
+        type is ``text/html; charset=utf-8`` the params would be
+        ``{'charset': 'utf-8'}``.
+        """
+        self._parse_content_type()
+        return self._parsed_content_type[1]
+
+    @cached_property
+    def pragma(self):
+        """The Pragma general-header field is used to include
+        implementation-specific directives that might apply to any recipient
+        along the request/response chain.  All pragma directives specify
+        optional behavior from the viewpoint of the protocol; however, some
+        systems MAY require that behavior be consistent with the directives.
+        """
+        return parse_set_header(self.environ.get('HTTP_PRAGMA', ''))
+
+
+class CommonResponseDescriptorsMixin(object):
+    """A mixin for :class:`BaseResponse` subclasses.  Response objects that
+    mix this class in will automatically get descriptors for a couple of
+    HTTP headers with automatic type conversion.
+    """
+
+    def _get_mimetype(self):
+        ct = self.headers.get('content-type')
+        if ct:
+            return ct.split(';')[0].strip()
+
+    def _set_mimetype(self, value):
+        self.headers['Content-Type'] = get_content_type(value, self.charset)
+
+    def _get_mimetype_params(self):
+        def on_update(d):
+            self.headers['Content-Type'] = \
+                dump_options_header(self.mimetype, d)
+        d = parse_options_header(self.headers.get('content-type', ''))[1]
+        return CallbackDict(d, on_update)
+
+    mimetype = property(_get_mimetype, _set_mimetype, doc='''
+        The mimetype (content type without charset etc.)''')
+    mimetype_params = property(_get_mimetype_params, doc='''
+        The mimetype parameters as dict.  For example if the content
+        type is ``text/html; charset=utf-8`` the params would be
+        ``{'charset': 'utf-8'}``.
+
+        .. versionadded:: 0.5
+        ''')
+    location = header_property('Location', doc='''
+        The Location response-header field is used to redirect the recipient
+        to a location other than the Request-URI for completion of the request
+        or identification of a new resource.''')
+    age = header_property('Age', None, parse_date, http_date, doc='''
+        The Age response-header field conveys the sender's estimate of the
+        amount of time since the response (or its revalidation) was
+        generated at the origin server.
+
+        Age values are non-negative decimal integers, representing time in
+        seconds.''')
+    content_type = header_property('Content-Type', doc='''
+        The Content-Type entity-header field indicates the media type of the
+        entity-body sent to the recipient or, in the case of the HEAD method,
+        the media type that would have been sent had the request been a GET.
+    ''')
+    content_length = header_property('Content-Length', None, int, str, doc='''
+        The Content-Length entity-header field indicates the size of the
+        entity-body, in decimal number of OCTETs, sent to the recipient or,
+        in the case of the HEAD method, the size of the entity-body that would
+        have been sent had the request been a GET.''')
+    content_location = header_property('Content-Location', doc='''
+        The Content-Location entity-header field MAY be used to supply the
+        resource location for the entity enclosed in the message when that
+        entity is accessible from a location separate from the requested
+        resource's URI.''')
+    content_encoding = header_property('Content-Encoding', doc='''
+        The Content-Encoding entity-header field is used as a modifier to the
+        media-type.  When present, its value indicates what additional content
+        codings have been applied to the entity-body, and thus what decoding
+        mechanisms must be applied in order to obtain the media-type
+        referenced by the Content-Type header field.''')
+    content_md5 = header_property('Content-MD5', doc='''
+         The Content-MD5 entity-header field, as defined in RFC 1864, is an
+         MD5 digest of the entity-body for the purpose of providing an
+         end-to-end message integrity check (MIC) of the entity-body.  (Note:
+         a MIC is good for detecting accidental modification of the
+         entity-body in transit, but is not proof against malicious attacks.)
+        ''')
+    date = header_property('Date', None, parse_date, http_date, doc='''
+        The Date general-header field represents the date and time at which
+        the message was originated, having the same semantics as orig-date
+        in RFC 822.''')
+    expires = header_property('Expires', None, parse_date, http_date, doc='''
+        The Expires entity-header field gives the date/time after which the
+        response is considered stale. A stale cache entry may not normally be
+        returned by a cache.''')
+    last_modified = header_property('Last-Modified', None, parse_date,
+                                    http_date, doc='''
+        The Last-Modified entity-header field indicates the date and time at
+        which the origin server believes the variant was last modified.''')
+
+    def _get_retry_after(self):
+        value = self.headers.get('retry-after')
+        if value is None:
+            return
+        elif value.isdigit():
+            return datetime.utcnow() + timedelta(seconds=int(value))
+        return parse_date(value)
+    def _set_retry_after(self, value):
+        if value is None:
+            if 'retry-after' in self.headers:
+                del self.headers['retry-after']
+            return
+        elif isinstance(value, datetime):
+            value = http_date(value)
+        else:
+            value = str(value)
+        self.headers['Retry-After'] = value
+
+    retry_after = property(_get_retry_after, _set_retry_after, doc='''
+        The Retry-After response-header field can be used with a 503 (Service
+        Unavailable) response to indicate how long the service is expected
+        to be unavailable to the requesting client.
+
+        Time in seconds until expiration or date.''')
+
+    def _set_property(name, doc=None):
+        def fget(self):
+            def on_update(header_set):
+                if not header_set and name in self.headers:
+                    del self.headers[name]
+                elif header_set:
+                    self.headers[name] = header_set.to_header()
+            return parse_set_header(self.headers.get(name), on_update)
+        return property(fget, doc=doc)
+
+    vary = _set_property('Vary', doc='''
+         The Vary field value indicates the set of request-header fields that
+         fully determines, while the response is fresh, whether a cache is
+         permitted to use the response to reply to a subsequent request
+         without revalidation.''')
+    content_language = _set_property('Content-Language', doc='''
+         The Content-Language entity-header field describes the natural
+         language(s) of the intended audience for the enclosed entity.  Note
+         that this might not be equivalent to all the languages used within
+         the entity-body.''')
+    allow = _set_property('Allow', doc='''
+        The Allow entity-header field lists the set of methods supported
+        by the resource identified by the Request-URI. The purpose of this
+        field is strictly to inform the recipient of valid methods
+        associated with the resource. An Allow header field MUST be
+        present in a 405 (Method Not Allowed) response.''')
+
+    del _set_property, _get_mimetype, _set_mimetype, _get_retry_after, \
+        _set_retry_after
+
+
+class WWWAuthenticateMixin(object):
+    """Adds a :attr:`www_authenticate` property to a response object."""
+
+    @property
+    def www_authenticate(self):
+        """The `WWW-Authenticate` header in a parsed form."""
+        def on_update(www_auth):
+            if not www_auth and 'www-authenticate' in self.headers:
+                del self.headers['www-authenticate']
+            elif www_auth:
+                self.headers['WWW-Authenticate'] = www_auth.to_header()
+        header = self.headers.get('www-authenticate')
+        return parse_www_authenticate_header(header, on_update)
+
+
+class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
+              UserAgentMixin, AuthorizationMixin,
+              CommonRequestDescriptorsMixin):
+    """Full featured request object implementing the following mixins:
+
+    - :class:`AcceptMixin` for accept header parsing
+    - :class:`ETagRequestMixin` for etag and cache control handling
+    - :class:`UserAgentMixin` for user agent introspection
+    - :class:`AuthorizationMixin` for http auth handling
+    - :class:`CommonRequestDescriptorsMixin` for common headers
+    """
+
+
+class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,
+               CommonResponseDescriptorsMixin,
+               WWWAuthenticateMixin):
+    """Full featured response object implementing the following mixins:
+
+    - :class:`ETagResponseMixin` for etag and cache control handling
+    - :class:`ResponseStreamMixin` to add support for the `stream` property
+    - :class:`CommonResponseDescriptorsMixin` for various HTTP descriptors
+    - :class:`WWWAuthenticateMixin` for HTTP authentication support
+    """
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/werkzeug/werkzeug/wsgi.py	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,763 @@
+# -*- coding: utf-8 -*-
+"""
+    werkzeug.wsgi
+    ~~~~~~~~~~~~~
+
+    This module implements WSGI related helpers.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import urllib
+import urlparse
+import posixpath
+import mimetypes
+from zlib import adler32
+from time import time, mktime
+from datetime import datetime
+
+from werkzeug._internal import _patch_wrapper
+from werkzeug.utils import http_date
+from werkzeug.http import is_resource_modified
+
+
+def responder(f):
+    """Marks a function as responder.  Decorate a function with it and it
+    will automatically call the return value as WSGI application.
+
+    Example::
+
+        @responder
+        def application(environ, start_response):
+            return Response('Hello World!')
+    """
+    return _patch_wrapper(f, lambda *a: f(*a)(*a[-2:]))
+
+
+def get_current_url(environ, root_only=False, strip_querystring=False,
+                    host_only=False):
+    """A handy helper function that recreates the full URL for the current
+    request or parts of it.  Here an example:
+
+    >>> from werkzeug import create_environ
+    >>> env = create_environ("/?param=foo", "http://localhost/script")
+    >>> get_current_url(env)
+    'http://localhost/script/?param=foo'
+    >>> get_current_url(env, root_only=True)
+    'http://localhost/script/'
+    >>> get_current_url(env, host_only=True)
+    'http://localhost/'
+    >>> get_current_url(env, strip_querystring=True)
+    'http://localhost/script/'
+
+    :param environ: the WSGI environment to get the current URL from.
+    :param root_only: set `True` if you only want the root URL.
+    :param strip_querystring: set to `True` if you don't want the querystring.
+    :param host_only: set to `True` if the host URL should be returned.
+    """
+    tmp = [environ['wsgi.url_scheme'], '://', get_host(environ)]
+    cat = tmp.append
+    if host_only:
+        return ''.join(tmp) + '/'
+    cat(urllib.quote(environ.get('SCRIPT_NAME', '').rstrip('/')))
+    if root_only:
+        cat('/')
+    else:
+        cat(urllib.quote('/' + environ.get('PATH_INFO', '').lstrip('/')))
+        if not strip_querystring:
+            qs = environ.get('QUERY_STRING')
+            if qs:
+                cat('?' + qs)
+    return ''.join(tmp)
+
+
+def get_host(environ):
+    """Return the real host for the given WSGI environment.  This takes care
+    of the `X-Forwarded-Host` header.
+
+    :param environ: the WSGI environment to get the host of.
+    """
+    if 'HTTP_X_FORWARDED_HOST' in environ:
+        return environ['HTTP_X_FORWARDED_HOST']
+    elif 'HTTP_HOST' in environ:
+        return environ['HTTP_HOST']
+    result = environ['SERVER_NAME']
+    if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \
+       in (('https', '443'), ('http', '80')):
+        result += ':' + environ['SERVER_PORT']
+    return result
+
+
+def pop_path_info(environ):
+    """Removes and returns the next segment of `PATH_INFO`, pushing it onto
+    `SCRIPT_NAME`.  Returns `None` if there is nothing left on `PATH_INFO`.
+
+    If there are empty segments (``'/foo//bar``) these are ignored but
+    properly pushed to the `SCRIPT_NAME`:
+
+    >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'}
+    >>> pop_path_info(env)
+    'a'
+    >>> env['SCRIPT_NAME']
+    '/foo/a'
+    >>> pop_path_info(env)
+    'b'
+    >>> env['SCRIPT_NAME']
+    '/foo/a/b'
+
+    .. versionadded:: 0.5
+
+    :param environ: the WSGI environment that is modified.
+    """
+    path = environ.get('PATH_INFO')
+    if not path:
+        return None
+
+    script_name = environ.get('SCRIPT_NAME', '')
+
+    # shift multiple leading slashes over
+    old_path = path
+    path = path.lstrip('/')
+    if path != old_path:
+        script_name += '/' * (len(old_path) - len(path))
+
+    if '/' not in path:
+        environ['PATH_INFO'] = ''
+        environ['SCRIPT_NAME'] = script_name + path
+        return path
+
+    segment, path = path.split('/', 1)
+    environ['PATH_INFO'] = '/' + path
+    environ['SCRIPT_NAME'] = script_name + segment
+    return segment
+
+
+def peek_path_info(environ):
+    """Returns the next segment on the `PATH_INFO` or `None` if there
+    is none.  Works like :func:`pop_path_info` without modifying the
+    environment:
+
+    >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'}
+    >>> peek_path_info(env)
+    'a'
+    >>> peek_path_info(env)
+    'a'
+
+    .. versionadded:: 0.5
+
+    :param environ: the WSGI environment that is checked.
+    """
+    segments = environ.get('PATH_INFO', '').lstrip('/').split('/', 1)
+    if segments:
+        return segments[0]
+
+
+def extract_path_info(environ_or_baseurl, path_or_url, charset='utf-8',
+                      errors='ignore', collapse_http_schemes=True):
+    """Extracts the path info from the given URL (or WSGI environment) and
+    path.  The path info returned is a unicode string, not a bytestring
+    suitable for a WSGI environment.  The URLs might also be IRIs.
+
+    If the path info could not be determined, `None` is returned.
+
+    Some examples:
+
+    >>> extract_path_info('http://example.com/app', '/app/hello')
+    u'/hello'
+    >>> extract_path_info('http://example.com/app',
+    ...                   'https://example.com/app/hello')
+    u'/hello'
+    >>> extract_path_info('http://example.com/app',
+    ...                   'https://example.com/app/hello',
+    ...                   collapse_http_schemes=False) is None
+    True
+
+    Instead of providing a base URL you can also pass a WSGI environment.
+
+    .. versionadded:: 0.6
+
+    :param environ_or_baseurl: a WSGI environment dict, a base URL or
+                               base IRI.  This is the root of the
+                               application.
+    :param path_or_url: an absolute path from the server root, a
+                        relative path (in which case it's the path info)
+                        or a full URL.  Also accepts IRIs and unicode
+                        parameters.
+    :param charset: the charset for byte data in URLs
+    :param errors: the error handling on decode
+    :param collapse_http_schemes: if set to `False` the algorithm does
+                                  not assume that http and https on the
+                                  same server point to the same
+                                  resource.
+    """
+    from werkzeug.urls import uri_to_iri, url_fix
+
+    def _as_iri(obj):
+        if not isinstance(obj, unicode):
+            return uri_to_iri(obj, charset, errors)
+        return obj
+
+    def _normalize_netloc(scheme, netloc):
+        parts = netloc.split(u'@', 1)[-1].split(u':', 1)
+        if len(parts) == 2:
+            netloc, port = parts
+            if (scheme == u'http' and port == u'80') or \
+               (scheme == u'https' and port == u'443'):
+                port = None
+        else:
+            netloc = parts[0]
+            port = None
+        if port is not None:
+            netloc += u':' + port
+        return netloc
+
+    # make sure whatever we are working on is a IRI and parse it
+    path = _as_iri(path_or_url)
+    if isinstance(environ_or_baseurl, dict):
+        environ_or_baseurl = get_current_url(environ_or_baseurl,
+                                             root_only=True)
+    base_iri = _as_iri(environ_or_baseurl)
+    base_scheme, base_netloc, base_path, = \
+        urlparse.urlsplit(base_iri)[:3]
+    cur_scheme, cur_netloc, cur_path, = \
+        urlparse.urlsplit(urlparse.urljoin(base_iri, path))[:3]
+
+    # normalize the network location
+    base_netloc = _normalize_netloc(base_scheme, base_netloc)
+    cur_netloc = _normalize_netloc(cur_scheme, cur_netloc)
+
+    # is that IRI even on a known HTTP scheme?
+    if collapse_http_schemes:
+        for scheme in base_scheme, cur_scheme:
+            if scheme not in (u'http', u'https'):
+                return None
+    else:
+        if not (base_scheme in (u'http', u'https') and \
+                base_scheme == cur_scheme):
+            return None
+
+    # are the netlocs compatible?
+    if base_netloc != cur_netloc:
+        return None
+
+    # are we below the application path?
+    base_path = base_path.rstrip(u'/')
+    if not cur_path.startswith(base_path):
+        return None
+
+    return u'/' + cur_path[len(base_path):].lstrip(u'/')
+
+
+class SharedDataMiddleware(object):
+    """A WSGI middleware that provides static content for development
+    environments or simple server setups. Usage is quite simple::
+
+        import os
+        from werkzeug import SharedDataMiddleware
+
+        app = SharedDataMiddleware(app, {
+            '/shared': os.path.join(os.path.dirname(__file__), 'shared')
+        })
+
+    The contents of the folder ``./shared`` will now be available on
+    ``http://example.com/shared/``.  This is pretty useful during development
+    because a standalone media server is not required.  One can also mount
+    files on the root folder and still continue to use the application because
+    the shared data middleware forwards all unhandled requests to the
+    application, even if the requests are below one of the shared folders.
+
+    If `pkg_resources` is available you can also tell the middleware to serve
+    files from package data::
+
+        app = SharedDataMiddleware(app, {
+            '/shared': ('myapplication', 'shared_files')
+        })
+
+    This will then serve the ``shared_files`` folder in the `myapplication`
+    Python package.
+
+    The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch`
+    rules for files that are not accessible from the web.  If `cache` is set to
+    `False` no caching headers are sent.
+
+    Currently the middleware does not support non ASCII filenames.  If the
+    encoding on the file system happens to be the encoding of the URI it may
+    work but this could also be by accident.  We strongly suggest using ASCII
+    only file names for static files.
+
+    The middleware will guess the mimetype using the Python `mimetype`
+    module.  If it's unable to figure out the charset it will fall back
+    to `fallback_mimetype`.
+
+    .. versionchanged:: 0.5
+       The cache timeout is configurable now.
+
+    .. versionadded:: 0.6
+       The `fallback_mimetype` parameter was added.
+
+    :param app: the application to wrap.  If you don't want to wrap an
+                application you can pass it :exc:`NotFound`.
+    :param exports: a dict of exported files and folders.
+    :param diallow: a list of :func:`~fnmatch.fnmatch` rules.
+    :param fallback_mimetype: the fallback mimetype for unknown files.
+    :param cache: enable or disable caching headers.
+    :Param cache_timeout: the cache timeout in seconds for the headers.
+    """
+
+    def __init__(self, app, exports, disallow=None, cache=True,
+                 cache_timeout=60 * 60 * 12, fallback_mimetype='text/plain'):
+        self.app = app
+        self.exports = {}
+        self.cache = cache
+        self.cache_timeout = cache_timeout
+        for key, value in exports.iteritems():
+            if isinstance(value, tuple):
+                loader = self.get_package_loader(*value)
+            elif isinstance(value, basestring):
+                if os.path.isfile(value):
+                    loader = self.get_file_loader(value)
+                else:
+                    loader = self.get_directory_loader(value)
+            else:
+                raise TypeError('unknown def %r' % value)
+            self.exports[key] = loader
+        if disallow is not None:
+            from fnmatch import fnmatch
+            self.is_allowed = lambda x: not fnmatch(x, disallow)
+        self.fallback_mimetype = fallback_mimetype
+
+    def is_allowed(self, filename):
+        """Subclasses can override this method to disallow the access to
+        certain files.  However by providing `disallow` in the constructor
+        this method is overwritten.
+        """
+        return True
+
+    def _opener(self, filename):
+        return lambda: (
+            open(filename, 'rb'),
+            datetime.utcfromtimestamp(os.path.getmtime(filename)),
+            int(os.path.getsize(filename))
+        )
+
+    def get_file_loader(self, filename):
+        return lambda x: (os.path.basename(filename), self._opener(filename))
+
+    def get_package_loader(self, package, package_path):
+        from pkg_resources import DefaultProvider, ResourceManager, \
+             get_provider
+        loadtime = datetime.utcnow()
+        provider = get_provider(package)
+        manager = ResourceManager()
+        filesystem_bound = isinstance(provider, DefaultProvider)
+        def loader(path):
+            if path is None:
+                return None, None
+            path = posixpath.join(package_path, path)
+            if not provider.has_resource(path):
+                return None, None
+            basename = posixpath.basename(path)
+            if filesystem_bound:
+                return basename, self._opener(
+                    provider.get_resource_filename(manager, path))
+            return basename, lambda: (
+                provider.get_resource_stream(manager, path),
+                loadtime,
+                0
+            )
+        return loader
+
+    def get_directory_loader(self, directory):
+        def loader(path):
+            if path is not None:
+                path = os.path.join(directory, path)
+            else:
+                path = directory
+            if os.path.isfile(path):
+                return os.path.basename(path), self._opener(path)
+            return None, None
+        return loader
+
+    def generate_etag(self, mtime, file_size, real_filename):
+        return 'wzsdm-%d-%s-%s' % (
+            mktime(mtime.timetuple()),
+            file_size,
+            adler32(real_filename) & 0xffffffff
+        )
+
+    def __call__(self, environ, start_response):
+        # sanitize the path for non unix systems
+        cleaned_path = environ.get('PATH_INFO', '').strip('/')
+        for sep in os.sep, os.altsep:
+            if sep and sep != '/':
+                cleaned_path = cleaned_path.replace(sep, '/')
+        path = '/'.join([''] + [x for x in cleaned_path.split('/')
+                                if x and x != '..'])
+        file_loader = None
+        for search_path, loader in self.exports.iteritems():
+            if search_path == path:
+                real_filename, file_loader = loader(None)
+                if file_loader is not None:
+                    break
+            if not search_path.endswith('/'):
+                search_path += '/'
+            if path.startswith(search_path):
+                real_filename, file_loader = loader(path[len(search_path):])
+                if file_loader is not None:
+                    break
+        if file_loader is None or not self.is_allowed(real_filename):
+            return self.app(environ, start_response)
+
+        guessed_type = mimetypes.guess_type(real_filename)
+        mime_type = guessed_type[0] or self.fallback_mimetype
+        f, mtime, file_size = file_loader()
+
+        headers = [('Date', http_date())]
+        if self.cache:
+            timeout = self.cache_timeout
+            etag = self.generate_etag(mtime, file_size, real_filename)
+            headers += [
+                ('Etag', '"%s"' % etag),
+                ('Cache-Control', 'max-age=%d, public' % timeout)
+            ]
+            if not is_resource_modified(environ, etag, last_modified=mtime):
+                f.close()
+                start_response('304 Not Modified', headers)
+                return []
+            headers.append(('Expires', http_date(time() + timeout)))
+        else:
+            headers.append(('Cache-Control', 'public'))
+
+        headers.extend((
+            ('Content-Type', mime_type),
+            ('Content-Length', str(file_size)),
+            ('Last-Modified', http_date(mtime))
+        ))
+        start_response('200 OK', headers)
+        return wrap_file(environ, f)
+
+
+class DispatcherMiddleware(object):
+    """Allows one to mount middlewares or applications in a WSGI application.
+    This is useful if you want to combine multiple WSGI applications::
+
+        app = DispatcherMiddleware(app, {
+            '/app2':        app2,
+            '/app3':        app3
+        })
+    """
+
+    def __init__(self, app, mounts=None):
+        self.app = app
+        self.mounts = mounts or {}
+
+    def __call__(self, environ, start_response):
+        script = environ.get('PATH_INFO', '')
+        path_info = ''
+        while '/' in script:
+            if script in self.mounts:
+                app = self.mounts[script]
+                break
+            items = script.split('/')
+            script = '/'.join(items[:-1])
+            path_info = '/%s%s' % (items[-1], path_info)
+        else:
+            app = self.mounts.get(script, self.app)
+        original_script_name = environ.get('SCRIPT_NAME', '')
+        environ['SCRIPT_NAME'] = original_script_name + script
+        environ['PATH_INFO'] = path_info
+        return app(environ, start_response)
+
+
+class ClosingIterator(object):
+    """The WSGI specification requires that all middlewares and gateways
+    respect the `close` callback of an iterator.  Because it is useful to add
+    another close action to a returned iterator and adding a custom iterator
+    is a boring task this class can be used for that::
+
+        return ClosingIterator(app(environ, start_response), [cleanup_session,
+                                                              cleanup_locals])
+
+    If there is just one close function it can be passed instead of the list.
+
+    A closing iterator is not needed if the application uses response objects
+    and finishes the processing if the response is started::
+
+        try:
+            return response(environ, start_response)
+        finally:
+            cleanup_session()
+            cleanup_locals()
+    """
+
+    def __init__(self, iterable, callbacks=None):
+        iterator = iter(iterable)
+        self._next = iterator.next
+        if callbacks is None:
+            callbacks = []
+        elif callable(callbacks):
+            callbacks = [callbacks]
+        else:
+            callbacks = list(callbacks)
+        iterable_close = getattr(iterator, 'close', None)
+        if iterable_close:
+            callbacks.insert(0, iterable_close)
+        self._callbacks = callbacks
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        return self._next()
+
+    def close(self):
+        for callback in self._callbacks:
+            callback()
+
+
+def wrap_file(environ, file, buffer_size=8192):
+    """Wraps a file.  This uses the WSGI server's file wrapper if available
+    or otherwise the generic :class:`FileWrapper`.
+
+    .. versionadded:: 0.5
+
+    If the file wrapper from the WSGI server is used it's important to not
+    iterate over it from inside the application but to pass it through
+    unchanged.  If you want to pass out a file wrapper inside a response
+    object you have to set :attr:`~BaseResponse.direct_passthrough` to `True`.
+
+    More information about file wrappers are available in :pep:`333`.
+
+    :param file: a :class:`file`-like object with a :meth:`~file.read` method.
+    :param buffer_size: number of bytes for one iteration.
+    """
+    return environ.get('wsgi.file_wrapper', FileWrapper)(file, buffer_size)
+
+
+class FileWrapper(object):
+    """This class can be used to convert a :class:`file`-like object into
+    an iterable.  It yields `buffer_size` blocks until the file is fully
+    read.
+
+    You should not use this class directly but rather use the
+    :func:`wrap_file` function that uses the WSGI server's file wrapper
+    support if it's available.
+
+    .. versionadded:: 0.5
+
+    If you're using this object together with a :class:`BaseResponse` you have
+    to use the `direct_passthrough` mode.
+
+    :param file: a :class:`file`-like object with a :meth:`~file.read` method.
+    :param buffer_size: number of bytes for one iteration.
+    """
+
+    def __init__(self, file, buffer_size=8192):
+        self.file = file
+        self.buffer_size = buffer_size
+
+    def close(self):
+        if hasattr(self.file, 'close'):
+            self.file.close()
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        data = self.file.read(self.buffer_size)
+        if data:
+            return data
+        raise StopIteration()
+
+
+def make_line_iter(stream, limit=None, buffer_size=10 * 1024):
+    """Safely iterates line-based over an input stream.  If the input stream
+    is not a :class:`LimitedStream` the `limit` parameter is mandatory.
+
+    This uses the stream's :meth:`~file.read` method internally as opposite
+    to the :meth:`~file.readline` method that is unsafe and can only be used
+    in violation of the WSGI specification.  The same problem applies to the
+    `__iter__` function of the input stream which calls :meth:`~file.readline`
+    without arguments.
+
+    If you need line-by-line processing it's strongly recommended to iterate
+    over the input stream using this helper function.
+
+    :param stream: the stream to iterate over.
+    :param limit: the limit in bytes for the stream.  (Usually
+                  content length.  Not necessary if the `stream`
+                  is a :class:`LimitedStream`.
+    :param buffer_size: The optional buffer size.
+    """
+    if not isinstance(stream, LimitedStream):
+        if limit is None:
+            raise TypeError('stream not limited and no limit provided.')
+        stream = LimitedStream(stream, limit)
+    _read = stream.read
+    buffer = []
+    while 1:
+        if len(buffer) > 1:
+            yield buffer.pop()
+            continue
+
+        # we reverse the chunks because popping from the last
+        # position of the list is O(1) and the number of chunks
+        # read will be quite large for binary files.
+        chunks = _read(buffer_size).splitlines(True)
+        chunks.reverse()
+
+        first_chunk = buffer and buffer[0] or ''
+        if chunks:
+            first_chunk += chunks.pop()
+        if not first_chunk:
+            return
+
+        buffer = chunks
+        yield first_chunk
+
+
+class LimitedStream(object):
+    """Wraps a stream so that it doesn't read more than n bytes.  If the
+    stream is exhausted and the caller tries to get more bytes from it
+    :func:`on_exhausted` is called which by default returns an empty
+    string.  The return value of that function is forwarded
+    to the reader function.  So if it returns an empty string
+    :meth:`read` will return an empty string as well.
+
+    The limit however must never be higher than what the stream can
+    output.  Otherwise :meth:`readlines` will try to read past the
+    limit.
+
+    The `silent` parameter has no effect if :meth:`is_exhausted` is
+    overriden by a subclass.
+
+    .. versionchanged:: 0.6
+       Non-silent usage was deprecated because it causes confusion.
+       If you want that, override :meth:`is_exhausted` and raise a
+       :exc:`~exceptions.BadRequest` yourself.
+
+    .. admonition:: Note on WSGI compliance
+
+       calls to :meth:`readline` and :meth:`readlines` are not
+       WSGI compliant because it passes a size argument to the
+       readline methods.  Unfortunately the WSGI PEP is not safely
+       implementable without a size argument to :meth:`readline`
+       because there is no EOF marker in the stream.  As a result
+       of that the use of :meth:`readline` is discouraged.
+
+       For the same reason iterating over the :class:`LimitedStream`
+       is not portable.  It internally calls :meth:`readline`.
+
+       We strongly suggest using :meth:`read` only or using the
+       :func:`make_line_iter` which safely iterates line-based
+       over a WSGI input stream.
+
+    :param stream: the stream to wrap.
+    :param limit: the limit for the stream, must not be longer than
+                  what the string can provide if the stream does not
+                  end with `EOF` (like `wsgi.input`)
+    :param silent: If set to `True` the stream will allow reading
+                   past the limit and will return an empty string.
+    """
+
+    def __init__(self, stream, limit, silent=True):
+        self._read = stream.read
+        self._readline = stream.readline
+        self._pos = 0
+        self.limit = limit
+        self.silent = silent
+        if not silent:
+            from warnings import warn
+            warn(DeprecationWarning('non-silent usage of the '
+            'LimitedStream is deprecated.  If you want to '
+            'continue to use the stream in non-silent usage '
+            'override on_exhausted.'), stacklevel=2)
+
+    def __iter__(self):
+        return self
+
+    @property
+    def is_exhausted(self):
+        """If the stream is exhausted this attribute is `True`."""
+        return self._pos >= self.limit
+
+    def on_exhausted(self):
+        """This is called when the stream tries to read past the limit.
+        The return value of this function is returned from the reading
+        function.
+
+        Per default this raises a :exc:`~werkzeug.exceptions.BadRequest`.
+        """
+        if self.silent:
+            return ''
+        from werkzeug.exceptions import BadRequest
+        raise BadRequest('input stream exhausted')
+
+    def exhaust(self, chunk_size=1024 * 16):
+        """Exhaust the stream.  This consumes all the data left until the
+        limit is reached.
+
+        :param chunk_size: the size for a chunk.  It will read the chunk
+                           until the stream is exhausted and throw away
+                           the results.
+        """
+        to_read = self.limit - self._pos
+        chunk = chunk_size
+        while to_read > 0:
+            chunk = min(to_read, chunk)
+            self.read(chunk)
+            to_read -= chunk
+
+    def read(self, size=None):
+        """Read `size` bytes or if size is not provided everything is read.
+
+        :param size: the number of bytes read.
+        """
+        if self._pos >= self.limit:
+            return self.on_exhausted()
+        if size is None:
+            size = self.limit
+        read = self._read(min(self.limit - self._pos, size))
+        self._pos += len(read)
+        return read
+
+    def readline(self, size=None):
+        """Reads one line from the stream."""
+        if self._pos >= self.limit:
+            return self.on_exhausted()
+        if size is None:
+            size = self.limit - self._pos
+        else:
+            size = min(size, self.limit - self._pos)
+        line = self._readline(size)
+        self._pos += len(line)
+        return line
+
+    def readlines(self, size=None):
+        """Reads a file into a list of strings.  It calls :meth:`readline`
+        until the file is read to the end.  It does support the optional
+        `size` argument if the underlaying stream supports it for
+        `readline`.
+        """
+        last_pos = self._pos
+        result = []
+        if size is not None:
+            end = min(self.limit, last_pos + size)
+        else:
+            end = self.limit
+        while 1:
+            if size is not None:
+                size -= last_pos - self._pos
+            if self._pos >= end:
+                break
+            result.append(self.readline(size))
+            if size is not None:
+                last_pos = self._pos
+        return result
+
+    def next(self):
+        line = self.readline()
+        if line is None:
+            raise StopIteration()
+        return line
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/aal.css	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,99 @@
+/* 
+  aardvark.legs by Anatoli Papirovski - http://fecklessmind.com/
+  Licensed under the MIT license. http://www.opensource.org/licenses/mit-license.php
+*/
+
+/* 
+  Reset first. Modified version of Eric Meyer and Paul Chaplin reset 
+  from http://meyerweb.com/eric/tools/css/reset/ 
+*/
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+header, nav, section, article, aside, footer
+{border: 0; margin: 0; outline: 0; padding: 0; background: transparent; vertical-align: baseline;}
+
+blockquote, q {quotes: none;}
+blockquote:before,blockquote:after,q:before,q:after {content: ''; content: none;}
+
+header, nav, section, article, aside, footer {display: block;}
+
+/* Basic styles */
+body {background: #fff; color: #111; font: 0.75em/1.5em "Helvetica Neue", Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif;}
+html>body {font-size: 12px;}
+
+img {display: inline-block; vertical-align: bottom;}
+
+h1,h2,h3,h4,h5,h6,strong,b,dt,th {font-weight: 700;}
+address,cite,em,i,caption,dfn,var {font-style: italic;}
+
+h1 {margin: 0 0 0.75em; font-size: 2em;}
+h2 {margin: 0 0 1em; font-size: 1.5em;}
+h3 {margin: 0 0 1.286em; font-size: 1.167em;}
+h4 {margin: 0 0 1.5em; font-size: 1em;}
+h5 {margin: 0 0 1.8em; font-size: .834em;}
+h6 {margin: 0 0 2em; font-size: .75em;}
+
+p,ul,ol,dl,blockquote,pre {margin: 0 0 1.5em;}
+
+li ul,li ol {margin: 0;}
+ul {list-style: outside disc;}
+ol {list-style: outside decimal;}
+li {margin: 0 0 0 2em;}
+dd {padding-left: 1.5em;}
+blockquote {padding: 0 1.5em;}
+
+a {text-decoration: underline;}
+a:hover {text-decoration: none;}
+abbr,acronym {border-bottom: 1px dotted; cursor: help;}
+del {text-decoration: line-through;}
+ins {text-decoration: overline;}
+sub {font-size: .834em; line-height: 1em; vertical-align: sub;}
+sup {font-size: .834em; line-height: 1em; vertical-align: super;}
+
+tt,code,kbd,samp,pre {font-size: 1em; font-family: Consolas, Monaco, "Courier New", Courier, monospace;}
+
+/* Table styles */
+table {border-collapse: collapse; border-spacing: 0; margin: 0 0 1.5em;}
+caption {text-align: left;}
+th, td {padding: .25em .5em;}
+tbody td, tbody th {border: 1px solid #000;}
+tfoot {font-style: italic;}
+
+/* Form styles */
+fieldset {clear: both;}
+legend {padding: 0 0 1.286em; font-size: 1.167em; font-weight: 700;}
+fieldset fieldset legend {padding: 0 0 1.5em; font-size: 1em;}
+* html legend {margin-left: -7px;}
+*+html legend {margin-left: -7px;}
+
+form .field, form .buttons {clear: both; margin: 0 0 1.5em;}
+form .field label {display: block;}
+form ul.fields li {list-style-type: none; margin: 0;}
+form ul.inline li, form ul.inline label {display: inline;}
+form ul.inline li {padding: 0 .75em 0 0;}
+
+input.radio, input.checkbox {vertical-align: top;}
+label, button, input.submit, input.image {cursor: pointer;}
+* html input.radio, * html input.checkbox {vertical-align: middle;}
+*+html input.radio, *+html input.checkbox {vertical-align: middle;}
+
+textarea {overflow: auto;}
+input.text, input.password, textarea, select {margin: 0; font: 1em/1.3 Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif; vertical-align: baseline;}
+input.text, input.password, textarea {border: 1px solid #444; border-bottom-color: #666; border-right-color: #666; padding: 2px;}
+
+* html button {margin: 0 .34em 0 0;}
+*+html button {margin: 0 .34em 0 0;}
+
+form.horizontal .field {padding-left: 150px;}
+form.horizontal .field label {display: inline; float: left; width: 140px; margin-left: -150px;}
+
+/* Useful classes */
+img.left {display: inline; float: left; margin: 0 1.5em .75em 0;}
+img.right {display: inline; float: right; margin: 0 0 .75em .75em;}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/comments.js	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,53 @@
+$(function() {
+    
+    $("div > form").hide();
+    $("tr:has(form)").hide();
+    
+    
+    $("span.cancel a").click(function(event) {
+        $(event.target).closest("div.input").children(".activate").children("a").show();
+        $(event.target).parents("form").hide();
+        return false;
+    });
+    
+    $("span.cancel-line a").live("click", function(event) {
+        $(event.target).closest("tr").prev().removeClass("comment-line-selected").addClass("commentable");
+        $(event.target).closest("tr").remove();
+        return false;
+    });
+    
+    $(".input .activate a").click(function(event) {
+        $(event.target).hide();
+        $(event.target).closest("div").children("form").fadeIn("fast");
+        return false;
+    });
+    
+    $("tr.rem.commentable,tr.add.commentable,tr.con.commentable").live("click", function(event) {
+        $(event.target).closest("tr").addClass("comment-line-selected").removeClass("commentable");
+        var filename = $(event.target).closest("tr").find(".line-data").children(".filename").first().text();
+        var linenumber = $(event.target).closest("tr").find(".line-data").children(".linenumber").first().html();
+        
+        var comment_form = '<tr class="comment-line">\n\
+                <td>\n\
+                    <form id="comment-line-form" method="post" action="">\n\
+                        <div class="field">\n\
+                            <label for="body">Add a comment on this line:</label>\n\
+                            <textarea cols="60" rows="6" name="new-comment-body"></textarea>\n\
+                        </div>\n\
+                        <div class="buttons">\n\
+                            <input type="submit" class="button" value="Submit" />\n\
+                            <span class="cancel-line"><a href="">Cancel</a></span>\n\
+                        </div>\n\
+                        <input type="hidden" name="filename" value="<<<FILENAME>>>" />\n\
+                        <input type="hidden" name="lines" value="<<<LINENUMBER>>>" />\n\
+                    </form>\n\
+                </td>\n\
+            </tr>';
+        comment_form = comment_form.replace('<<<FILENAME>>>', filename);
+        comment_form = comment_form.replace('<<<LINENUMBER>>>', linenumber);
+        
+        $(event.target).closest("tr").after(comment_form);
+        return false;
+    });
+
+});
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/jquery-1.4.2.min.js	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/style.css	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,194 @@
+/* Basic layout and typography. */
+body {
+    background: #999;
+}
+div#content-wrap {
+    background-color: #fafafa;
+}
+div#main-wrap {
+    width: 75em;
+    margin: 0em auto;
+    padding: 3em;
+}
+div#head-wrap {
+    text-align: center;
+    padding: 1.5em 0em .5em;
+    color: #fff;
+    background-color: #111;
+    border-bottom: 6px solid #3C659A;
+}
+div#head-wrap h1 a, div#head-wrap h1 {
+    font-weight: normal;
+}
+div#remote-wrap {
+    text-align: center;
+    padding-top: 1.5em;
+    margin-bottom: -1.5em;
+}
+div#remote-wrap span.remote-section h3 {
+    display: inline;
+}
+div#remote-wrap span.remote-section {
+    margin: 0em 1em;
+}
+div#remote-wrap form {
+    display: inline;
+}
+div#footer {
+    border-top: 6px solid #666;
+    color: #fff;
+    text-align: center;
+    font-style: italic;
+    padding-top: 1.5em;
+}
+div#footer a {
+    color: #fff;
+}
+div#footer a:hover {
+    text-decoration: underline;
+}
+
+/* Links. */
+a {
+    text-decoration: none;
+    font-weight: bold;
+    color: #3C659A;
+}
+a:hover {
+    color: #EA0076;
+}
+div#head-wrap a {
+    color: inherit;
+}
+
+/* Tables. */
+table {
+    width: 100%;
+    border: 1px solid #666;
+    background: #f2f2f2;
+}
+table td {
+    border: none;
+}
+table tr.odd {
+    background: #eee;
+}
+table tr td.last {
+    text-align: right;
+}
+table tr form * {
+    margin: 0em;
+}
+
+/* Review pages. */
+div.filename-header {
+    background-color: #ccc;
+    border: 1px solid #c5c5c5;
+    padding: 1em;
+    margin-bottom: 1.5em;
+    margin-top: 1.5em;
+}
+div.filename-header h3 {
+    margin: 0em;
+}
+div.filename-header a.fold-file, div.filename-header a.unfold-file {
+    float: right;
+    font-weight: bold;
+    font-size: 1.5em;
+}
+
+/* Comments. */
+.comment {
+    white-space: normal;
+    background: #FBEAD0;
+    border: 1px dashed #666;
+    font-family: Consolas, Monaco, "Courier New", Courier, monospace;
+    padding: 0.75em;
+    margin-bottom: 1.5em;
+}
+.comment div.avatar {
+    border: 1px solid black;
+    float: right;
+}
+.comment div.message {
+    margin-top: 1.5em;
+    white-space: pre;
+}
+div#comment-review form {
+    margin-bottom: 3em;
+    display: none;
+}
+div#comment-file form {
+}
+table tbody tr.comment-line-selected {
+    background-color: #FBEAD0 !important;
+}
+span.cancel, form span.cancel-line {
+    margin-left: 10px;
+}
+
+/* Signoffs. */
+.signoff {
+    white-space: normal;
+    border: 1px dashed #666;
+    font-family: Consolas, Monaco, "Courier New", Courier, monospace;
+    padding: 0.75em;
+    margin-bottom: 1.5em;
+}
+.signoff.yes {
+    background-color: #B4FF9D;
+}
+.signoff.no {
+    background-color: #FC9696;
+}
+.signoff.neutral {
+    background-color: #F0F0F0;
+}
+.signoff div.avatar {
+    border: 1px solid black;
+    float: right;
+}
+.signoff div.message {
+    margin-top: 1.5em;
+    white-space: pre;
+}
+.signoff .opinion {
+    font-weight: bold;
+}
+
+/* Diffs. */
+div.diff {
+    overflow: auto;
+}
+div.diff table tr {
+    white-space: pre;
+}
+div.diff table tr.comment-line {
+    white-space: normal;
+}
+div.diff table tr.con {
+    font-size: 11px;
+}
+div.diff table tr.add {
+    background: #DBF3D1;
+    font-size: 11px;
+}
+div.diff table tr.rem {
+    background: #FBDBDA;
+    font-size: 11px;
+}
+div.diff table tr.skipped {
+    background: #ccc;
+    font-size: 11px;
+}
+div.diff table tr td.diff-line div.line-data {
+    display: none;
+}
+div.diff .comment {
+    margin-bottom: 0em;
+}
+
+.debug {
+    border: 2px solid green;
+    background-color: red;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/ui.js	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,14 @@
+$(function() {
+    
+    $("a.fold-file").toggle(function(event) {
+        $(event.target).closest("div.file-review").find("div.file-review-contents").slideUp("fast");
+        $(event.target).html("&nbsp;&nbsp;&nbsp;&larr;").addClass("unfold-file").removeClass("fold-file");
+        return false;
+    },
+    function(event) {
+        $(event.target).closest("div.file-review").find("div.file-review-contents").slideDown("fast");
+        $(event.target).html("&nbsp;&nbsp;&nbsp;&darr;").addClass("fold-file").removeClass("unfold-file");
+        return false;
+    });
+
+});
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/templates/base.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,58 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+{% import 'macros.html' as macros %}
+
+<html>
+    <head>
+        <title>Reviewing {{ utils["basename"](datastore.target.root) }}{% block title %}{% endblock %}</title>
+        <link rel="stylesheet" href="/static/aal.css" type="text/css" media="screen" />
+        <link rel="stylesheet" href="/static/style.css" type="text/css" media="screen" />
+        
+        <script type="text/javascript" src="/static/jquery-1.4.2.min.js"></script>
+        <script type="text/javascript" src="/static/ui.js"></script>
+        <script type="text/javascript" src="/static/comments.js"></script>
+    </head>
+    
+    <body>
+        <div id="head-wrap">
+            <h1>
+                Reviewing
+                <a href="/">
+                    {{ utils["basename"](datastore.target.root) }}
+                </a>
+                {% block header %}{% endblock %}
+            </h1>
+        </div>
+        <div id="content-wrap">
+            {% if not read_only %}
+                <div id="remote-wrap">
+                    <span id="remote-push" class="remote-section">
+                        <h3>Push comments to:</h3>
+                        {% for name, path in datastore.repo.ui.configitems("paths") %}
+                            <form action="/push/" method="post">
+                                <input type="hidden" name="path" value="{{ name }}" />
+                                <input type="submit" value="{{ name }}" />
+                            </form>
+                        {% endfor %}
+                    </span>
+                    <span id="remote-pull" class="remote-section">
+                        <h3>Pull comments from:</h3>
+                        {% for name, path in datastore.repo.ui.configitems("paths") %}
+                            <form action="/pull/" method="post">
+                                <input type="hidden" name="path" value="{{ name }}" />
+                                <input type="submit" value="{{ name }}" />
+                            </form>
+                        {% endfor %}
+                    </span>
+                </div>
+            {% endif %}
+            <div id="main-wrap">
+                {% block content %}{% endblock %}
+            </div>
+        </div>
+        <div id="footer">
+            <p>reviewing {{ utils["basename"](datastore.target.root) }} with <a href="http://bitbucket.org/sjl/hg-review/">hg-review</a></p>
+        </div>
+    </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/templates/changeset.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,176 @@
+{% extends "base.html" %}
+
+{% block title %} at {{ title }}{% endblock %}
+{% block header %} at {{ title }}{% endblock %}
+
+{% block content %}
+    <h2>Changeset {{ rev.rev() }}: {{ rev.description().splitlines()[0] }}</h2>
+    
+    {% for comment in rcset.review_level_comments() %}
+        <div class="comment">
+            {{ macros.gravatar(comment, utils) }}
+            <div>
+                <div class="author">
+                    <a href="mailto:${ email(comment.author) }">{{ utils['templatefilters'].person(comment.author) }}</a>
+                    said:
+                </div>
+                <div class="message">{{ comment.message }}</div>
+            </div>
+        </div>
+    {% endfor %}
+    
+    {% if not read_only %}
+        <div id="comment-review" class="input">
+            <p class="activate"><a href="">Add a comment on this changeset</a></p>
+            <form id="comment-review-form" method="post" action="">
+                <div class="field">
+                    <label for="body">Add a comment on this changeset:</label>
+                    <textarea cols="60" rows="6" name="new-comment-body"></textarea>
+                </div>
+                <div class="buttons">
+                    <input type="submit" class="button" value="Submit" />
+                    <span class="cancel"><a href="#">Cancel</a></span>
+                </div>
+            </form>
+        </div>
+    {% endif %}
+    
+    <h2>Signoffs</h2>
+    
+    {% for signoff in rcset.signoffs %}
+        <div class="signoff {{ signoff.opinion or 'neutral' }}">
+            {{ macros.gravatar(signoff, utils) }}
+            <div>
+                <div class="author">
+                    <a href="mailto:${ email(signoff.author) }">{{ utils['templatefilters'].person(signoff.author) }}</a>
+                    signed off as <span class="opinion">{{ signoff.opinion or "neutral" }}</span> on this changeset, saying:
+                </div>
+                <div class="message">{{ signoff.message }}</div>
+            </div>
+        </div>
+    {% endfor %}
+    
+    {% if not read_only %}
+        <div id="signoff-review" class="input">
+            <p class="activate">
+                {% if cu_signoff %}
+                    <a href="#">Change your signoff</a>
+                {% else %}
+                    <a href="#">Sign off on this changeset</a>
+                {% endif %}
+            </p>
+            <form id="signoff-review-form" method="post" action="">
+                <div class="field">
+                    <input type="radio" name="signoff" value="yes" {% if cu_signoff and cu_signoff.opinion == 'yes' %}checked{% endif %}/><span>Yes</span>
+                    <input type="radio" name="signoff" value="no" {% if cu_signoff and cu_signoff.opinion == 'no' %}checked{% endif %}/><span>No</span>
+                    <input type="radio" name="signoff" value="neutral" {% if cu_signoff and cu_signoff.opinion == '' %}checked{% endif %}/><span>Neutral</span>
+                </div>
+                <div class="field">
+                    <label for="body">Signoff message:</label>
+                    <textarea cols="60" rows="6" name="new-signoff-body">{% if cu_signoff %}{{ cu_signoff.message }}{% endif %}</textarea>
+                </div>
+                <div class="buttons">
+                    <input type="submit" class="button" value="Submit" />
+                    <span class="cancel"><a href="#">Cancel</a></span>
+                </div>
+            </form>
+        </div>
+    {% endif %}
+
+    <h2>Files</h2>
+    
+    {% for filename in rcset.files() %}
+        <div class="file-review">
+            <div class="filename-header">
+                <a class="fold-file" href="">&nbsp;&nbsp;&nbsp;&darr;</a>
+                <h3>{{ filename }}</h3>
+            </div>
+            
+            <div class="file-review-contents">
+                {% for comment in rcset.file_level_comments(filename) %}
+                    <div class="comment">
+                        {{ macros.gravatar(comment, utils) }}
+                        <div>
+                            <div class="author">
+                                <a href="mailto:{{ utils['email'](comment.author) }}">
+                                    {{ utils['templatefilters'].person(comment.author) }}</a>
+                                    said:
+                            </div>
+                            <div class="message">{{ comment.message }}</div>
+                        </div>
+                    </div>
+                {% endfor %}
+                
+                {% if not read_only %}
+                    <div id="comment-file" class="input">
+                        <p class="activate"><a href="">Add a comment on this file</a></p>
+                        
+                        <form id="comment-file-form" method="post" action="">
+                            <div class="field">
+                                <label for="body">Add a comment on this file:</label>
+                                <textarea cols="60" rows="6" name="new-comment-body"></textarea>
+                            </div>
+                            <div class="buttons">
+                                <input type="submit" class="button" value="Submit" />
+                                <span class="cancel"><a href="#">Cancel</a></span>
+                            </div>
+                            <input type="hidden" name="filename" value="{{ filename }}" />
+                        </form>
+                    </div>
+                {% endif %}
+                
+                <div class="diff">
+                    <table>
+                        <tbody>
+                            {% set annotated_diff = rcset.annotated_diff(filename) %}
+                            {# We need to ignore the first item from this generator, because
+                               we don't care about providing a line-number prefix (for now!). #}
+                            {% set ignore_this_variable = annotated_diff.next() %}
+                            
+                            {% for line in annotated_diff %}
+                                {% if line['skipped'] %}
+                                    <tr class="skipped">
+                                        <td><code>&hellip; skipped {{ line['skipped'] }} lines &hellip;</code></td>
+                                    </tr>
+                                    {% for comment in line['comments'] %}
+                                        <tr><td class="comment">
+                                            {{ macros.gravatar(comment, utils) }}
+                                            <div>
+                                                <div class="author">
+                                                    <a href="mailto:{{ utils['email'](comment.author) }}">
+                                                        {{ utils['templatefilters'].person(comment.author) }}
+                                                    </a>
+                                                    said (on a skipped line):
+                                                </div>
+                                                <div class="message">{{ comment.message|escape }}</div>
+                                            </div>
+                                        </td></tr>
+                                    {% endfor %}
+                                {% else %}
+                                    <tr class="{{ utils['line_type'](line['content']) }}{% if not read_only %} commentable {% endif %}">
+                                        <td class="diff-line"><div class="line-data"><span class="linenumber">{{ line['number'] }}</span><span class="filename">{{ filename }}</span></div><code>{{ line['number'] }}: {{ line['content'][1:]|escape }}</code></td>
+                                    </tr>
+                                        
+                                    {% for comment in line['comments'] %}
+                                        <tr><td class="comment">
+                                            {{ macros.gravatar(comment, utils) }}
+                                            <div>
+                                                <div class="author">
+                                                    <a href="mailto:{{ utils['email'](comment.author) }}">
+                                                        {{ utils['templatefilters'].person(comment.author) }}
+                                                    </a>
+                                                    said:
+                                                </div>
+                                                <div class="message">{{ comment.message }}</div>
+                                            </div>
+                                        </td></tr>
+                                    {% endfor %}
+                                {% endif %}
+                            {% endfor %}
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    {% endfor %}
+{% endblock %}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/templates/index.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block title %}{% endblock %}
+{% block header %}{% endblock %}
+
+{% block content %}
+    <h2>Changesets</h2>
+    <table>
+        {% for rcset in rcsets %}
+            {% set rev = rcset.target[rcset.node] %}
+            {% set node_short = utils['node_short'](rev.node()) %}
+            <tr class="${ loop.parity }">
+                <td>{{ rev.rev() }}:{{ node_short }}</td>
+                <td>
+                    <a href="/changeset/{{ node_short }}/">{{  rev.description().splitlines()[0] }}</a>
+                </td>
+                <td class="last">
+                    {{ utils['len'](rcset.comments) }} comments,
+                    {{ utils['len'](rcset.signoffs) }} signoffs
+                </td>
+            </tr>
+        {% endfor %}
+    </table>
+{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/review/templates/macros.html	Fri Jun 11 21:25:17 2010 -0400
@@ -0,0 +1,7 @@
+{% macro gravatar(item, utils) -%}
+    <div class="avatar">
+        <img height="52" width="52"
+             src="{{ utils['item_gravatar'](item) }}?s=52"
+        />
+    </div>
+{%- endmacro %}
\ No newline at end of file
--- a/review/web_media/aal.css	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/* 
-  aardvark.legs by Anatoli Papirovski - http://fecklessmind.com/
-  Licensed under the MIT license. http://www.opensource.org/licenses/mit-license.php
-*/
-
-/* 
-  Reset first. Modified version of Eric Meyer and Paul Chaplin reset 
-  from http://meyerweb.com/eric/tools/css/reset/ 
-*/
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, font, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-header, nav, section, article, aside, footer
-{border: 0; margin: 0; outline: 0; padding: 0; background: transparent; vertical-align: baseline;}
-
-blockquote, q {quotes: none;}
-blockquote:before,blockquote:after,q:before,q:after {content: ''; content: none;}
-
-header, nav, section, article, aside, footer {display: block;}
-
-/* Basic styles */
-body {background: #fff; color: #111; font: 0.75em/1.5em "Helvetica Neue", Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif;}
-html>body {font-size: 12px;}
-
-img {display: inline-block; vertical-align: bottom;}
-
-h1,h2,h3,h4,h5,h6,strong,b,dt,th {font-weight: 700;}
-address,cite,em,i,caption,dfn,var {font-style: italic;}
-
-h1 {margin: 0 0 0.75em; font-size: 2em;}
-h2 {margin: 0 0 1em; font-size: 1.5em;}
-h3 {margin: 0 0 1.286em; font-size: 1.167em;}
-h4 {margin: 0 0 1.5em; font-size: 1em;}
-h5 {margin: 0 0 1.8em; font-size: .834em;}
-h6 {margin: 0 0 2em; font-size: .75em;}
-
-p,ul,ol,dl,blockquote,pre {margin: 0 0 1.5em;}
-
-li ul,li ol {margin: 0;}
-ul {list-style: outside disc;}
-ol {list-style: outside decimal;}
-li {margin: 0 0 0 2em;}
-dd {padding-left: 1.5em;}
-blockquote {padding: 0 1.5em;}
-
-a {text-decoration: underline;}
-a:hover {text-decoration: none;}
-abbr,acronym {border-bottom: 1px dotted; cursor: help;}
-del {text-decoration: line-through;}
-ins {text-decoration: overline;}
-sub {font-size: .834em; line-height: 1em; vertical-align: sub;}
-sup {font-size: .834em; line-height: 1em; vertical-align: super;}
-
-tt,code,kbd,samp,pre {font-size: 1em; font-family: Consolas, Monaco, "Courier New", Courier, monospace;}
-
-/* Table styles */
-table {border-collapse: collapse; border-spacing: 0; margin: 0 0 1.5em;}
-caption {text-align: left;}
-th, td {padding: .25em .5em;}
-tbody td, tbody th {border: 1px solid #000;}
-tfoot {font-style: italic;}
-
-/* Form styles */
-fieldset {clear: both;}
-legend {padding: 0 0 1.286em; font-size: 1.167em; font-weight: 700;}
-fieldset fieldset legend {padding: 0 0 1.5em; font-size: 1em;}
-* html legend {margin-left: -7px;}
-*+html legend {margin-left: -7px;}
-
-form .field, form .buttons {clear: both; margin: 0 0 1.5em;}
-form .field label {display: block;}
-form ul.fields li {list-style-type: none; margin: 0;}
-form ul.inline li, form ul.inline label {display: inline;}
-form ul.inline li {padding: 0 .75em 0 0;}
-
-input.radio, input.checkbox {vertical-align: top;}
-label, button, input.submit, input.image {cursor: pointer;}
-* html input.radio, * html input.checkbox {vertical-align: middle;}
-*+html input.radio, *+html input.checkbox {vertical-align: middle;}
-
-textarea {overflow: auto;}
-input.text, input.password, textarea, select {margin: 0; font: 1em/1.3 Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif; vertical-align: baseline;}
-input.text, input.password, textarea {border: 1px solid #444; border-bottom-color: #666; border-right-color: #666; padding: 2px;}
-
-* html button {margin: 0 .34em 0 0;}
-*+html button {margin: 0 .34em 0 0;}
-
-form.horizontal .field {padding-left: 150px;}
-form.horizontal .field label {display: inline; float: left; width: 140px; margin-left: -150px;}
-
-/* Useful classes */
-img.left {display: inline; float: left; margin: 0 1.5em .75em 0;}
-img.right {display: inline; float: right; margin: 0 0 .75em .75em;}
\ No newline at end of file
--- a/review/web_media/comments.js	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-$(function() {
-    
-    $("div > form").hide();
-    $("tr:has(form)").hide();
-    
-    
-    $("span.cancel a").click(function(event) {
-        $(event.target).closest("div.input").children(".activate").children("a").show();
-        $(event.target).parents("form").hide();
-        return false;
-    });
-    
-    $("span.cancel-line a").live("click", function(event) {
-        $(event.target).closest("tr").prev().removeClass("comment-line-selected").addClass("commentable");
-        $(event.target).closest("tr").remove();
-        return false;
-    });
-    
-    $(".input .activate a").click(function(event) {
-        $(event.target).hide();
-        $(event.target).closest("div").children("form").fadeIn("fast");
-        return false;
-    });
-    
-    $("tr.rem.commentable,tr.add.commentable,tr.con.commentable").live("click", function(event) {
-        $(event.target).closest("tr").addClass("comment-line-selected").removeClass("commentable");
-        var filename = $(event.target).closest("tr").find(".line-data").children(".filename").first().text();
-        var linenumber = $(event.target).closest("tr").find(".line-data").children(".linenumber").first().html();
-        
-        var comment_form = '<tr class="comment-line">\n\
-                <td>\n\
-                    <form id="comment-line-form" method="post" action="">\n\
-                        <div class="field">\n\
-                            <label for="body">Add a comment on this line:</label>\n\
-                            <textarea cols="60" rows="6" name="new-comment-body"></textarea>\n\
-                        </div>\n\
-                        <div class="buttons">\n\
-                            <input type="submit" class="button" value="Submit" />\n\
-                            <span class="cancel-line"><a href="">Cancel</a></span>\n\
-                        </div>\n\
-                        <input type="hidden" name="filename" value="<<<FILENAME>>>" />\n\
-                        <input type="hidden" name="lines" value="<<<LINENUMBER>>>" />\n\
-                    </form>\n\
-                </td>\n\
-            </tr>';
-        comment_form = comment_form.replace('<<<FILENAME>>>', filename);
-        comment_form = comment_form.replace('<<<LINENUMBER>>>', linenumber);
-        
-        $(event.target).closest("tr").after(comment_form);
-        return false;
-    });
-
-});
\ No newline at end of file
--- a/review/web_media/jquery-1.4.2.min.js	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.4.2
- * http://jquery.com/
- *
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- * Copyright 2010, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- *
- * Date: Sat Feb 13 22:33:48 2010 -0500
- */
-(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
-e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
-j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
-"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
-true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
-Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
-(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
-a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
-"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
-function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
-c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
-L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
-"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
-a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
-d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
-a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
-!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
-true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
-var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
-parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
-false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
-s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
-applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
-else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
-a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
-w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
-cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
-i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
-" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
-this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
-e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
-c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
-a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
-function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
-k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
-C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
-null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
-e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
-f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
-if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
-d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
-"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
-a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
-isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
-{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
-if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
-e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
-"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
-d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
-!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
-toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
-u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
-function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
-if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
-e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
-t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
-g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
-for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
-1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
-CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
-relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
-l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
-h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
-CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
-g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
-text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
-setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
-h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
-m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
-"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
-h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
-!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
-h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
-q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
-if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
-(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
-function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
-gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
-c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
-{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
-"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
-d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
-a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
-1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
-a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
-c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
-wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
-prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
-this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
-return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
-""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
-this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
-u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
-1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
-return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
-""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
-c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
-c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
-function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
-Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
-"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
-a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
-a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
-"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
-serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
-function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
-global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
-e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
-"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
-false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
-false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
-c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
-d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
-g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
-1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
-"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
-if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
-this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
-"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
-animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
-j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
-this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
-"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
-c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
-this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
-this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
-e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
-c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
-function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
-this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
-k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
-f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
-a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
-c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
-d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
-"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
-e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
\ No newline at end of file
--- a/review/web_media/style.css	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-/* Basic layout and typography. */
-body {
-    background: #999;
-}
-div#content-wrap {
-    background-color: #fafafa;
-}
-div#main-wrap {
-    width: 75em;
-    margin: 0em auto;
-    padding: 3em;
-}
-div#head-wrap {
-    text-align: center;
-    padding: 1.5em 0em .5em;
-    color: #fff;
-    background-color: #111;
-    border-bottom: 6px solid #3C659A;
-}
-div#head-wrap h1 a, div#head-wrap h1 {
-    font-weight: normal;
-}
-div#remote-wrap {
-    text-align: center;
-    padding-top: 1.5em;
-    margin-bottom: -1.5em;
-}
-div#remote-wrap span.remote-section h3 {
-    display: inline;
-}
-div#remote-wrap span.remote-section {
-    margin: 0em 1em;
-}
-div#remote-wrap form {
-    display: inline;
-}
-div#footer {
-    border-top: 6px solid #666;
-    color: #fff;
-    text-align: center;
-    font-style: italic;
-    padding-top: 1.5em;
-}
-div#footer a {
-    color: #fff;
-}
-div#footer a:hover {
-    text-decoration: underline;
-}
-
-/* Links. */
-a {
-    text-decoration: none;
-    font-weight: bold;
-    color: #3C659A;
-}
-a:hover {
-    color: #EA0076;
-}
-div#head-wrap a {
-    color: inherit;
-}
-
-/* Tables. */
-table {
-    width: 100%;
-    border: 1px solid #666;
-    background: #f2f2f2;
-}
-table td {
-    border: none;
-}
-table tr.odd {
-    background: #eee;
-}
-table tr td.last {
-    text-align: right;
-}
-table tr form * {
-    margin: 0em;
-}
-
-/* Review pages. */
-div.filename-header {
-    background-color: #ccc;
-    border: 1px solid #c5c5c5;
-    padding: 1em;
-    margin-bottom: 1.5em;
-    margin-top: 1.5em;
-}
-div.filename-header h3 {
-    margin: 0em;
-}
-div.filename-header a.fold-file, div.filename-header a.unfold-file {
-    float: right;
-    font-weight: bold;
-    font-size: 1.5em;
-}
-
-/* Comments. */
-.comment {
-    white-space: normal;
-    background: #FBEAD0;
-    border: 1px dashed #666;
-    font-family: Consolas, Monaco, "Courier New", Courier, monospace;
-    padding: 0.75em;
-    margin-bottom: 1.5em;
-}
-.comment div.avatar {
-    border: 1px solid black;
-    float: right;
-}
-.comment div.message {
-    margin-top: 1.5em;
-    white-space: pre;
-}
-div#comment-review form {
-    margin-bottom: 3em;
-    display: none;
-}
-div#comment-file form {
-}
-table tbody tr.comment-line-selected {
-    background-color: #FBEAD0 !important;
-}
-span.cancel, form span.cancel-line {
-    margin-left: 10px;
-}
-
-/* Signoffs. */
-.signoff {
-    white-space: normal;
-    border: 1px dashed #666;
-    font-family: Consolas, Monaco, "Courier New", Courier, monospace;
-    padding: 0.75em;
-    margin-bottom: 1.5em;
-}
-.signoff.yes {
-    background-color: #B4FF9D;
-}
-.signoff.no {
-    background-color: #FC9696;
-}
-.signoff.neutral {
-    background-color: #F0F0F0;
-}
-.signoff div.avatar {
-    border: 1px solid black;
-    float: right;
-}
-.signoff div.message {
-    margin-top: 1.5em;
-    white-space: pre;
-}
-.signoff .opinion {
-    font-weight: bold;
-}
-
-/* Diffs. */
-div.diff {
-    overflow: auto;
-}
-div.diff table tr {
-    white-space: pre;
-}
-div.diff table tr.comment-line {
-    white-space: normal;
-}
-div.diff table tr.con {
-    font-size: 11px;
-}
-div.diff table tr.add {
-    background: #DBF3D1;
-    font-size: 11px;
-}
-div.diff table tr.rem {
-    background: #FBDBDA;
-    font-size: 11px;
-}
-div.diff table tr.skipped {
-    background: #ccc;
-    font-size: 11px;
-}
-div.diff table tr td.diff-line div.line-data {
-    display: none;
-}
-div.diff .comment {
-    margin-bottom: 0em;
-}
-
-.debug {
-    border: 2px solid green;
-    background-color: red;
-}
\ No newline at end of file
--- a/review/web_media/ui.js	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-$(function() {
-    
-    $("a.fold-file").toggle(function(event) {
-        $(event.target).closest("div.file-review").find("div.file-review-contents").slideUp("fast");
-        $(event.target).html("&nbsp;&nbsp;&nbsp;&larr;").addClass("unfold-file").removeClass("fold-file");
-        return false;
-    },
-    function(event) {
-        $(event.target).closest("div.file-review").find("div.file-review-contents").slideDown("fast");
-        $(event.target).html("&nbsp;&nbsp;&nbsp;&darr;").addClass("fold-file").removeClass("unfold-file");
-        return false;
-    });
-
-});
\ No newline at end of file
--- a/review/web_templates/base.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
-{% import 'macros.html' as macros %}
-
-<html>
-    <head>
-        <title>Reviewing {{ utils["basename"](datastore.target.root) }}{% block title %}{% endblock %}</title>
-        <link rel="stylesheet" href="/media/aal.css" type="text/css" media="screen" />
-        <link rel="stylesheet" href="/media/style.css" type="text/css" media="screen" />
-        
-        <script type="text/javascript" src="/media/jquery-1.4.2.min.js"></script>
-        <script type="text/javascript" src="/media/ui.js"></script>
-        <script type="text/javascript" src="/media/comments.js"></script>
-    </head>
-    
-    <body>
-        <div id="head-wrap">
-            <h1>
-                Reviewing
-                <a href="/">
-                    {{ utils["basename"](datastore.target.root) }}
-                </a>
-                {% block header %}{% endblock %}
-            </h1>
-        </div>
-        <div id="content-wrap">
-            {% if not read_only %}
-                <div id="remote-wrap">
-                    <span id="remote-push" class="remote-section">
-                        <h3>Push comments to:</h3>
-                        {% for name, path in datastore.repo.ui.configitems("paths") %}
-                            <form action="/push/" method="post">
-                                <input type="hidden" name="path" value="{{ name }}" />
-                                <input type="submit" value="{{ name }}" />
-                            </form>
-                        {% endfor %}
-                    </span>
-                    <span id="remote-pull" class="remote-section">
-                        <h3>Pull comments from:</h3>
-                        {% for name, path in datastore.repo.ui.configitems("paths") %}
-                            <form action="/pull/" method="post">
-                                <input type="hidden" name="path" value="{{ name }}" />
-                                <input type="submit" value="{{ name }}" />
-                            </form>
-                        {% endfor %}
-                    </span>
-                </div>
-            {% endif %}
-            <div id="main-wrap">
-                {% block content %}{% endblock %}
-            </div>
-        </div>
-        <div id="footer">
-            <p>reviewing {{ utils["basename"](datastore.target.root) }} with <a href="http://bitbucket.org/sjl/hg-review/">hg-review</a></p>
-        </div>
-    </body>
-</html>
\ No newline at end of file
--- a/review/web_templates/changeset.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %} at {{ title }}{% endblock %}
-{% block header %} at {{ title }}{% endblock %}
-
-{% block content %}
-    <h2>Changeset {{ rev.rev() }}: {{ rev.description().splitlines()[0] }}</h2>
-    
-    {% for comment in rcset.review_level_comments() %}
-        <div class="comment">
-            {{ macros.gravatar(comment, utils) }}
-            <div>
-                <div class="author">
-                    <a href="mailto:${ email(comment.author) }">{{ utils['templatefilters'].person(comment.author) }}</a>
-                    said:
-                </div>
-                <div class="message">{{ comment.message }}</div>
-            </div>
-        </div>
-    {% endfor %}
-    
-    {% if not read_only %}
-        <div id="comment-review" class="input">
-            <p class="activate"><a href="">Add a comment on this changeset</a></p>
-            <form id="comment-review-form" method="post" action="">
-                <div class="field">
-                    <label for="body">Add a comment on this changeset:</label>
-                    <textarea cols="60" rows="6" name="new-comment-body"></textarea>
-                </div>
-                <div class="buttons">
-                    <input type="submit" class="button" value="Submit" />
-                    <span class="cancel"><a href="#">Cancel</a></span>
-                </div>
-            </form>
-        </div>
-    {% endif %}
-    
-    <h2>Signoffs</h2>
-    
-    {% for signoff in rcset.signoffs %}
-        <div class="signoff {{ signoff.opinion or 'neutral' }}">
-            {{ macros.gravatar(signoff, utils) }}
-            <div>
-                <div class="author">
-                    <a href="mailto:${ email(signoff.author) }">{{ utils['templatefilters'].person(signoff.author) }}</a>
-                    signed off as <span class="opinion">{{ signoff.opinion or "neutral" }}</span> on this changeset, saying:
-                </div>
-                <div class="message">{{ signoff.message }}</div>
-            </div>
-        </div>
-    {% endfor %}
-    
-    {% if not read_only %}
-        <div id="signoff-review" class="input">
-            <p class="activate">
-                {% if cu_signoff %}
-                    <a href="#">Change your signoff</a>
-                {% else %}
-                    <a href="#">Sign off on this changeset</a>
-                {% endif %}
-            </p>
-            <form id="signoff-review-form" method="post" action="">
-                <div class="field">
-                    <input type="radio" name="signoff" value="yes" {% if cu_signoff and cu_signoff.opinion == 'yes' %}checked{% endif %}/><span>Yes</span>
-                    <input type="radio" name="signoff" value="no" {% if cu_signoff and cu_signoff.opinion == 'no' %}checked{% endif %}/><span>No</span>
-                    <input type="radio" name="signoff" value="neutral" {% if cu_signoff and cu_signoff.opinion == '' %}checked{% endif %}/><span>Neutral</span>
-                </div>
-                <div class="field">
-                    <label for="body">Signoff message:</label>
-                    <textarea cols="60" rows="6" name="new-signoff-body">{% if cu_signoff %}{{ cu_signoff.message }}{% endif %}</textarea>
-                </div>
-                <div class="buttons">
-                    <input type="submit" class="button" value="Submit" />
-                    <span class="cancel"><a href="#">Cancel</a></span>
-                </div>
-            </form>
-        </div>
-    {% endif %}
-
-    <h2>Files</h2>
-    
-    {% for filename in rcset.files() %}
-        <div class="file-review">
-            <div class="filename-header">
-                <a class="fold-file" href="">&nbsp;&nbsp;&nbsp;&darr;</a>
-                <h3>{{ filename }}</h3>
-            </div>
-            
-            <div class="file-review-contents">
-                {% for comment in rcset.file_level_comments(filename) %}
-                    <div class="comment">
-                        {{ macros.gravatar(comment, utils) }}
-                        <div>
-                            <div class="author">
-                                <a href="mailto:{{ utils['email'](comment.author) }}">
-                                    {{ utils['templatefilters'].person(comment.author) }}</a>
-                                    said:
-                            </div>
-                            <div class="message">{{ comment.message }}</div>
-                        </div>
-                    </div>
-                {% endfor %}
-                
-                {% if not read_only %}
-                    <div id="comment-file" class="input">
-                        <p class="activate"><a href="">Add a comment on this file</a></p>
-                        
-                        <form id="comment-file-form" method="post" action="">
-                            <div class="field">
-                                <label for="body">Add a comment on this file:</label>
-                                <textarea cols="60" rows="6" name="new-comment-body"></textarea>
-                            </div>
-                            <div class="buttons">
-                                <input type="submit" class="button" value="Submit" />
-                                <span class="cancel"><a href="#">Cancel</a></span>
-                            </div>
-                            <input type="hidden" name="filename" value="{{ filename }}" />
-                        </form>
-                    </div>
-                {% endif %}
-                
-                <div class="diff">
-                    <table>
-                        <tbody>
-                            {% set annotated_diff = rcset.annotated_diff(filename) %}
-                            {# We need to ignore the first item from this generator, because
-                               we don't care about providing a line-number prefix (for now!). #}
-                            {% set ignore_this_variable = annotated_diff.next() %}
-                            
-                            {% for line in annotated_diff %}
-                                {% if line['skipped'] %}
-                                    <tr class="skipped">
-                                        <td><code>&hellip; skipped {{ line['skipped'] }} lines &hellip;</code></td>
-                                    </tr>
-                                    {% for comment in line['comments'] %}
-                                        <tr><td class="comment">
-                                            {{ macros.gravatar(comment, utils) }}
-                                            <div>
-                                                <div class="author">
-                                                    <a href="mailto:{{ utils['email'](comment.author) }}">
-                                                        {{ utils['templatefilters'].person(comment.author) }}
-                                                    </a>
-                                                    said (on a skipped line):
-                                                </div>
-                                                <div class="message">{{ comment.message|escape }}</div>
-                                            </div>
-                                        </td></tr>
-                                    {% endfor %}
-                                {% else %}
-                                    <tr class="{{ utils['line_type'](line['content']) }}{% if not read_only %} commentable {% endif %}">
-                                        <td class="diff-line"><div class="line-data"><span class="linenumber">{{ line['number'] }}</span><span class="filename">{{ filename }}</span></div><code>{{ line['number'] }}: {{ line['content'][1:]|escape }}</code></td>
-                                    </tr>
-                                        
-                                    {% for comment in line['comments'] %}
-                                        <tr><td class="comment">
-                                            {{ macros.gravatar(comment, utils) }}
-                                            <div>
-                                                <div class="author">
-                                                    <a href="mailto:{{ utils['email'](comment.author) }}">
-                                                        {{ utils['templatefilters'].person(comment.author) }}
-                                                    </a>
-                                                    said:
-                                                </div>
-                                                <div class="message">{{ comment.message }}</div>
-                                            </div>
-                                        </td></tr>
-                                    {% endfor %}
-                                {% endif %}
-                            {% endfor %}
-                        </tbody>
-                    </table>
-                </div>
-            </div>
-        </div>
-    {% endfor %}
-{% endblock %}
\ No newline at end of file
--- a/review/web_templates/index.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}{% endblock %}
-{% block header %}{% endblock %}
-
-{% block content %}
-    <h2>Changesets</h2>
-    <table>
-        {% for rcset in rcsets %}
-            {% set rev = rcset.target[rcset.node] %}
-            {% set node_short = utils['node_short'](rev.node()) %}
-            <tr class="${ loop.parity }">
-                <td>{{ rev.rev() }}:{{ node_short }}</td>
-                <td>
-                    <a href="/changeset/{{ node_short }}/">{{  rev.description().splitlines()[0] }}</a>
-                </td>
-                <td class="last">
-                    {{ utils['len'](rcset.comments) }} comments,
-                    {{ utils['len'](rcset.signoffs) }} signoffs
-                </td>
-            </tr>
-        {% endfor %}
-    </table>
-{% endblock %}
--- a/review/web_templates/macros.html	Fri Jun 11 20:07:20 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-{% macro gravatar(item, utils) -%}
-    <div class="avatar">
-        <img height="52" width="52"
-             src="{{ utils['item_gravatar'](item) }}?s=52"
-        />
-    </div>
-{%- endmacro %}
\ No newline at end of file
--- a/review/web_ui.py	Fri Jun 11 20:07:20 2010 -0400
+++ b/review/web_ui.py	Fri Jun 11 21:25:17 2010 -0400
@@ -1,32 +1,37 @@
+from __future__ import with_statement
+
 """The review extension's web UI."""
-from __future__ import with_statement
 
 import sys, os
 from hashlib import md5
 
-from mercurial import commands, cmdutil, hg, templatefilters
-from mercurial.node import short, hex
+from mercurial import commands, templatefilters
+from mercurial.node import short
 from mercurial.util import email
 
 import api
 
-package_path = os.path.split(os.path.realpath(__file__))[0]
-template_path = os.path.join(package_path, 'web_templates')
-media_path = os.path.join(package_path, 'web_media')
-top_path = os.path.split(package_path)[0]
-bundled_path = os.path.join(top_path, 'bundled')
-cherrypy_path = os.path.join(bundled_path, 'cherrypy')
-jinja2_path = os.path.join(bundled_path, 'jinja2')
+def unbundle():
+    package_path = os.path.split(os.path.realpath(__file__))[0]
+    template_path = os.path.join(package_path, 'web_templates')
+    media_path = os.path.join(package_path, 'web_media')
+    top_path = os.path.split(package_path)[0]
+    bundled_path = os.path.join(top_path, 'bundled')
+    flask_path = os.path.join(bundled_path, 'flask')
+    jinja2_path = os.path.join(bundled_path, 'jinja2')
+    werkzeug_path = os.path.join(bundled_path, 'werkzeug')
+    simplejson_path = os.path.join(bundled_path, 'simplejson')
 
-sys.path.insert(0, cherrypy_path)
-sys.path.insert(0, jinja2_path)
+    sys.path.insert(0, flask_path)
+    sys.path.insert(0, werkzeug_path)
+    sys.path.insert(0, jinja2_path)
+    sys.path.insert(0, simplejson_path)
 
-import cherrypy
-from jinja2 import Environment, FileSystemLoader
+unbundle()
 
-
-TEMPLATE_DIR = os.path.join(package_path, 'web_templates')
-jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_DIR))
+from flask import Flask
+from flask import abort, redirect, render_template, request
+app = Flask(__name__)
 
 LOG_PAGE_LEN = 1000000
 
@@ -47,99 +52,84 @@
     'line_type': _line_type,
 }
 
-class ReviewWebUI(object):
-    def __init__(self, datastore, read_only):
-        self.datastore = datastore
-        self.read_only = read_only
-    
-    
-    @cherrypy.expose
-    def index(self):
-        rev_max = self.datastore.target['tip'].rev()
-        rev_min = rev_max - LOG_PAGE_LEN if rev_max >= LOG_PAGE_LEN else 0
-        rcsets = [self.datastore[r] for r in xrange(rev_max, rev_min, -1)]
-        
-        return jinja_env.get_template('index.html').render(
-            read_only=self.read_only,
-            utils=utils, datastore=self.datastore, title='',
-            rcsets=rcsets,
-        )
-    
+datastore = None
+site_read_only = False
+
+def _render(template, **kwargs):
+    return render_template(template, read_only=site_read_only, utils=utils,
+        datastore=datastore, **kwargs)
+
+
+@app.route('/')
+def index():
+    rev_max = datastore.target['tip'].rev()
+    rev_min = rev_max - LOG_PAGE_LEN if rev_max >= LOG_PAGE_LEN else 0
+    rcsets = [datastore[r] for r in xrange(rev_max, rev_min, -1)]
+    return _render('index.html', title='', rcsets=rcsets)
+
+
+def _handle_signoff(revhash):
+    signoff = request.form.get('signoff', None)
+
+    if signoff not in ['yes', 'no', 'neutral']:
+        abort(400)
+
+    if signoff == 'neutral':
+        signoff = ''
+
+    body = request.form.get('new-signoff-body', '')
+    rcset = datastore[revhash]
+    rcset.add_signoff(body, signoff, force=True)
+
+    return redirect("/changeset/%s/" % revhash)
+
+def _handle_comment(revhash):
+    filename = request.form.get('filename', '')
+    lines = str(request.form.get('lines', ''))
+    if lines:
+        lines = lines.split(',')
+    body = request.form['new-comment-body']
     
-    @cherrypy.expose
-    def changeset(self, *args, **kwargs):
-        if len(args) != 1:
-            return 'OH GOD HOW DID THIS GET HERE I AM NOT GOOD WITH LINKS'
-        rev_id = args[0]
-        
-        if kwargs and not self.read_only:
-            signoff = kwargs.get('signoff', None)
-            if signoff:
-                if signoff not in ['yes', 'no', 'neutral']:
-                    return 'Invalid signoff type.'
-                if signoff == 'neutral':
-                    signoff = ''
-                body = kwargs.get('new-signoff-body', '')
-                rcset = self.datastore[rev_id]
-                rcset.add_signoff(body, signoff, force=True)
-                raise cherrypy.HTTPRedirect("/changeset/%s/" % rev_id)
-            
-            filename = kwargs.get('filename', '')
-            lines = str(kwargs['lines']) if 'lines' in kwargs else ''
-            if lines:
-                lines = lines.split(',')
-            body = kwargs['new-comment-body']
-            
-            if body:
-                rcset = self.datastore[rev_id]
-                rcset.add_comment(body, filename, lines)
-            
-            raise cherrypy.HTTPRedirect("/changeset/%s/" % rev_id)
-        
-        rcset = self.datastore[rev_id]
-        rev = rcset.target[rev_id]
-        
-        cu_signoffs = rcset.signoffs_for_current_user()
-        cu_signoff = cu_signoffs[0] if cu_signoffs else None
-        print cu_signoff
-        
-        return jinja_env.get_template('changeset.html').render(
-            read_only=self.read_only,
-            utils=utils, datastore=self.datastore,
-            title='%s:%s' % (rev.rev(), short(rev.node())),
-            rcset=rcset, rev=rev, cu_signoff=cu_signoff
-        )
+    if body:
+        rcset = datastore[revhash]
+        rcset.add_comment(body, filename, lines)
+    
+    return redirect("/changeset/%s/" % revhash)
+
+@app.route('/changeset/<revhash>/', methods=['GET', 'POST'])
+def changeset(revhash):
+    if request.method == 'POST' and not site_read_only:
+        signoff = request.form.get('signoff', None)
+        if signoff:
+            return _handle_signoff(revhash)
+        else:
+            return _handle_comment(revhash)
+    
+    rcset = datastore[revhash]
+    rev = rcset.target[revhash]
     
+    cu_signoffs = rcset.signoffs_for_current_user()
+    cu_signoff = cu_signoffs[0] if cu_signoffs else None
     
-    @cherrypy.expose
-    def pull(self, **kwargs):
-        if not self.read_only:
-            if 'path' not in kwargs:
-                return 'OH GOD HOW DID THIS GET HERE I AM NOT GOOD WITH LINKS'
-            path = kwargs['path']
-            
-            commands.pull(
-                self.datastore.repo.ui, self.datastore.repo, path, **{
-                    'update': True
-                }
-            )
-        
-        raise cherrypy.HTTPRedirect("/")
-    
-    
-    @cherrypy.expose
-    def push(self, **kwargs):
-        if not self.read_only:
-            if 'path' not in kwargs:
-                return 'OH GOD HOW DID THIS GET HERE I AM NOT GOOD WITH LINKS'
-            path = kwargs['path']
-            
-            commands.push(
-                self.datastore.repo.ui,self.datastore.repo, path, **{}
-            )
-        
-        raise cherrypy.HTTPRedirect("/")
-    
+    return _render('changeset.html',
+        title='%s:%s' % (rev.rev(), short(rev.node())),
+        rcset=rcset, rev=rev, cu_signoff=cu_signoff
+    )
+
+
+@app.route('/pull/', methods=['POST'])
+def pull():
+    if not site_read_only:
+        path = request.form['path']
+        commands.pull(datastore.repo.ui, datastore.repo, path, update=True)
+    return redirect('/')
+
+@app.route('/push/', methods=['POST'])
+def push():
+    if not site_read_only:
+        path = request.form['path']
+        commands.push(datastore.repo.ui, datastore.repo, path)
+    return redirect('/')
 
 
 def load_interface(ui, repo, read_only=False, open=False, address='127.0.0.1', port=8080):
@@ -147,16 +137,9 @@
         import webbrowser
         webbrowser.open('http://localhost:%d/' % port)
         
-    conf = {
-        '/media': {
-            'tools.staticdir.on': True,
-            'tools.staticdir.dir': media_path,
-        },
-        'global': {
-            'server.socket_host': address,
-            'server.socket_port': port,
-        }
-    }
-    
-    cherrypy.quickstart(ReviewWebUI(api.ReviewDatastore(ui, repo), read_only=read_only), config=conf)
+    global datastore, site_read_only
+    datastore = api.ReviewDatastore(ui, repo)
+    site_read_only = read_only
 
+    app.debug = True
+    app.run()