Commits

Robert Brewer  committed 30481ba

Root and config are now isolated per app:

1. object_path is now called path_info, and there's a new request.script_name attribute. This should equal the mount point of the current application.
2. cherrypy.root is gone, use cherrypy.request.app.root for now instead. Perhaps cherrypy.root will reappear and point to that.
3. cherrypy.tree.mount_points has been replaced with cherrypy.tree.apps, a dict of the form {script_name: Application(root, conf)}.
4. The [global] config namespace is now contained in a flat cherrypy.config.globalconf dict.
5. Got rid of handling favicon.ico and the "*" URI (although they may return someday).
6. Upshot is that e.g. test_objectmapping.py takes 1/3 the time as CP 2.2.
7. Moved request body size check into _cprequest from _cpwsgi.
8. Fixed lib/wsgiapp and made a tool for it.

  • Participants
  • Parent commits 8fbb411
  • Branches cherrypy

Comments (0)

Files changed (44)

 import _cptree
 tree = _cptree.Tree()
 
-root = None
-
 import _cpengine
 engine = _cpengine.Engine()
 import _cpserver

File _cpengine.py

         
         # Output config options to log
         if conf("log_config_options", True):
-            cherrypy.config.outputConfigMap()
-        
-        # Hmmm...we *could* check config in _start instead, but I think
-        # most people would like CP to fail before autoreload kicks in.
-        err = cherrypy.WrongConfigValue
-        for name, section in cherrypy.config.configs.iteritems():
-            for k, v in section.iteritems():
-                if k == "environment":
-                    if v and v not in cherrypy.config.environments:
-                        raise err("'%s' is not a registered environment." % v)
+            cherrypy.config.output_config_map()
         
         if cherrypy.codecoverage:
             from cherrypy.lib import covercp
             covercp.start()
         
-        # set cgi.maxlen which will limit the size of POST request bodies
-        cgi.maxlen = conf('server.max_request_size')
-        
         # Set up the profiler if requested.
         if conf("profiling.on", False):
             ppath = conf("profiling.path", "")

File _cprequest.py

         self.remote_addr = remote_addr
         self.remote_port = remote_port
         self.remote_host = remote_host
+        self.scheme = scheme
         
