Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 1390

Show
Ignore:
Timestamp:
10/04/06 02:20:27
Author:
fumanchu
Message:

New RoutesDispatcher?.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/_cprequest.py

    r1387 r1390  
    88 
    99import cherrypy 
     10from cherrypy._cpdispatch import * 
    1011from cherrypy import _cpcgifs 
    1112from cherrypy._cperror import format_exc, bare_error 
     
    9798        return newmap 
    9899    copy = __copy__ 
    99  
    100  
    101 class PageHandler(object): 
    102     """Callable which sets response.body.""" 
    103      
    104     def __init__(self, callable, *args, **kwargs): 
    105         self.callable = callable 
    106         self.args = args 
    107         self.kwargs = kwargs 
    108      
    109     def __call__(self): 
    110         return self.callable(*self.args, **self.kwargs) 
    111  
    112  
    113 class LateParamPageHandler(PageHandler): 
    114     """When passing cherrypy.request.params to the page handler, we do not 
    115     want to capture that dict too early; we want to give tools like the 
    116     decoding tool a chance to modify the params dict in-between the lookup 
    117     of the handler and the actual calling of the handler. This subclass 
    118     takes that into account, and allows request.params to be 'bound late' 
    119     (it's more complicated than that, but that's the effect). 
    120     """ 
    121      
    122     def _get_kwargs(self): 
    123         kwargs = cherrypy.request.params.copy() 
    124         if self._kwargs: 
    125             kwargs.update(self._kwargs) 
    126         return kwargs 
    127      
    128     def _set_kwargs(self, kwargs): 
    129         self._kwargs = kwargs 
    130      
    131     kwargs = property(_get_kwargs, _set_kwargs, 
    132                       doc='page handler kwargs (with ' 
    133                       'cherrypy.request.params copied in)') 
    134  
    135  
    136 class Dispatcher(object): 
    137      
    138     def __call__(self, path_info): 
    139         """Set handler and config for the current request.""" 
    140         request = cherrypy.request 
    141         func, vpath = self.find_handler(path_info) 
    142          
    143         if func: 
    144             # Decode any leftover %2F in the virtual_path atoms. 
    145             vpath = [x.replace("%2F", "/") for x in vpath] 
    146             request.handler = LateParamPageHandler(func, *vpath) 
    147         else: 
    148             request.handler = cherrypy.NotFound() 
    149      
    150     def find_handler(self, path): 
    151         """Return the appropriate page handler, plus any virtual path. 
    152          
    153         This will return two objects. The first will be a callable, 
    154         which can be used to generate page output. Any parameters from 
    155         the query string or request body will be sent to that callable 
    156         as keyword arguments. 
    157          
    158         The callable is found by traversing the application's tree, 
    159         starting from cherrypy.request.app.root, and matching path 
    160         components to successive objects in the tree. For example, the 
    161         URL "/path/to/handler" might return root.path.to.handler. 
    162          
    163         The second object returned will be a list of names which are 
    164         'virtual path' components: parts of the URL which are dynamic, 
    165         and were not used when looking up the handler. 
    166         These virtual path components are passed to the handler as 
    167         positional arguments. 
    168         """ 
    169         request = cherrypy.request 
    170         app = request.app 
    171         root = app.root 
    172          
    173         # Get config for the root object/path. 
    174         curpath = "" 
    175         nodeconf = {} 
    176         if hasattr(root, "_cp_config"): 
    177             nodeconf.update(root._cp_config) 
    178         if "/" in app.config: 
    179             nodeconf.update(app.config["/"]) 
    180         object_trail = [['root', root, nodeconf, curpath]] 
    181          
    182         node = root 
    183         names = [x for x in path.strip('/').split('/') if x] + ['index'] 
    184         for name in names: 
    185             # map to legal Python identifiers (replace '.' with '_') 
    186             objname = name.replace('.', '_') 
    187              
    188             nodeconf = {} 
    189             node = getattr(node, objname, None) 
    190             if node is not None: 
    191                 # Get _cp_config attached to this node. 
    192                 if hasattr(node, "_cp_config"): 
    193                     nodeconf.update(node._cp_config) 
    194              
    195             # Mix in values from app.config for this path. 
    196             curpath = "/".join((curpath, name)) 
    197             if curpath in app.config: 
    198                 nodeconf.update(app.config[curpath]) 
    199              
    200             object_trail.append([name, node, nodeconf, curpath]) 
    201          
    202         def set_conf(): 
    203             """Set cherrypy.request.config.""" 
    204             base = cherrypy.config.copy() 
    205             # Note that we merge the config from each node 
    206             # even if that node was None. 
    207             for name, obj, conf, curpath in object_trail: 
    208                 base.update(conf) 
    209                 if 'tools.staticdir.dir' in conf: 
    210                     base['tools.staticdir.section'] = curpath 
    211             return base 
    212          
    213         # Try successive objects (reverse order) 
    214         num_candidates = len(object_trail) - 1 
    215         for i in xrange(num_candidates, -1, -1): 
    216              
    217             name, candidate, nodeconf, curpath = object_trail[i] 
    218             if candidate is None: 
    219                 continue 
    220              
    221             # Try a "default" method on the current leaf. 
    222             if hasattr(candidate, "default"): 
    223                 defhandler = candidate.default 
    224                 if getattr(defhandler, 'exposed', False): 
    225                     # Insert any extra _cp_config from the default handler. 
    226                     conf = getattr(defhandler, "_cp_config", {}) 
    227                     object_trail.insert(i+1, ["default", defhandler, conf, curpath]) 
    228                     request.config = set_conf() 
    229                     request.is_index = False 
    230                     return defhandler, names[i:-1] 
    231              
    232             # Uncomment the next line to restrict positional params to "default". 
    233             # if i < num_candidates - 2: continue 
    234              
    235             # Try the current leaf. 
    236             if getattr(candidate, 'exposed', False): 
    237                 request.config = set_conf() 
    238                 if i == num_candidates: 
    239                     # We found the extra ".index". Mark request so tools 
    240                     # can redirect if path_info has no trailing slash. 
    241                     request.is_index = True 
    242                 else: 
    243                     # We're not at an 'index' handler. Mark request so tools 
    244                     # can redirect if path_info has NO trailing slash. 
    245                     # Note that this also includes handlers which take 
    246                     # positional parameters (virtual paths). 
    247                     request.is_index = False 
    248                 return candidate, names[i:-1] 
    249          
    250         # We didn't find anything 
    251         request.config = set_conf() 
    252         return None, [] 
    253  
    254  
    255 class MethodDispatcher(Dispatcher): 
    256     """Additional dispatch based on cherrypy.request.method.upper(). 
    257      
    258     Methods named GET, POST, etc will be called on an exposed class. 
    259     The method names must be all caps; the appropriate Allow header 
    260     will be output showing all capitalized method names as allowable 
    261     HTTP verbs. 
    262      
    263     Note that the containing class must be exposed, not the methods. 
    264     """ 
    265      
    266     def __call__(self, path_info): 
    267         """Set handler and config for the current request.""" 
    268         request = cherrypy.request 
    269         resource, vpath = self.find_handler(path_info) 
    270          
    271         if resource: 
    272             # Set Allow header 
    273             avail = [m for m in dir(resource) if m.isupper()] 
    274             if "GET" in avail and "HEAD" not in avail: 
    275                 avail.append("HEAD") 
    276             avail.sort() 
    277             cherrypy.response.headers['Allow'] = ", ".join(avail) 
    278              
    279             # Find the subhandler 
    280             meth = request.method.upper() 
    281             func = getattr(resource, meth, None) 
    282             if func is None and meth == "HEAD": 
    283                 func = getattr(resource, "GET", None) 
    284             if func: 
    285                 # Decode any leftover %2F in the virtual_path atoms. 
    286                 vpath = [x.replace("%2F", "/") for x in vpath] 
    287                 request.handler = LateParamPageHandler(func, *vpath) 
    288             else: 
    289                 request.handler = cherrypy.HTTPError(405) 
    290         else: 
    291             request.handler = cherrypy.NotFound() 
    292100 
    293101 

Hosted by WebFaction

Log in as guest/cpguest to create tickets