-        self.scheme = scheme
         self.closed = False
         
         pts = ['on_start_resource', 'before_request_body',
     def _run(self):
         try:
             # This has to be done very early in the request process,
-            # because request.object_path is used for config lookups
+            # because request.path_info is used for config lookups
             # right away.
             self.process_request_line()
             self.dispatch = self.config.get("dispatch") or _cputil.dispatch
                 try:
                     self.process_headers()
                     
+                    # Prepare the SizeCheckWrapper for the request body
+                    mbs = int(self.config.get('server.max_request_body_size',
+                                              100 * 1024 * 1024))
+                    if mbs > 0:
+                        self.rfile = httptools.SizeCheckWrapper(self.rfile, mbs)
+                    
                     self.hooks.run('before_request_body')
                     if self.process_request_body:
                         self.process_body()
                         try:
                             self.hooks.run('before_main')
                             if self.dispatch:
-                                self.dispatch(self.object_path)
+                                self.dispatch(self.path_info)
                             break
                         except cherrypy.InternalRedirect, ir:
-                            self.object_path = ir.path
+                            self.path_info = ir.path
                     
                     self.hooks.run('before_finalize')
                     cherrypy.response.finalize()
                 raise
             self.handle_error(sys.exc_info())
     
-    def _get_object_path(self):
-        return self._object_path
-    def _set_object_path(self, value):
-        self._object_path = value
+    def _get_path_info(self):
+        return self._path_info
+    def _set_path_info(self, value):
+        self._path_info = value
         self.config = cherrypy.config.request_config()
         
         # Get all 'tools.*' config entries as a {toolname: {k: v}} dict.
                 toolname = atoms.pop(0)
                 bucket = self.toolmap.setdefault(toolname, {})
                 bucket[".".join(atoms)] = v
-    object_path = property(_get_object_path, _set_object_path,
+    path_info = property(_get_path_info, _set_path_info,
                            doc="The path to the rendered resource.")
     
     def process_request_line(self):
         self.query_string = qs
         self.protocol = proto
         
-        # Change object_path to change the object that will get rendered
-        self.object_path = path
-        
         # Compare request and server HTTP versions, in case our server does
         # not support the requested version. We can't tell the server what
         # version number to write in the response, so we limit our output
         
         # cherrypy.response.version should be used to determine whether or
         # not to include a given HTTP/1.1 feature in the response content.
-        server_v = cherrypy.config.get("server.protocol_version", "HTTP/1.0")
+        server_v = cherrypy.config.get('server.protocol_version', 'HTTP/1.0')
         server_v = httptools.Version.from_http(server_v)
         cherrypy.response.version = min(self.version, server_v)
+        
+        # Change path_info to change the object that will get rendered.
+        # path_info should be the path from the app root to the handler.
+        self.script_name = r = cherrypy.tree.script_name(path)
+        self.app = cherrypy.tree.apps[r]
+        self.path_info = path[len(r.rstrip("/")):]
     
     def process_headers(self):
         self.params = httptools.parseQueryString(self.query_string)
 
-class Root:
-    pass
-
-class Branch:
-    pass
+class Application:
+    
+    def __init__(self, root, conf):
+        self.root = root
+        self.conf = conf
 
 
 class Tree:
-    """A scaffold for cherrypy.root.
-    
-    This class works together with cherrypy.root, providing helper methods
-    for mounting applications at diverse points. "Trellis" would be a more
-    accurate name (but too hard to remember, and perhaps in CP 3.0 this
-    class will become cherrypy.root).
-    """
+    """A registry of mounted applications at diverse points."""
     
     def __init__(self):
-        self._mount_points = {}
+        self.apps = {}
     
-    def _get_mount_points(self):
-        m = self._mount_points
-        if "/" not in m:
-            import cherrypy
-            if cherrypy.root is not None and not isinstance(cherrypy.root, Root):
-                m["/"] = cherrypy.root
-        return m
-    def _set_mount_points(self, newvalue):
-        self._mount_points = newvalue
-    mount_points = property(_get_mount_points, _set_mount_points)
-    
-    def mount(self, app_root, baseurl=None, conf=None):
-        """Mount the given app_root at the given baseurl (relative to root)."""
+    def mount(self, root, script_name=None, conf=None):
+        """Mount the given application root object at the given script_name."""
         import cherrypy
         
         if conf and not isinstance(conf, dict):
             conf = cherrypy.config.dict_from_config_file(conf)
+        elif conf is None:
+            conf = {}
         
-        if baseurl is None:
-            baseurl = "/"
+        if script_name is None:
+            script_name = "/"
             if conf:
-                conf_pt = conf.get("global", {}).get("mount_point")
+                conf_pt = conf.get("global", {}).get("script_name")
                 if conf_pt:
-                    baseurl = conf_pt
+                    script_name = conf_pt
         
-        point = baseurl.lstrip("/")
-        if point:
-            node = cherrypy.root
-            if node is None:
-                node = cherrypy.root = Root()
-            atoms = point.split("/")
-            tail = atoms.pop()
-            for atom in atoms:
-                if not hasattr(node, atom):
-                    setattr(node, atom, Branch())
-                node = getattr(node, atom)
-            if hasattr(node, tail):
-                raise ValueError("The url '%s' is already mounted." % baseurl)
-        else:
-            # Mount the app_root at cherrypy.root.
-            if cherrypy.root is not None:
-                raise ValueError("The url '%s' is already mounted." % baseurl)
-            node = cherrypy
-            tail = "root"
+        self.apps[script_name] = Application(root, conf)
+    
+    def script_name(self, path=None):
+        """The script_name of the app at the given path, or None.
         
-        setattr(node, tail, app_root)
-        self.mount_points[baseurl] = app_root
-        
-        if conf is not None:
-            cherrypy.config.update(updateMap=conf, baseurl=baseurl)
-    
-    def mount_point(self, path=None):
-        """The 'root path' of the app which governs the given path, or None.
-        
-        If path is None, cherrypy.request.object_path is used.
+        If path is None, cherrypy.request.path is used.
         """
         
         if path is None:
             try:
                 import cherrypy
-                path = cherrypy.request.object_path
+                path = cherrypy.request.path
             except AttributeError:
                 return None
         
         while path:
-            if path in self.mount_points:
+            if path in self.apps:
                 return path
             
             # Move one node up the tree and try again.
         
         return None
     
-    def url(self, path, mount_point=None):
-        """Return 'path', prefixed with mount_point.
+    def url(self, path, script_name=None):
+        """Return 'path', prefixed with script_name.
         
-        If mount_point is None, cherrypy.request.object_path will be used
-        to find a mount point.
+        If script_name is None, cherrypy.request.path will be used
+        to find a script_name.
         """
         
-        if mount_point is None:
-            mount_point = self.mount_point()
-            if mount_point is None:
+        if script_name is None:
+            script_name = self.script_name()
+            if script_name is None:
                 return path
         
         from cherrypy.lib import httptools
-        return httptools.urljoin(mount_point, path)
+        return httptools.urljoin(script_name, path)
 
 from cherrypy.lib import httptools
 
 
-def get_object_trail(objectpath=None, root=None):
-    """List of (name, object) pairs, from root (cherrypy.root) down objectpath.
+def get_object_trail(path=None, root=None):
+    """List of (name, object) pairs, from root (app.root) down path.
     
     If any named objects are unreachable, (name, None) pairs are used.
     """
     
-    if objectpath is None:
+    if path is None:
         try:
-            objectpath = cherrypy.request.object_path
+            path = cherrypy.request.path_info
         except AttributeError:
             pass
     
-    if objectpath is not None:
-        objectpath = objectpath.strip('/')
+    if path is not None:
+        path = path.strip('/')
     
-    # Convert the objectpath into a list of names
-    if not objectpath:
+    # Convert the path into a list of names
+    if not path:
         nameList = []
     else:
-        nameList = objectpath.split('/')
-    
-    if nameList == ['global']:
-        # Special-case a Request-URI of * to allow for our default handler.
-        root = getattr(cherrypy, 'root', None)
-        if root is None:
-            return [('root', None), ('global_', None), ('index', None)]
-        gh = getattr(root, 'global_', _cpGlobalHandler)
-        return [('root', cherrypy.root), ('global_', gh), ('index', None)]
+        nameList = path.split('/')
     
     if root is None:
-        root = getattr(cherrypy, 'root', None)
-        if root is None:
+        try:
+            root = cherrypy.request.app.root
+        except AttributeError:
             return [('root', None), ('index', None)]
     
     nameList.append('index')
     
     # Convert the list of names into a list of objects
     node = root
-    objectTrail = [('root', root)]
+    object_trail = [('root', root)]
     for name in nameList:
         # maps virtual names to Python identifiers (replaces '.' with '_')
         objname = name.replace('.', '_')
         node = getattr(node, objname, None)
         if node is None:
-            objectTrail.append((name, node))
+            object_trail.append((name, node))
         else:
-            objectTrail.append((objname, node))
+            object_trail.append((objname, node))
     
-    return objectTrail
+    return object_trail
 
 def dispatch(path):
     """Find and run the appropriate page handler."""
     vpath = [x.replace("%2F", "/") for x in vpath]
     cherrypy.response.body = handler(*vpath, **request.params)
 
-def find_handler(objectpath):
+def find_handler(path):
     """Find the appropriate page handler for the given path."""
-    objectTrail = get_object_trail(objectpath)
-    names = [name for name, candidate in objectTrail]
+    object_trail = get_object_trail(path)
+    names = [name for name, candidate in object_trail]
     
     # Try successive objects (reverse order)
-    mounted_app_roots = cherrypy.tree.mount_points.values()
-    for i in xrange(len(objectTrail) - 1, -1, -1):
+    for i in xrange(len(object_trail) - 1, -1, -1):
         
-        name, candidate = objectTrail[i]
+        name, candidate = object_trail[i]
         
         # Try a "default" method on the current leaf.
         defhandler = getattr(candidate, "default", None)
             return defhandler, names[:i+1] + ["default"], names[i+1:-1]
         
         # Uncomment the next line to restrict positional params to "default".
-        # if i < len(objectTrail) - 2: continue
+        # if i < len(object_trail) - 2: continue
         
         # Try the current leaf.
         if callable(candidate) and getattr(candidate, 'exposed', False):
-            if i == len(objectTrail) - 1:
+            if i == len(object_trail) - 1:
                 # We found the extra ".index". Check if the original path
                 # had a trailing slash (otherwise, do a redirect).
-                if not objectpath.endswith('/'):
+                if not path.endswith('/'):
                     atoms = cherrypy.request.browser_url.split("?", 1)
                     newUrl = atoms.pop(0) + '/'
                     if atoms:
                         newUrl += "?" + atoms[0]
                     raise cherrypy.HTTPRedirect(newUrl)
             return candidate, names[:i+1], names[i+1:-1]
-        
-        if candidate in mounted_app_roots:
-            break
     
     # We didn't find anything
-    raise cherrypy.NotFound(objectpath)
+    raise cherrypy.NotFound(path)
 
 
 def get_special_attribute(name):
     
     # First, we look in the right-most object to see if this special
     # attribute is implemented. If not, then we try the previous object,
-    # and so on until we reach cherrypy.root, or a mount point.
+    # and so on until we reach app.root (a mount point).
     # If it's still not there, we use the implementation from this module.
-    mounted_app_roots = cherrypy.tree.mount_points.values()
     objectList = get_object_trail()
     objectList.reverse()
     for objname, obj in objectList:
         if hasattr(obj, name):
             return getattr(obj, name)
-        if obj in mounted_app_roots:
-            break
     
     try:
         return globals()[name]
         msg = "Special attribute %s could not be found" % repr(name)
         raise cherrypy.HTTPError(500, msg)
 
-def _cpGlobalHandler():
-    """Default handler for a Request-URI of '*'."""
-    response = cherrypy.response
-    response.headers['Content-Type'] = 'text/plain'
-    
-    # OPTIONS is defined in HTTP 1.1 and greater
-    request = cherrypy.request
-    if request.method == 'OPTIONS' and request.version >= 1.1:
-        response.headers['Allow'] = 'HEAD, GET, POST, PUT, OPTIONS'
-    else:
-        response.headers['Allow'] = 'HEAD, GET, POST'
-    return ""
-_cpGlobalHandler.exposed = True
-
-
 def logtime():
     now = datetime.datetime.now()
     month = httptools.monthname[now.month][:3].capitalize()
                 if path == "*":
                     path = "global"
                 
-                # Prepare the SizeCheckWrapper for the request body
-                mbs = int(cherrypy.config.get('server.max_request_body_size',
-                                              100 * 1024 * 1024, path=path))
                 if isinstance(self.rfile, httptools.SizeCheckWrapper):
-                    if mbs > 0:
-                        self.rfile.bytes_read = 0
-                        self.rfile.maxlen = mbs
-                    else:
-                        # Unwrap the rfile
-                        self.rfile = self.rfile.rfile
-                else:
-                    if mbs > 0:
-                        self.rfile = httptools.SizeCheckWrapper(self.rfile, mbs)
+                    # Unwrap the rfile
+                    self.rfile = self.rfile.rfile
                 self.environ["wsgi.input"] = self.rfile
 
 
     def __init__(self):
         conf = cherrypy.config.get
         
-        sockFile = cherrypy.config.get('server.socket_file')
+        sockFile = conf('server.socket_file')
         if sockFile:
             bind_addr = sockFile
         else:
-            bind_addr = (conf("server.socket_host"), conf("server.socket_port"))
+            bind_addr = (conf('server.socket_host'), conf('server.socket_port'))
         
-        pts = cherrypy.tree.mount_points
-        if pts:
-            apps = [(base, wsgiApp) for base in pts.keys()]
-        else:
-            apps = [("", wsgiApp)]
+        apps = []
+        for base in cherrypy.tree.apps:
+            if base == "/":
+                base = ""
+            apps.append((base, wsgiApp))
         
         s = _cpwsgiserver.CherryPyWSGIServer
         s.__init__(self, bind_addr, apps,
-                   conf("server.thread_pool"),
-                   conf("server.socket_host"),
+                   conf('server.thread_pool'),
+                   conf('server.socket_host'),
                    request_queue_size = conf('server.socket_queue_size'),
                    )
 
 """Configuration system for CherryPy."""
 
 import ConfigParser
-import os
-_favicon_path = os.path.join(os.path.dirname(__file__), "favicon.ico")
 
 import cherrypy
 from cherrypy import _cputil
 from cherrypy.lib import autoreload, cptools, httptools
 
-
-# This configs dict holds the settings metadata for all cherrypy objects.
-# Keys are URL paths, and values are dicts.
-configs = {}
-
-default_global = {
-    'server.socket_port': 8080,
-    'server.socket_host': '',
-    'server.socket_file': '',
-    'server.socket_queue_size': 5,
-    'server.protocol_version': 'HTTP/1.0',
-    'log_to_screen': True,
-    'log_file': '',
-    'tools.log_tracebacks.on': True,
-    'server.reverse_dns': False,
-    'server.thread_pool': 10,
-    'environment': "development",
-    
-    '/favicon.ico': {'tools.staticfile.on': True,
-                     'tools.staticfile.filename': _favicon_path},
-    }
-
 environments = {
     "development": {
         'autoreload.on': True,
     "embedded": {
         'autoreload.on': False,
         'log_to_screen': False,
-        'server.init_only': True,
         'server.class': None,
         },
     }
 
-def update(updateMap=None, file=None, overwrite=True, baseurl=""):
-    """Update configs from a dictionary or a config file.
+def merge(base, other):
+    """Merge one config (from a dict, file, or filename) into another."""
+    if isinstance(other, basestring):
+        if other not in autoreload.reloadFiles:
+            autoreload.reloadFiles.append(other)
+        other = dict_from_config_file(other)
+    elif hasattr(other, 'read'):
+        other = dict_from_config_file(other)
     
-    If overwrite is False then the update will not modify values
-    already defined in the configs.
-    """
-    if updateMap is None:
-        updateMap = {}
+    # Load other into base
+    for section, value_map in other.iteritems():
+        base.setdefault(section, {}).update(value_map)
+
+
+default_conf = {
+    'server.socket_port': 8080,
+    'server.socket_host': '',
+    'server.socket_file': '',
+    'server.socket_queue_size': 5,
+    'server.protocol_version': 'HTTP/1.0',
+    'server.reverse_dns': False,
+    'server.thread_pool': 10,
+    'log_to_screen': True,
+    'log_file': '',
+    'tools.log_tracebacks.on': True,
+    'environment': "development",
+    }
+
+globalconf = default_conf.copy()
+
+def reset():
+    globalconf.clear()
+    globalconf.update(default_conf)
+
+def update(conf):
+    """Update globalconf from a dict, file or filename."""
+    if isinstance(conf, basestring):
+        if conf not in autoreload.reloadFiles:
+            autoreload.reloadFiles.append(conf)
+        conf = dict_from_config_file(conf)
+    elif hasattr(conf, 'read'):
+        conf = dict_from_config_file(conf)
+    if isinstance(conf.get("global", None), dict):
+        conf = conf["global"]
+    globalconf.update(conf)
+
+
+def get(key, default=None):
+    """Return the config value corresponding to key, or default."""
     
-    if file:
-        if file not in autoreload.reloadFiles:
-            autoreload.reloadFiles.append(file)
-        updateMap = updateMap.copy()
-        updateMap.update(dict_from_config_file(file))
+    try:
+        conf = cherrypy.request.config
+    except AttributeError:
+        # There's no request, so just use globalconf.
+        conf = globalconf
     
-    # Load new conf into cherrypy.configs
-    for section, valueMap in updateMap.iteritems():
-        # Handle shortcut syntax for "global" section
-        #   example: update({'server.socket_port': 80})
-        if not isinstance(valueMap, dict):
-            valueMap = {section: valueMap}
-            section = 'global'
-        
-        if baseurl and section.startswith("/"):
-            if section == "/":
-                section = baseurl
-            else:
-                section = httptools.urljoin(baseurl, section)
-        
-        bucket = configs.setdefault(section, {})
-        if overwrite:
-            bucket.update(valueMap)
-        else:
-            for key, value in valueMap.iteritems():
-                bucket.setdefault(key, value)
-
-def reset(useDefaults=True):
-    """Clear configuration and restore defaults"""
-    configs.clear()
-    if useDefaults:
-        update(default_global)
-reset()
-
-def get(key, default_value=None, return_section=False, path=None):
-    """Return the configuration value corresponding to key
-    If specified, return default_value on lookup failure. If return_section is
-    specified, return the path to the value, instead of the value itself.
-    """
-    
-    if path is None:
+    try:
+        return conf[key]
+    except KeyError:
         try:
-            path = cherrypy.request.object_path
-        except AttributeError:
-            # There's no request.object_path yet, so use the global settings.
-            path = "global"
-    
-    while True:
-        if path == "":
-            path = "/"
-        
-        try:
-            result = configs[path][key]
-            break
+            env = conf["environment"]
+            return environments[env][key]
         except KeyError:
-            pass
-        
-        try:
-            env = configs[path]["environment"]
-            result = environments[env][key]
-            break
-        except KeyError:
-            pass
-        pass
-
-        if path == "global":
-            result = default_value
-            break
-        
-        # Move one node up the tree and try again.
-        if path == "/":
-            path = "global"
-        elif path in cherrypy.tree.mount_points:
-            # We've reached the mount point for an application,
-            # and should skip the rest of the tree (up to "global").
-            path = "global"
-        else:
-            path = path[:path.rfind("/")]
-    
-    if return_section:
-        return path
-    else:
-        return result
+            return default
 
 def request_config():
     """Return all configs in effect for the current request in a single dict."""
-    path = cherrypy.request.object_path
-    mounted_app_roots = cherrypy.tree.mount_points.values()
+    path = cherrypy.request.path_info
+    app = cherrypy.request.app
     
     # Convert the path into a list of names
-    if (not path) or path == "*":
+    if (not path) or path == "/":
         nameList = []
     else:
         nameList = path.strip('/').split('/')
     nameList.append('index')
     
     curpath = ""
-    node = cherrypy.root
+    node = app.root
     conf = getattr(node, "_cp_config", {}).copy()
-    conf.update(configs.get("/", {}))
+    conf.update(app.conf.get("/", {}))
     for name in nameList:
-        # Get _cp_config attached to each node on the cherrypy tree.
+        # Get _cp_config attached to each node on this app's tree.
         objname = name.replace('.', '_')
         node = getattr(node, objname, None)
-        if node is not None:
-            if node in mounted_app_roots:
-                # Dump and start over. This inefficiency should disappear
-                # once we make cherrypy.localroot (specific to each request).
-                conf = {}
-            conf.update(getattr(node, "_cp_config", {}))
+        nodeconf = getattr(node, "_cp_config", {})
         
-        # Get values from cherrypy.config for this path.
+        # Get values from app.config for this path.
         curpath = "/".join((curpath, name))
-        conf.update(configs.get(curpath, {}))
+        nodeconf.update(app.conf.get(curpath, {}))
+        
+        # Resolve "environment" entries. This must be done node-by-node
+        # so that a child's "environment" can override concrete settings
+        # of a parent. However, concrete settings in this node will
+        # override "environment" settings in the same node.
+        env = nodeconf.get("environment")
+        if env:
+            for k, v in environments[env].iteritems():
+                if k not in nodeconf:
+                    nodeconf[k] = v
+        
+        conf.update(nodeconf)
     
-    base = configs.get("global", {}).copy()
+    base = globalconf.copy()
     base.update(conf)
     return base
 
 
 def request_config_section(key):
     """Return the (longest) path where the given key is defined (or None)."""
-    path = cherrypy.request.object_path
-    mounted_app_roots = cherrypy.tree.mount_points.values()
+    path = cherrypy.request.path_info
+    app = cherrypy.request.app
     
     # Convert the path into a list of names
-    if (not path) or path == "*":
+    if (not path):
         nameList = []
     else:
         nameList = path.strip('/').split('/')
     foundpath = None
     
     curpath = ""
-    node = cherrypy.root
-    if key in getattr(node, "_cp_config", {}) or key in configs.get("/", {}):
+    node = app.root
+    if key in getattr(node, "_cp_config", {}) or key in app.conf.get("/", {}):
         foundpath = "/"
     for name in nameList:
         curpath = "/".join((curpath, name))
         
-        # Get _cp_config attached to each node on the cherrypy tree.
+        # Get _cp_config attached to each node on this app's tree.
         objname = name.replace('.', '_')
         node = getattr(node, objname, None)
         if node is not None:
-            if node in mounted_app_roots:
-                # Dump and start over. This inefficiency should disappear
-                # once we make cherrypy.localroot (specific to each request).
-                foundpath = None
             if key in getattr(node, "_cp_config", {}):
-                foundpath = curpath or "/"
+                foundpath = curpath
+                break
         
         # Get values from cherrypy.config for this path.
-        if key in configs.get(curpath, {}):
+        if key in app.conf.get(curpath, {}):
             foundpath = curpath
     
     if foundpath is None:
-        foundpath = configs.get("global", {}).get(key)
+        foundpath = globalconf.get(key)
     return foundpath
 
 
             finally:
                 fp.close()
 
-def dict_from_config_file(configFile, raw=False, vars=None):
+def dict_from_config_file(config_file, raw=False, vars=None):
     """Convert an INI file to a dictionary"""
     
     # Parse config file
     configParser = CaseSensitiveConfigParser()
-    if hasattr(configFile, 'read'):
-        configParser.readfp(configFile)
+    if hasattr(config_file, 'read'):
+        configParser.readfp(config_file)
     else:
-        configParser.read(configFile)
+        configParser.read(config_file)
     
     # Load INI file into a dict
     result = {}
     return result
 
 
-def outputConfigMap():
-    """Log server configuration parameters"""
+def output_config_map():
+    """Log engine configuration parameters."""
     cherrypy.log("Server parameters:", 'CONFIG')
     
     serverVars = [
                   'server.socket_queue_size',
                   'server.thread_pool',
                  ]
-
+    
     for var in serverVars:
         cherrypy.log("  %s: %s" % (var, get(var)), 'CONFIG')
 

File lib/cptools.py

     
     from cherrypy.lib.cptools import ExposeItems
     ...
-    cherrypy.root.foo = ExposeItems(mylist)
-    cherrypy.root.bar = ExposeItems(mydict)
+    root.foo = ExposeItems(mylist)
+    root.bar = ExposeItems(mydict)
     """
     exposed = True
     def __init__(self, items):
     return False
 
 def virtual_host(use_x_forwarded_host=True, **domains):
-    """Change the object_path based on the Host.
+    """Change the path_info based on the Host.
     
     Useful when running multiple sites within one CP server.
     
     
     prefix = domains.get(domain, "")
     if prefix:
-        cherrypy.request.object_path = prefix + "/" + cherrypy.request.object_path
+        cherrypy.request.path_info = prefix + "/" + cherrypy.request.path_info
 
 def log_traceback():
     """Write the last error's traceback to the cherrypy error log."""

File lib/static.py

         return False
 
 def staticdir(section, dir, root="", match="", content_types=None, index=""):
-    if match and not re.search(match, cherrypy.request.object_path):
+    if match and not re.search(match, cherrypy.request.path_info):
         return False
     
     # If dir is relative, make absolute using "root".
     if section == 'global':
         section = "/"
     section = section.rstrip(r"\/")
-    branch = cherrypy.request.object_path[len(section) + 1:]
+    branch = cherrypy.request.path_info[len(section) + 1:]
     branch = urllib.unquote(branch.lstrip(r"\/"))
     
     # If branch is "", filename will end in a slash
     return handled
 
 def staticfile(filename, root=None, match="", content_types=None):
-    if match and not re.search(match, cherrypy.request.object_path):
+    if match and not re.search(match, cherrypy.request.path_info):
         return False
     
     # If filename is relative, make absolute using "root".

File lib/wsgiapp.py

 import sys
 
 import cherrypy
-from cherrypy import _cphttptools
-from cherrypy._cputil import get_object_trail
 
 
 # is this sufficient for start_response?
     headers_dict = dict(response_headers)
     cherrypy.response.headers.update(headers_dict)
 
-def get_path_components(path):
-    """returns (script_name, path_info)
-
-    determines what part of the path belongs to cp (script_name)
-    and what part belongs to the wsgi application (path_info)
-    """
-    no_parts = ['']
-    object_trail = get_object_trail(path)
-    root = object_trail.pop(0)
-    if not path.endswith('/index'):
-        object_trail.pop()
-    script_name_parts = [""]
-    path_info_parts = [""]
-    for (pc,obj) in object_trail:
-        if obj:
-            script_name_parts.append(pc)
-        else:
-            path_info_parts.append(pc)
-    script_name = "/".join(script_name_parts)
-    path_info = "/".join(path_info_parts)
-    if len(script_name) > 1 and path.endswith('/'):
-        path_info = path_info + '/'
-    
-    if script_name and not script_name.startswith('/'):
-        script_name = '/' + script_name
-    if path_info and not path_info.startswith('/'):
-        path_info = '/' + path_info
-    
-    return script_name, path_info
-
 def make_environ():
     """grabbed some of below from _cpwsgiserver.py
     
     for hosting WSGI apps in non-WSGI environments (yikes!)
     """
-
-    script_name, path_info = get_path_components(cherrypy.request.path)
     
     # create and populate the wsgi environment
     environ = dict()
     environ["wsgi.multiprocess"] = False
     environ["wsgi.run_once"] = False
     environ["REQUEST_METHOD"] = cherrypy.request.method
-    environ["SCRIPT_NAME"] = script_name
-    environ["PATH_INFO"] = path_info
-    environ["QUERY_STRING"] = cherrypy.request.queryString
+    environ["SCRIPT_NAME"] = cherrypy.request.script_name
+    environ["PATH_INFO"] = cherrypy.request.path_info
+    environ["QUERY_STRING"] = cherrypy.request.query_string
     environ["SERVER_PROTOCOL"] = cherrypy.request.version
     server_name = getattr(cherrypy.server.httpserver, 'server_name', "None")
     environ["SERVER_NAME"] = server_name 
     return environ
 
 
-def WSGIAppRequestFactory(wsgi_app, env_update=None):
-    """
-    wsgi_app - any wsgi application callable
-    env_update - a dictionary with arbitrary keys and values to be 
-                 merged with the WSGI environment dictionary.
-    """
+def run(app, env=None):
+    """Run the (WSGI) app and set response.body to its output"""
+    try:
+        environ = cherrypy.request.wsgi_environ
+    except AttributeError:
+        environ = make_environ()
+    environ['SCRIPT_NAME'] = cherrypy.request.script_name
+    environ['PATH_INFO'] = cherrypy.request.path_info
     
-    class WSGIAppRequest(_cphttptools.Request):
-        """A custom Request object for running a WSGI app within CP."""
-       
-        def process_body(self):
-            pass
-        
-        def main(self, *args, **kwargs):
-            """run the wsgi_app and set response.body to its output"""
-            try:
-                env = self.wsgi_environ
-                env['SCRIPT_NAME'], env['PATH_INFO'] = get_path_components(self.path)
-            except AttributeError:
-                env = make_environ()
-            env.update(env_update or {})
-            cherrypy.response.body = wsgi_app(env, start_response)
-    return WSGIAppRequest
+    # update the environ with the dict passed to the filter's
+    # constructor
+    if env:
+        environ.update(env)
+    
+    # run the wsgi app and have it set response.body
+    cherrypy.response.body = app(environ, start_response)
+    
+    return True
+

File test/benchmark.py

 
 AB_PATH = ""
 APACHE_PATH = ""
-MOUNT_POINT = "/cpbench/users/rdelon/apps/blog"
+SCRIPT_NAME = "/cpbench/users/rdelon/apps/blog"
 
 __all__ = ['ABSession', 'Root', 'print_report', 'read_process',
            'run_standard_benchmarks', 'safe_threads',
         'tools.staticdir.root': curdir,
         },
     }
-cherrypy.tree.mount(Root(), MOUNT_POINT, conf)
+cherrypy.tree.mount(Root(), SCRIPT_NAME, conf)
 
 
 class NullRequest:
                        r'^Transfer rate:\s*([0-9.]+)'),
                       ]
     
-    def __init__(self, path=MOUNT_POINT + "/", requests=1000, concurrency=10):
+    def __init__(self, path=SCRIPT_NAME + "/", requests=1000, concurrency=10):
         self.path = path
         self.requests = requests
         self.concurrency = concurrency
     safe_threads = (10, 20, 30, 40, 50)
 
 
-def thread_report(path=MOUNT_POINT + "/", concurrency=safe_threads):
+def thread_report(path=SCRIPT_NAME + "/", concurrency=safe_threads):
     sess = ABSession(path)
     attrs, names, patterns = zip(*sess.parse_patterns)
     rows = [('threads',) + names]
     attrs, names, patterns = zip(*sess.parse_patterns)
     rows = [('bytes',) + names]
     for sz in sizes:
-        sess.path = "%s/sizer?size=%s" % (MOUNT_POINT, sz)
+        sess.path = "%s/sizer?size=%s" % (SCRIPT_NAME, sz)
         sess.run()
         rows.append([sz] + [getattr(sess, attr) for attr in attrs])
     return rows
     print
     print ("Client Thread Report (1000 requests, 14 bytes via staticdir, "
            "%s server threads):" % cherrypy.config.get('server.thread_pool'))
-    print_report(thread_report("%s/static/index.html" % MOUNT_POINT))
+    print_report(thread_report("%s/static/index.html" % SCRIPT_NAME))
     
     print
     print ("Size Report (1000 requests, 50 client threads, "

File test/helper.py

 
 class CPWebCase(webtest.WebCase):
     
-    mount_point = ""
+    script_name = ""
     
     def prefix(self):
-        return self.mount_point.rstrip("/")
+        return self.script_name.rstrip("/")
     
     def exit(self):
         sys.exit()
     
     def getPage(self, url, headers=None, method="GET", body=None, protocol="HTTP/1.1"):
         """Open the url. Return status, headers, body."""
-        if self.mount_point:
-            url = httptools.urljoin(self.mount_point, url)
-        
+        if self.script_name:
+            url = httptools.urljoin(self.script_name, url)
         webtest.WebCase.getPage(self, url, headers, method, body, protocol)
     
     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.
-        """
+        """Compare the response body with a built in error page.
         
-        # This will never contain a traceback:
+        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.
         esc = re.escape
         epage = esc(page)
         epage = epage.replace(esc('<pre id="traceback"></pre>'),
-                              esc('<pre id="traceback">')
-                              + '(.*)' + esc('</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\n' + page)
 CPTestRunner = webtest.TerseTestRunner(verbosity=2)
 
 def setConfig(conf):
-    """Set the config using a copy of conf."""
+    """Set the global config using a copy of conf."""
     if isinstance(conf, basestring):
         # assume it's a filename
-        cherrypy.config.update(file=conf)
+        cherrypy.config.update(conf)
     else:
         cherrypy.config.update(conf.copy())
 
     The server is started and stopped once, regardless of the number
     of test modules. The config, however, is reset for each module.
     """
+    cherrypy.config.reset()
     setConfig(conf)
     cherrypy.server.start(server)
     cherrypy.engine.start_with_callback(_run_test_suite_thread,
                                         args=(moduleNames, conf))
 
 def _run_test_suite_thread(moduleNames, conf):
+    from cherrypy import _cpwsgi
     for testmod in moduleNames:
         # Must run each module in a separate suite,
         # because each module uses/overwrites cherrypy globals.
-        cherrypy.root = None
         cherrypy.tree = cherrypy._cptree.Tree()
         cherrypy.config.reset()
         setConfig(conf)
         setup = getattr(m, "setup_server", None)
         if setup:
             setup()
+        # The setup functions probably mounted new apps.
+        # Tell our server about them.
+        apps = []
+        for base in cherrypy.tree.apps:
+            if base == "/":
+                base = ""
+            apps.append((base, _cpwsgi.wsgiApp))
+        apps.sort()
+        apps.reverse()
+        cherrypy.server.httpserver.mount_points = apps
+        
         suite = CPTestLoader.loadTestsFromName(testmod)
         CPTestRunner.run(suite)
     thread.interrupt_main()
 
-def testmain(conf=None, *args, **kwargs):
+def testmain(conf=None):
     """Run __main__ as a test module, with webtest debugging."""
     if conf is None:
         conf = {}
     setConfig(conf)
     try:
         cherrypy.server.start()
-        cherrypy.engine.start_with_callback(_test_main_thread, *args, **kwargs)
+        cherrypy.engine.start_with_callback(_test_main_thread)
     except KeyboardInterrupt:
         cherrypy.server.stop()
         cherrypy.engine.stop()

File test/standalone_test_alt_script_name.py

     index.exposed = True
     
     def myMethod(self):
-        return "myMethod from dir1, object Path is:" + repr(cherrypy.request.object_path)
+        return "myMethod from dir1, path_info is:" + repr(cherrypy.request.path_info)
     myMethod.exposed = True
     
     def default(self, *params):
         return "index for dir2, path is:" + cherrypy.request.path
     index.exposed = True
     
-    def mount_point(self):
-        return cherrypy.tree.mount_point()
-    mount_point.exposed = True
+    def script_name(self):
+        return cherrypy.tree.script_name()
+    script_name.exposed = True
     
     def tree_url(self):
         return cherrypy.tree.url("/extra")
 Root.dir1.dir2.dir3 = Dir3()
 Root.dir1.dir2.dir3.dir4 = Dir4()
 
-mount_points = ["/apps", "/apps/users/fred/blog", "/apps/corp/blog"]
-for url in mount_points:
+script_names = ["/apps", "/apps/users/fred/blog", "/apps/corp/blog"]
+for url in script_names:
     conf = {'user': url.split("/")[-2]}
     cherrypy.tree.mount(Root(), url, {'/': conf})
 
         self.getPage('/missing')
         self.assertStatus('404 Not Found')
 
-        self.mount_point = mp
+        self.script_name = mp
         self.getPage('/script_name')
         self.assertBody(mp)
 
     def testObjectMapping(self):
-        for url in mount_points:
-            prefix = self.mount_point = url
+        for url in script_names:
+            prefix = self.script_name = url
             if prefix == "/":
                 prefix = ""
             
             self.assertBody('world')
             
             self.getPage("/dir1/myMethod")
-            self.assertBody("myMethod from dir1, object Path is:'%s/dir1/myMethod'"
+            self.assertBody("myMethod from dir1, path_info is:'%s/dir1/myMethod'"
                             % prefix)
             
             self.getPage("/this/method/does/not/exist")
             self.getPage("/page%2Fname")
             self.assertBody("default:('page/name',)")
             
-            self.getPage("/dir1/dir2/mount_point")
+            self.getPage("/dir1/dir2/script_name")
             self.assertBody(url)
             self.getPage("/dir1/dir2/tree_url")
             self.assertBody(prefix + "/extra")
             self.getPage("/confvalue")
             self.assertBody(url.split("/")[-2])
         
-        self.mount_point = mp
+        self.script_name = mp
         
         # Test that the "isolated" app doesn't leak url's into the root app.
         # If it did leak, Root.default() would answer with
         self.assertStatus("404 Not Found")
     
     def testPositionalParams(self):
-        self.mount_point = mp
+        self.script_name = mp
 
         self.getPage("/dir1/dir2/posparam/18/24/hut/hike")
         self.assertBody("18/24/hut/hike")
         self.assertBody("default for dir1, param is:('dir2', '5', '3', 'sir')")
     
     def testExpose(self):
-        self.mount_point = mp
+        self.script_name = mp
         
         # Test the cherrypy.expose function/decorator
         self.getPage("/exposing/base")

File test/test_cache_filter.py

 
 def setup_server():
     class Root:
+        
+        _cp_config = {'tools.caching.on': True}
+        
         def __init__(self):
             cherrypy.counter = 0
         
             msg = "visit #%s" % cherrypy.counter
             return msg
         index.exposed = True
-
-    cherrypy.root = Root()
+    
+    cherrypy.tree.mount(Root())
     cherrypy.config.update({
-            'log_to_screen': False,
-            'environment': 'production',
-            'tools.caching.on': True,
+        'log_to_screen': False,
+        'environment': 'production',
     })
 
 

File test/test_combinedfilters.py

             yield europoundUnicode
         index.exposed = True
 
-    cherrypy.root = Root()
+    cherrypy.tree.mount(Root())
     cherrypy.config.update({
             'log_to_screen': False,
             'environment': 'production',

File test/test_config.py

 def setup_server():
     
     class Root:
+        
+        _cp_config = {'foo': 'this',
+                      'bar': 'that'}
+        
         def index(self, key):
             return cherrypy.config.get(key, "None")
         index.exposed = True
         xyz = index
     
     class Foo:
+        
+        _cp_config = {'foo': 'this2',
+                      'baz': 'that2'}
+        
         def index(self, key):
             return cherrypy.config.get(key, "None")
         index.exposed = True
-        bar = index
         nex = index
+        
+        def bar(self, key):
+            return cherrypy.config.get(key, "None")
+        bar.exposed = True
+        bar._cp_config = {'foo': 'this3', 'bax': 'this4'}
     
     class Env:
+        
         def index(self, key):
             return str(cherrypy.config.get(key, "None"))
         index.exposed = True
         prod = index
         embed = index
-        
-        def wrong(self):
-            conf = "\n[global]\nenvironment = production\n"
-            cherrypy.config.update(file=StringIO.StringIO(conf))
-        wrong.exposed=True
     
-    cherrypy.tree.mount(Root())
-    cherrypy.root.foo = Foo()
+    root = Root()
+    root.foo = Foo()
+    cherrypy.tree.mount(root)
     
-    cherrypy.config.update({
-        'global': {'log_to_screen': False,
-                   'environment': 'production',
-                   'show_tracebacks': True,
-                   },
-        '/': {
-            'foo': 'this',
-            'bar': 'that',
-            },
-        '/foo': {
-            'foo': 'this2',
-            'baz': 'that2',
-            },
-        '/foo/bar': {
-            'foo': 'this3',
-            'bax': 'this4',
-            },
-    })
-
+    cherrypy.config.update({'log_to_screen': False,
+                            'environment': 'production',
+                            'show_tracebacks': True,
+                            })
+    
     _env_conf = {'/': {'environment': 'development'},
                  '/prod': {'environment': 'production'},
                  '/embed': {'environment': 'embedded'},
                  }
     cherrypy.tree.mount(Env(), "/env", _env_conf)
-
+    
     # Shortcut syntax--should get put in the "global" bucket
     cherrypy.config.update({'luxuryyacht': 'throatwobblermangrove'})
 
     
     def testConfig(self):
         tests = [
-            ('*',        'luxuryyacht', 'throatwobblermangrove'),
             ('/',        'nex', 'None'),
             ('/',        'foo', 'this'),
             ('/',        'bar', 'that'),
             self.getPage(path + "?key=" + key)
             self.assertBody(expected)
     
-    def testUnrepr(self):
-        err = ('WrongConfigValue: ("section: '
-               "'global', option: 'environment', value: 'production'"
-               '''", 'UnknownType', ('production',))''')
-        self.getPage("/env/wrong")
-        self.assertErrorPage(500, pattern=err)
-    
     def testEnvironments(self):
         for key, val in cherrypy.config.environments['development'].iteritems():
             self.getPage("/env/?key=" + key)

File test/test_core.py

 localDir = os.path.dirname(__file__)
 log_file = os.path.join(localDir, "error.log")
 log_access_file = os.path.join(localDir, "access.log")
+favicon_path = os.path.join(os.getcwd(), localDir, "../favicon.ico")
 
 defined_http_methods = ("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE",
                         "TRACE", "CONNECT")
             return "hello"
         index.exposed = True
         
+        favicon_ico = tools.staticfile.handler(filename=favicon_path)
+        
         def andnow(self):
             return "the larch"
         andnow.exposed = True
             return "Size: %s" % len(file.file.read())
         upload.exposed = True
     
-    cherrypy.root = Root()
+    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 cherrypy.root.
+        and adds an instance of the subclass as an attribute of root.
         """
         def __init__(cls, name, bases, dct):
             type.__init__(name, bases, dct)
             for value in dct.itervalues():
                 if isinstance(value, types.FunctionType):
                     value.exposed = True
-            setattr(cherrypy.root, name.lower(), cls())
+            setattr(root, name.lower(), cls())
     class Test(object):
         __metaclass__ = TestType
 
 
     class Params(Test):
         
+        _cp_config = {'log_file': log_file}
+        
         def index(self, thing):
             return repr(thing)
         
 
     class Flatten(Test):
         
+        _cp_config = {'log_file': log_file, 'log_access_file': log_access_file,}
+        
         def as_string(self):
             return "content"
         
 
     class Error(Test):
         
+        _cp_config = {'log_file': log_file, 'tools.log_tracebacks.on': True}
+        
         def custom(self):
             raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
+        custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html")}
         
         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()
             yield "howdy"
             raise ValueError()
         
+        # We support Python 2.3, but the @-deco syntax would look like this:
+        # @cherrypy.set_config(stream_response=True)
         def page_streamed(self):
             yield "word up"
             raise ValueError()
             yield "very oops"
+        page_streamed = cherrypy.set_config(stream_response=True)(page_streamed)
+        assert(page_streamed._cp_config == {'stream_response': 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 = {'show_tracebacks': False}
         
         def rethrow(self):
             """Test that an error raised here will be thrown out to the server."""
             raise ValueError()
-
-
+        rethrow._cp_config = {'throw_errors': True}
+    
+    
     class Ranges(Test):
         
         def get_ranges(self):
                     (ID, self.documents.get(ID, "empty")))
         get.exposed = True
 
-    cherrypy.root.divorce = Divorce()
+    root.divorce = Divorce()
 
 
     class Cookies(Test):
             return u'Wrong login/password'
     
     cherrypy.config.update({
-        'global': {
-            'log_to_screen': False,
-            'server.protocol_version': "HTTP/1.1",
-            'environment': 'production',
-            'show_tracebacks': True,
-            'server.max_request_body_size': 200,
-            'server.max_request_header_size': 500,
-        },
-        '/flatten': {
-            'log_file': log_file,
-            'log_access_file': log_access_file,
-        },
-        '/params': {
-            'log_file': log_file,
-        },
-        '/error': {
-            'log_file': log_file,
-            'tools.log_tracebacks.on': True,
-        },
-        '/error/page_streamed': {
-            'stream_response': True,
-        },
-        '/error/cause_err_in_finalize': {
-            'show_tracebacks': False,
-        },
-        '/error/custom': {
-            'error_page.404': os.path.join(localDir, "static/index.html"),
-        },
-        '/error/noexist': {
-            'error_page.404': "nonexistent.html",
-        },
-        '/error/rethrow': {
-            'throw_errors': True,
-        },
-    })
+        'log_to_screen': False,
+        'server.protocol_version': "HTTP/1.1",
+        'environment': 'production',
+        'show_tracebacks': True,
+        'server.max_request_body_size': 200,
+        'server.max_request_header_size': 500,
+        })
+    cherrypy.tree.mount(root)
 
 
 #                             Client-side code                             #
         self.getPage("/method/", method="SEARCH")
         self.assertStatus(501)
         
-        # Request the OPTIONS method with a Request-URI of "*".
-        self.getPage("*", method="OPTIONS")
-        self.assertStatus(200)
-        # Content-Length header required for OPTIONS with no response body.
-        # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2
-        self.assertHeader("Content-Length", "0")
-        
-        # Now be really dastardly and delete our custom global_ handler,
-        # to see if the default one works.
-        self.getPage("/delglobal")
-        self.getPage("*", method="OPTIONS")
-        self.assertStatus(200)
-        self.assertHeader("Allow", 'HEAD, GET, POST, PUT, OPTIONS')
-        
         # 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
         self.assertStatus(200)
     
     def testFavicon(self):
-        # favicon.ico is served by staticfile by default (see config.py)
+        # favicon.ico is served by staticfile.
         icofilename = os.path.join(localDir, "../favicon.ico")
         icofile = open(icofilename, "rb")
         data = icofile.read()

File test/test_custom_filters.py

         def index(self):
             return "Howdy earth!"
         index.exposed = True
-    cherrypy.root = Root()
+    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 cherrypy.root.
+        and adds an instance of the subclass as an attribute of root.
         """
         def __init__(cls, name, bases, dct):
             type.__init__(name, bases, dct)
             for value in dct.itervalues():
                 if isinstance(value, types.FunctionType):
                     value.exposed = True
-            setattr(cherrypy.root, name.lower(), cls())
+            setattr(root, name.lower(), cls())
     class Test(object):
         __metaclass__ = TestType
     
             return "success!"
     
     
-    cherrypy.config.update({
-        'global': {
-            'log_to_screen': False,
-            'environment': 'production',
-            'show_tracebacks': True,
-        },
+    cherrypy.config.update({'log_to_screen': False,
+                            'environment': 'production',
+                            'show_tracebacks': True,
+                            })
+    
+    conf = {
         # METHOD THREE:
         # Do it all in config
         '/demo': {
             # Because this isn't a dict, on_start_resource will error.
             'tools.numerify.map': "pie->3.14159"
         },
-    })
+    }
+    cherrypy.tree.mount(root, conf=conf)
 
 
 #                             Client-side code                             #

File test/test_decodingencoding_filter.py

             return sing
         mao_zedong.exposed = True
 
-    cherrypy.root = Root()
+    cherrypy.tree.mount(Root())
     cherrypy.config.update({
             'log_to_screen': False,
             'environment': 'production',

File test/test_gzip_filter.py

             raise IndexError()
             yield "Here be dragons"
         noshow_stream.exposed = True
-
-    cherrypy.root = Root()
+        noshow_stream._cp_config = {'stream_response': True}
+    
+    cherrypy.tree.mount(Root())
     cherrypy.config.update({
         'global': {'log_to_screen': False,
                    'environment': 'production',
                    'show_tracebacks': True,
                    'tools.gzip.on': True,
                    },
-        '/noshow_stream': {'stream_response': True},
     })
 
 

File test/test_noserver.py

     index.exposed = True
     wsgi_asp = index
 
-cherrypy.root = HelloWorld()
-cherrypy.root.test = HelloWorld()
-
+root = HelloWorld()
+root.test = HelloWorld()
+cherrypy.tree.mount(root)
 cherrypy.config.update({"environment": "production"})
 cherrypy.engine.start()
 

File test/test_objectmapping.py

 import cherrypy
 
 
-mount_points = ["/", "/users/fred/blog", "/corp/blog"]
+script_names = ["/", "/users/fred/blog", "/corp/blog"]
 
 def setup_server():
     class Root:
         def confvalue(self):
             return cherrypy.config.get("user")
         confvalue.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, object Path is:" + repr(cherrypy.request.object_path)
+            return "myMethod from dir1, path_info is:" + repr(cherrypy.request.path_info)
         myMethod.exposed = True
         
         def default(self, *params):
             return "index for dir2, path is:" + cherrypy.request.path
         index.exposed = True
         
-        def mount_point(self):
-            return cherrypy.tree.mount_point()
-        mount_point.exposed = True
+        def script_name(self):
+            return cherrypy.tree.script_name()
+        script_name.exposed = True
         
         def tree_url(self):
             return cherrypy.tree.url("/extra")
         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):
             return "defnoindex:" + repr(args)
         default.exposed = True
-
-
+    
+    
     Root.exposing = Exposing()
     Root.exposingnew = ExposingNewStyle()
     Root.dir1 = Dir1()
     Root.dir1.dir2.dir3 = Dir3()
     Root.dir1.dir2.dir3.dir4 = Dir4()
     Root.defnoindex = DefNoIndex()
-
-
-    for url in mount_points:
+    
+    
+    for url in script_names:
         conf = {'user': url.split("/")[-2]}
         cherrypy.tree.mount(Root(), url, {'/': conf})
-
+    
     cherrypy.config.update({
         'log_to_screen': False,
         'environment': "production",
     })
-
-
+    
+    
     class Isolated:
         def index(self):
             return "made it!"
         index.exposed = True
-
+    
     cherrypy.tree.mount(Isolated(), "/isolated")
 
 
 class ObjectMappingTest(helper.CPWebCase):
     
     def testObjectMapping(self):
-        for url in mount_points:
-            prefix = self.mount_point = url
+        for url in script_names:
+            prefix = self.script_name = url
             if prefix == "/":
                 prefix = ""
             
             self.assertBody('world')
             
             self.getPage("/dir1/myMethod")
-            self.assertBody("myMethod from dir1, object Path is:'%s/dir1/myMethod'"
-                            % prefix)
+            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.assertBody("default:('notExposed',)")
             
             self.getPage("/dir1/dir2/")
-            self.assertBody('index for dir2, path is:%s/dir1/dir2/'
-                            % prefix)
+            self.assertBody('index for dir2, path is:%s/dir1/dir2/' % prefix)
             
             self.getPage("/dir1/dir2")
             self.assert_(self.status in ('302 Found', '303 See Other'))
             self.getPage("/page%2Fname")
             self.assertBody("default:('page/name',)")
             
-            self.getPage("/dir1/dir2/mount_point")
+            self.getPage("/dir1/dir2/script_name")
             self.assertBody(url)
             self.getPage("/dir1/dir2/tree_url")
             self.assertBody(prefix + "/extra")
             self.getPage("/confvalue")
             self.assertBody(url.split("/")[-2])
         
-        self.mount_point = ""
+        self.script_name = ""
         
         # Test that the "isolated" app doesn't leak url's into the root app.
         # If it did leak, Root.default() would answer with

File test/test_response_headers_filter.py

         def other(self):
             return "salut"
         other.exposed = True
-    
-    cherrypy.root = Root()
-    cherrypy.config.update({
-        '/other': {
+        other._cp_config = {
             'tools.response_headers.on': True,
             'tools.response_headers.force': False,
             'tools.response_headers.headers': [("Content-Language", "fr"),
                                                ('Content-Type', 'text/plain')],
-            },
-        })
+            }
+    
+    cherrypy.tree.mount(Root())
 
 
 import helper

File test/test_session_concurrency.py

 
 # Server code
 class Root:
+    
+    _cp_config = {'session_filter.on': True,
+                  'session_filter.storage_type': storage_type,
+                  'session_filter.storage_path': storage_path,
+                  }
+    
     def index(self):
         # If you remove the "acquire_lock" call the assert at the end
         #   of this script will fail
     'environment': 'production',
     'log_to_screen': False,
     'server.thread_pool': server_thread_count,
-    'session_filter.on': True,
-    'session_filter.storage_type': storage_type,
-    'session_filter.storage_path': storage_path,
 })
-cherrypy.root = Root()
+cherrypy.tree.mount(Root())
 
 # Client code
 def run_client(cookie, request_count, data_dict, index):

File test/test_session_filter.py

 
 def setup_server():
     class Root:
+        
+        _cp_config = {'tools.sessions.on': True,
+                      'tools.sessions.storage_type' : 'file',
+                      'tools.sessions.storage_path' : '.',
+                      }
+        
         def testGen(self):
             counter = cherrypy.session.get('counter', 0) + 1
             cherrypy.session['counter'] = counter
             cherrypy.config.update({'tools.sessions.storage_type': newtype})
         setsessiontype.exposed = True
         
-    cherrypy.root = Root()
+    cherrypy.tree.mount(Root())
     cherrypy.config.update({
             'log_to_screen': False,
             'environment': 'production',
-            'tools.sessions.on': True,
-            'tools.sessions.storage_type' : 'file',
-            'tools.sessions.storage_path' : '.',
     })
 
 import helper

File test/test_sessionauthenticate_filter.py

 import cherrypy
 
 def setup_server():
-    class Test:
-        def index(self):
-            return "Hi, you are logged in"
-        index.exposed = True
-    
-    cherrypy.root = Test()
     
     def check(login, password):
         # Dummy check_login_and_password function
         if login != 'login' or password != 'password':
             return u'Wrong login/password'
     
+    class Test:
+        
+        _cp_config = {'tools.sessions.on': True,
+                      'tools.session_auth.on': True,
+                      'tools.session_auth.check_login_and_password': check,
+                      }
+        
+        def index(self):
+            return "Hi, you are logged in"
+        index.exposed = True
+    
+    cherrypy.tree.mount(Test())
     cherrypy.config.update({
             'log_to_screen': False,
             'environment': 'production',
-            'tools.sessions.on': True,
-            '/': {
-                'tools.session_auth.on': True,
-                'tools.session_auth.check_login_and_password': check,
-                },
             })
 
 

File test/test_states.py

         return "app was restarted succesfully"
     restart.exposed = True
 
-cherrypy.root = Root()
+cherrypy.tree.mount(Root())
 cherrypy.config.update({
-    'global': {
-        'log_to_screen': False,
-        'environment': 'production',
-    },
-})
+    'log_to_screen': False,
+    'environment': 'production',
+    })
 
 class Dependency: